Skip to content

Commit

Permalink
No rewrite-static-analysis dependency. Adopt latest Apache license OR
Browse files Browse the repository at this point in the history
  • Loading branch information
BoykoAlex committed Jan 22, 2025
1 parent f66a5f3 commit 1c64204
Show file tree
Hide file tree
Showing 18 changed files with 4,269 additions and 14 deletions.
23 changes: 11 additions & 12 deletions headless-services/commons/commons-rewrite/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,6 @@
<version>${project.version}</version>
</dependency>

<!-- ObjectDiff object becomes unresolved all of a sudden for JDT compiler as of rewrite 7.32.1. The dependency should come from rewrite -->
<!-- Try to remove in the future as it only seems to be required by Eclipse JDT. Maven build is happy without it -->
<dependency>
<groupId>de.danielbechler</groupId>
<artifactId>java-object-diff</artifactId>
<version>0.95</version>
</dependency>

<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
Expand All @@ -58,10 +50,6 @@
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-groovy</artifactId>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-kotlin</artifactId>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-gradle</artifactId>
Expand Down Expand Up @@ -113,6 +101,12 @@
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<exclusions>
<exclusion>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-static-analysis</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
Expand All @@ -133,6 +127,11 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openrewrite</groupId>
<artifactId>rewrite-test</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.staticanalysis;

import org.jspecify.annotations.NonNull;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.search.FindAnnotations;
import org.openrewrite.java.search.UsesJavaVersion;
import org.openrewrite.java.search.UsesType;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;

import java.time.Duration;
import java.util.Comparator;

public class AddSerialAnnotationToSerialVersionUID extends Recipe {
@Override
public String getDisplayName() {
return "Add `@Serial` annotation to `serialVersionUID`";
}

@Override
public String getDescription() {
return "Annotation any `serialVersionUID` fields with `@Serial` to indicate it's part of the serialization mechanism.";
}

@Override
public Duration getEstimatedEffortPerOccurrence() {
return Duration.ofMinutes(1);
}

@Override
@NonNull
public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(
Preconditions.and(
new UsesJavaVersion<>(14),
new UsesType<>("java.io.Serializable", true)
),
new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
if (TypeUtils.isAssignableTo("java.io.Serializable", classDecl.getType())) {
return super.visitClassDeclaration(classDecl, ctx);
}
return classDecl;
}

@Override
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
J.VariableDeclarations vd = super.visitVariableDeclarations(multiVariable, ctx);
if (isPrivateStaticFinalLongSerialVersionUID(vd) &&
FindAnnotations.find(vd, "@java.io.Serial").isEmpty()) {
maybeAddImport("java.io.Serial");
return JavaTemplate.builder("@Serial")
.imports("java.io.Serial")
.build()
.apply(getCursor(), vd.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
}
return vd;
}

private boolean isPrivateStaticFinalLongSerialVersionUID(J.VariableDeclarations vd) {
return vd.hasModifier(J.Modifier.Type.Private) &&
vd.hasModifier(J.Modifier.Type.Static) &&
vd.hasModifier(J.Modifier.Type.Final) &&
TypeUtils.asPrimitive(vd.getType()) == JavaType.Primitive.Long &&
vd.getVariables().size() == 1 &&
"serialVersionUID".equals(vd.getVariables().get(0).getSimpleName());
}
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright 2021 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.staticanalysis;

import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.Space;
import org.openrewrite.java.tree.TypeUtils;
import org.openrewrite.marker.Markers;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

public class AddSerialVersionUidToSerializable extends Recipe {

@Override
public String getDisplayName() {
return "Add `serialVersionUID` to a `Serializable` class when missing";
}

@Override
public String getDescription() {
return "A `serialVersionUID` field is strongly recommended in all `Serializable` classes. If this is not " +
"defined on a `Serializable` class, the compiler will generate this value. If a change is later made " +
"to the class, the generated value will change and attempts to deserialize the class will fail.";
}

@Override
public Set<String> getTags() {
return Collections.singleton("RSPEC-S2057");
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
final JavaTemplate template = JavaTemplate.builder("private static final long serialVersionUID = 1;").build();

@Override
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
// Anonymous classes are not of interest
return method;
}

@Override
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
// Anonymous classes are not of interest
return multiVariable;
}

@Override
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
J.ClassDeclaration c = super.visitClassDeclaration(classDecl, ctx);
if (c.getKind() != J.ClassDeclaration.Kind.Type.Class || !requiresSerialVersionField(classDecl.getType())) {
return c;
}
AtomicBoolean needsSerialVersionId = new AtomicBoolean(true);
J.Block body = c.getBody();
c = c.withBody(c.getBody().withStatements(ListUtils.map(c.getBody().getStatements(), s -> {
if (!(s instanceof J.VariableDeclarations)) {
return s;
}
J.VariableDeclarations varDecls = (J.VariableDeclarations) s;
for (J.VariableDeclarations.NamedVariable v : varDecls.getVariables()) {
if ("serialVersionUID".equals(v.getSimpleName())) {
needsSerialVersionId.set(false);
return maybeAutoFormat(varDecls, maybeFixVariableDeclarations(varDecls), ctx, new Cursor(getCursor(), body));
}
}
return s;
})));
if (needsSerialVersionId.get()) {
c = template.apply(updateCursor(c), c.getBody().getCoordinates().firstStatement());
}
return c;
}

private J.VariableDeclarations maybeFixVariableDeclarations(J.VariableDeclarations varDecls) {
List<J.Modifier> modifiers = varDecls.getModifiers();
if (!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Private) ||
!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Static) ||
!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Final)) {
varDecls = varDecls.withModifiers(Arrays.asList(
new J.Modifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, J.Modifier.Type.Private, Collections.emptyList()),
new J.Modifier(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, null, J.Modifier.Type.Static, Collections.emptyList()),
new J.Modifier(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, null, J.Modifier.Type.Final, Collections.emptyList())
));
}
if (TypeUtils.asPrimitive(varDecls.getType()) != JavaType.Primitive.Long) {
varDecls = varDecls.withTypeExpression(new J.Primitive(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JavaType.Primitive.Long));
}
return varDecls;
}

private boolean requiresSerialVersionField(@Nullable JavaType type) {
if (type == null) {
return false;
} else if (type instanceof JavaType.Primitive) {
return true;
} else if (type instanceof JavaType.Array) {
return requiresSerialVersionField(((JavaType.Array) type).getElemType());
} else if (type instanceof JavaType.Parameterized) {
JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
if (parameterized.isAssignableTo("java.util.Collection") || parameterized.isAssignableTo("java.util.Map")) {
//If the type is either a collection or a map, make sure the type parameters are serializable. We
//force all type parameters to be checked to correctly scoop up all non-serializable candidates.
boolean typeParametersSerializable = true;
for (JavaType typeParameter : parameterized.getTypeParameters()) {
typeParametersSerializable = typeParametersSerializable && requiresSerialVersionField(typeParameter);
}
return typeParametersSerializable;
}
//All other parameterized types fall through
} else if (type instanceof JavaType.FullyQualified) {
JavaType.FullyQualified fq = (JavaType.FullyQualified) type;
if (fq.getKind() == JavaType.Class.Kind.Enum) {
return false;
}

if (fq.getKind() != JavaType.Class.Kind.Interface &&
!fq.isAssignableTo("java.lang.Throwable")) {
return fq.isAssignableTo("java.io.Serializable");
}
}
return false;
}
};
}
}
Loading

0 comments on commit 1c64204

Please sign in to comment.