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

org.openrewrite.staticanalysis.FinalClassVisitor Maven / Gradle / Ivy

/*
 * 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.staticanalysis; import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Tree; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static java.util.Collections.emptyList; import static org.openrewrite.Tree.randomId; import static org.openrewrite.staticanalysis.ModifierOrder.sortModifiers; public class FinalClassVisitor extends JavaIsoVisitor { Tree visitRoot; final Set typesToFinalize = new HashSet<>(); final Set typesToNotFinalize = new HashSet<>(); @Override public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { boolean root = false; if (visitRoot == null && tree != null) { visitRoot = tree; root = true; } J result = super.visit(tree, ctx); if (root) { visitRoot = null; typesToFinalize.removeAll(typesToNotFinalize); if (!typesToFinalize.isEmpty()) { result = new FinalizingVisitor(typesToFinalize).visit(tree, ctx); } } return result; } @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDeclaration, ExecutionContext ctx) { J.ClassDeclaration cd = super.visitClassDeclaration(classDeclaration, ctx); if (cd.getKind() != J.ClassDeclaration.Kind.Type.Class || cd.hasModifier(J.Modifier.Type.Abstract) || cd.hasModifier(J.Modifier.Type.Final) || cd.getType() == null) { return cd; } excludeSupertypes(cd.getType()); boolean allPrivate = true; int constructorCount = 0; for (Statement s : cd.getBody().getStatements()) { if (s instanceof J.MethodDeclaration && ((J.MethodDeclaration) s).isConstructor()) { J.MethodDeclaration constructor = (J.MethodDeclaration) s; constructorCount++; if (!constructor.hasModifier(J.Modifier.Type.Private)) { allPrivate = false; } } if (constructorCount > 0 && !allPrivate) { return cd; } } if (constructorCount > 0) { typesToFinalize.add(cd.getType().getFullyQualifiedName()); } return cd; } private void excludeSupertypes(JavaType.FullyQualified type) { if (type.getSupertype() != null && type.getOwningClass() != null && typesToNotFinalize.add(type.getSupertype().getFullyQualifiedName())) { excludeSupertypes(type.getSupertype()); } } /** * Adding the `final` modifier is performed in a second phase, because we first need to check if any of the * classes need to remain non-final due to inheritance. */ private static class FinalizingVisitor extends JavaIsoVisitor { private final Set typesToFinalize; public FinalizingVisitor(Set typesToFinalize) { this.typesToFinalize = typesToFinalize; } @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); if (cd.getType() != null && typesToFinalize.remove(cd.getType().getFullyQualifiedName())) { List modifiers = new ArrayList<>(cd.getModifiers()); modifiers.add(new J.Modifier(randomId(), Space.EMPTY, Markers.EMPTY, null, J.Modifier.Type.Final, emptyList())); modifiers = sortModifiers(modifiers); cd = cd.withModifiers(modifiers); if (cd.getType() instanceof JavaType.Class && !cd.getType().hasFlags(Flag.Final)) { Set flags = new HashSet<>(cd.getType().getFlags()); flags.add(Flag.Final); cd = cd.withType(((JavaType.Class) cd.getType()).withFlags(flags)); } // Temporary work around until issue https://github.com/openrewrite/rewrite/issues/2348 is implemented. if (!cd.getLeadingAnnotations().isEmpty()) { // Setting the prefix to empty will cause the `Spaces` visitor to fix the formatting. cd = cd.getPadding().withKind(cd.getPadding().getKind().withPrefix(Space.EMPTY)); } assert getCursor().getParent() != null; cd = autoFormat(cd, cd.getName(), ctx, getCursor().getParent()); } return cd; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy