Skip to content

Commit 1c64204

Browse files
committed
No rewrite-static-analysis dependency. Adopt latest Apache license OR
1 parent f66a5f3 commit 1c64204

File tree

18 files changed

+4269
-14
lines changed

18 files changed

+4269
-14
lines changed

headless-services/commons/commons-rewrite/pom.xml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@
2828
<version>${project.version}</version>
2929
</dependency>
3030

31-
<!-- ObjectDiff object becomes unresolved all of a sudden for JDT compiler as of rewrite 7.32.1. The dependency should come from rewrite -->
32-
<!-- Try to remove in the future as it only seems to be required by Eclipse JDT. Maven build is happy without it -->
33-
<dependency>
34-
<groupId>de.danielbechler</groupId>
35-
<artifactId>java-object-diff</artifactId>
36-
<version>0.95</version>
37-
</dependency>
38-
3931
<dependency>
4032
<groupId>io.github.classgraph</groupId>
4133
<artifactId>classgraph</artifactId>
@@ -58,10 +50,6 @@
5850
<groupId>org.openrewrite</groupId>
5951
<artifactId>rewrite-groovy</artifactId>
6052
</dependency>
61-
<dependency>
62-
<groupId>org.openrewrite</groupId>
63-
<artifactId>rewrite-kotlin</artifactId>
64-
</dependency>
6553
<dependency>
6654
<groupId>org.openrewrite</groupId>
6755
<artifactId>rewrite-gradle</artifactId>
@@ -113,6 +101,12 @@
113101
<dependency>
114102
<groupId>org.openrewrite.recipe</groupId>
115103
<artifactId>rewrite-spring</artifactId>
104+
<exclusions>
105+
<exclusion>
106+
<groupId>org.openrewrite.recipe</groupId>
107+
<artifactId>rewrite-static-analysis</artifactId>
108+
</exclusion>
109+
</exclusions>
116110
</dependency>
117111

118112
<dependency>
@@ -133,6 +127,11 @@
133127
<version>${project.version}</version>
134128
<scope>test</scope>
135129
</dependency>
130+
<dependency>
131+
<groupId>org.openrewrite</groupId>
132+
<artifactId>rewrite-test</artifactId>
133+
<scope>test</scope>
134+
</dependency>
136135

137136
</dependencies>
138137

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.staticanalysis;
17+
18+
import org.jspecify.annotations.NonNull;
19+
import org.openrewrite.ExecutionContext;
20+
import org.openrewrite.Preconditions;
21+
import org.openrewrite.Recipe;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.java.JavaIsoVisitor;
24+
import org.openrewrite.java.JavaTemplate;
25+
import org.openrewrite.java.search.FindAnnotations;
26+
import org.openrewrite.java.search.UsesJavaVersion;
27+
import org.openrewrite.java.search.UsesType;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.JavaType;
30+
import org.openrewrite.java.tree.TypeUtils;
31+
32+
import java.time.Duration;
33+
import java.util.Comparator;
34+
35+
public class AddSerialAnnotationToSerialVersionUID extends Recipe {
36+
@Override
37+
public String getDisplayName() {
38+
return "Add `@Serial` annotation to `serialVersionUID`";
39+
}
40+
41+
@Override
42+
public String getDescription() {
43+
return "Annotation any `serialVersionUID` fields with `@Serial` to indicate it's part of the serialization mechanism.";
44+
}
45+
46+
@Override
47+
public Duration getEstimatedEffortPerOccurrence() {
48+
return Duration.ofMinutes(1);
49+
}
50+
51+
@Override
52+
@NonNull
53+
public TreeVisitor<?, ExecutionContext> getVisitor() {
54+
return Preconditions.check(
55+
Preconditions.and(
56+
new UsesJavaVersion<>(14),
57+
new UsesType<>("java.io.Serializable", true)
58+
),
59+
new JavaIsoVisitor<ExecutionContext>() {
60+
@Override
61+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
62+
if (TypeUtils.isAssignableTo("java.io.Serializable", classDecl.getType())) {
63+
return super.visitClassDeclaration(classDecl, ctx);
64+
}
65+
return classDecl;
66+
}
67+
68+
@Override
69+
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
70+
J.VariableDeclarations vd = super.visitVariableDeclarations(multiVariable, ctx);
71+
if (isPrivateStaticFinalLongSerialVersionUID(vd) &&
72+
FindAnnotations.find(vd, "@java.io.Serial").isEmpty()) {
73+
maybeAddImport("java.io.Serial");
74+
return JavaTemplate.builder("@Serial")
75+
.imports("java.io.Serial")
76+
.build()
77+
.apply(getCursor(), vd.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
78+
}
79+
return vd;
80+
}
81+
82+
private boolean isPrivateStaticFinalLongSerialVersionUID(J.VariableDeclarations vd) {
83+
return vd.hasModifier(J.Modifier.Type.Private) &&
84+
vd.hasModifier(J.Modifier.Type.Static) &&
85+
vd.hasModifier(J.Modifier.Type.Final) &&
86+
TypeUtils.asPrimitive(vd.getType()) == JavaType.Primitive.Long &&
87+
vd.getVariables().size() == 1 &&
88+
"serialVersionUID".equals(vd.getVariables().get(0).getSimpleName());
89+
}
90+
}
91+
);
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.staticanalysis;
17+
18+
import org.jspecify.annotations.Nullable;
19+
import org.openrewrite.*;
20+
import org.openrewrite.internal.ListUtils;
21+
import org.openrewrite.java.JavaIsoVisitor;
22+
import org.openrewrite.java.JavaTemplate;
23+
import org.openrewrite.java.tree.J;
24+
import org.openrewrite.java.tree.JavaType;
25+
import org.openrewrite.java.tree.Space;
26+
import org.openrewrite.java.tree.TypeUtils;
27+
import org.openrewrite.marker.Markers;
28+
29+
import java.util.Arrays;
30+
import java.util.Collections;
31+
import java.util.List;
32+
import java.util.Set;
33+
import java.util.concurrent.atomic.AtomicBoolean;
34+
35+
public class AddSerialVersionUidToSerializable extends Recipe {
36+
37+
@Override
38+
public String getDisplayName() {
39+
return "Add `serialVersionUID` to a `Serializable` class when missing";
40+
}
41+
42+
@Override
43+
public String getDescription() {
44+
return "A `serialVersionUID` field is strongly recommended in all `Serializable` classes. If this is not " +
45+
"defined on a `Serializable` class, the compiler will generate this value. If a change is later made " +
46+
"to the class, the generated value will change and attempts to deserialize the class will fail.";
47+
}
48+
49+
@Override
50+
public Set<String> getTags() {
51+
return Collections.singleton("RSPEC-S2057");
52+
}
53+
54+
@Override
55+
public TreeVisitor<?, ExecutionContext> getVisitor() {
56+
return new JavaIsoVisitor<ExecutionContext>() {
57+
final JavaTemplate template = JavaTemplate.builder("private static final long serialVersionUID = 1;").build();
58+
59+
@Override
60+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
61+
// Anonymous classes are not of interest
62+
return method;
63+
}
64+
65+
@Override
66+
public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) {
67+
// Anonymous classes are not of interest
68+
return multiVariable;
69+
}
70+
71+
@Override
72+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
73+
J.ClassDeclaration c = super.visitClassDeclaration(classDecl, ctx);
74+
if (c.getKind() != J.ClassDeclaration.Kind.Type.Class || !requiresSerialVersionField(classDecl.getType())) {
75+
return c;
76+
}
77+
AtomicBoolean needsSerialVersionId = new AtomicBoolean(true);
78+
J.Block body = c.getBody();
79+
c = c.withBody(c.getBody().withStatements(ListUtils.map(c.getBody().getStatements(), s -> {
80+
if (!(s instanceof J.VariableDeclarations)) {
81+
return s;
82+
}
83+
J.VariableDeclarations varDecls = (J.VariableDeclarations) s;
84+
for (J.VariableDeclarations.NamedVariable v : varDecls.getVariables()) {
85+
if ("serialVersionUID".equals(v.getSimpleName())) {
86+
needsSerialVersionId.set(false);
87+
return maybeAutoFormat(varDecls, maybeFixVariableDeclarations(varDecls), ctx, new Cursor(getCursor(), body));
88+
}
89+
}
90+
return s;
91+
})));
92+
if (needsSerialVersionId.get()) {
93+
c = template.apply(updateCursor(c), c.getBody().getCoordinates().firstStatement());
94+
}
95+
return c;
96+
}
97+
98+
private J.VariableDeclarations maybeFixVariableDeclarations(J.VariableDeclarations varDecls) {
99+
List<J.Modifier> modifiers = varDecls.getModifiers();
100+
if (!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Private) ||
101+
!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Static) ||
102+
!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Final)) {
103+
varDecls = varDecls.withModifiers(Arrays.asList(
104+
new J.Modifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, J.Modifier.Type.Private, Collections.emptyList()),
105+
new J.Modifier(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, null, J.Modifier.Type.Static, Collections.emptyList()),
106+
new J.Modifier(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, null, J.Modifier.Type.Final, Collections.emptyList())
107+
));
108+
}
109+
if (TypeUtils.asPrimitive(varDecls.getType()) != JavaType.Primitive.Long) {
110+
varDecls = varDecls.withTypeExpression(new J.Primitive(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JavaType.Primitive.Long));
111+
}
112+
return varDecls;
113+
}
114+
115+
private boolean requiresSerialVersionField(@Nullable JavaType type) {
116+
if (type == null) {
117+
return false;
118+
} else if (type instanceof JavaType.Primitive) {
119+
return true;
120+
} else if (type instanceof JavaType.Array) {
121+
return requiresSerialVersionField(((JavaType.Array) type).getElemType());
122+
} else if (type instanceof JavaType.Parameterized) {
123+
JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
124+
if (parameterized.isAssignableTo("java.util.Collection") || parameterized.isAssignableTo("java.util.Map")) {
125+
//If the type is either a collection or a map, make sure the type parameters are serializable. We
126+
//force all type parameters to be checked to correctly scoop up all non-serializable candidates.
127+
boolean typeParametersSerializable = true;
128+
for (JavaType typeParameter : parameterized.getTypeParameters()) {
129+
typeParametersSerializable = typeParametersSerializable && requiresSerialVersionField(typeParameter);
130+
}
131+
return typeParametersSerializable;
132+
}
133+
//All other parameterized types fall through
134+
} else if (type instanceof JavaType.FullyQualified) {
135+
JavaType.FullyQualified fq = (JavaType.FullyQualified) type;
136+
if (fq.getKind() == JavaType.Class.Kind.Enum) {
137+
return false;
138+
}
139+
140+
if (fq.getKind() != JavaType.Class.Kind.Interface &&
141+
!fq.isAssignableTo("java.lang.Throwable")) {
142+
return fq.isAssignableTo("java.io.Serializable");
143+
}
144+
}
145+
return false;
146+
}
147+
};
148+
}
149+
}

0 commit comments

Comments
 (0)