Modulith support (continued)
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@@ -17,6 +18,7 @@ import org.openrewrite.ExecutionContext;
|
||||
import org.openrewrite.Tree;
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.JavaVisitor;
|
||||
import org.openrewrite.java.marker.JavaSourceSet;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.CompilationUnit;
|
||||
import org.openrewrite.java.tree.J.FieldAccess;
|
||||
@@ -33,6 +35,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTy
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.MarkerVisitorContext;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.ProjectParser;
|
||||
|
||||
public class ModulithTypeReferenceViolation implements RecipeCodeActionDescriptor {
|
||||
|
||||
@@ -59,6 +62,10 @@ public class ModulithTypeReferenceViolation implements RecipeCodeActionDescripto
|
||||
if (appModules == null) {
|
||||
return cu;
|
||||
} else {
|
||||
JavaSourceSet sourceSet = cu.getMarkers().findFirst(JavaSourceSet.class).orElse(null);
|
||||
if (sourceSet != null && ProjectParser.TEST.equals(sourceSet.getName())) {
|
||||
return cu;
|
||||
}
|
||||
String pkgName = cu.getPackageDeclaration() == null ? "" : cu.getPackageDeclaration().getPackageName();
|
||||
p.putMessage(MSG_PKG_NAME, pkgName);
|
||||
return super.visitCompilationUnit(cu, p);
|
||||
@@ -76,19 +83,26 @@ public class ModulithTypeReferenceViolation implements RecipeCodeActionDescripto
|
||||
@Override
|
||||
public Identifier visitIdentifier(Identifier identifier, ExecutionContext p) {
|
||||
Identifier i = super.visitIdentifier(identifier, p);
|
||||
if (!(getCursor().getParent().firstEnclosingOrThrow(J.class) instanceof J.FieldAccess)) {
|
||||
return process(i, p.getMessage(MSG_PKG_NAME), TypeUtils.asFullyQualified(identifier.getType()));
|
||||
if (getCursor().getParent().firstEnclosingOrThrow(J.class) instanceof J.FieldAccess) {
|
||||
return i;
|
||||
}
|
||||
// check if identifier is a simple name of the type
|
||||
FullyQualified type = TypeUtils.asFullyQualified(identifier.getType());
|
||||
if (type != null && identifier.getSimpleName().equals(type.getClassName())) {
|
||||
return process(i, p.getMessage(MSG_PKG_NAME), type);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
private <T extends J> T process(T node, String packageName, FullyQualified type) {
|
||||
if (type != null) {
|
||||
if (!appModules.isReferenceAllowed(packageName, type.getFullyQualifiedName())) {
|
||||
Optional<T> opt = appModules.getModuleNotExposingType(packageName, type.getFullyQualifiedName()).map(module -> {
|
||||
FixAssistMarker fixMarker = new FixAssistMarker(Tree.randomId(), getId())
|
||||
.withLabel("Type is not allowed to be used in this package. Consider changing you 'Modulith' structure." );
|
||||
node = node.withMarkers(node.getMarkers().add(fixMarker));
|
||||
}
|
||||
.withLabel("Cannot use type in this package. Type is not exposed in module '" + module.name() + "'." );
|
||||
return node.withMarkers(node.getMarkers().add(fixMarker));
|
||||
});
|
||||
return opt.orElse(node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@ public class ServerUtils {
|
||||
private static final List<String> CLASS_FILES_TO_WATCH_GLOB = List.of("**/*.class");
|
||||
|
||||
public static void listenToClassFileChanges(FileObserver fileObserver, JavaProjectFinder projectFinder, Consumer<IJavaProject> callback) {
|
||||
fileObserver.onFilesChanged(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
|
||||
fileObserver.onFilesCreated(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
|
||||
fileObserver.onAnyChange(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
|
||||
}
|
||||
|
||||
private static void handleFiles(JavaProjectFinder projectFinder, String[] files, Consumer<IJavaProject> callback) {
|
||||
|
||||
@@ -12,7 +12,7 @@ package org.springframework.ide.vscode.boot.modulith;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
record AppModule(
|
||||
public record AppModule(
|
||||
String name,
|
||||
String basePackage,
|
||||
Collection<String> namedInterfaces
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.modulith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class AppModules {
|
||||
|
||||
@@ -20,14 +22,42 @@ public final class AppModules {
|
||||
this.modules = modules;
|
||||
}
|
||||
|
||||
public boolean isReferenceAllowed(String targetPackage, String referenceFqName) {
|
||||
String referencePackage = ModulithService.getPackageNameFromTypeFQName(referenceFqName);
|
||||
return modules
|
||||
.stream()
|
||||
.filter(m -> m.basePackage().equals(referencePackage))
|
||||
.findFirst()
|
||||
.map(m -> m.namedInterfaces().contains(referenceFqName))
|
||||
.orElse(true);
|
||||
public Optional<AppModule> getModuleNotExposingType(String targetPackage, String referenceFqName) {
|
||||
return getModuleForType(referenceFqName).flatMap(refModule -> {
|
||||
if (refModule.namedInterfaces().contains(referenceFqName)) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
if (getModuleForPackage(targetPackage).map(targetModule -> targetModule == refModule).orElse(false)) {
|
||||
// same module for target package and reference type
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(refModule);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Optional<AppModule> getModuleForPackage(String pkgName) {
|
||||
return generatePackageHierarchy(pkgName)
|
||||
.stream()
|
||||
.map(p -> modules.stream().filter(m -> m.basePackage().equals(p)).findFirst())
|
||||
.filter(o -> o.isPresent())
|
||||
.map(o -> o.get())
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private Optional<AppModule> getModuleForType(String typeFqName) {
|
||||
return getModuleForPackage(ModulithService.getPackageNameFromTypeFQName(typeFqName));
|
||||
}
|
||||
|
||||
private List<String> generatePackageHierarchy(String pkgName) {
|
||||
List<String> packageHierarchy = new ArrayList<>();
|
||||
packageHierarchy.add(pkgName);
|
||||
for (int i = pkgName.length() - 1; i >= 0; i--) {
|
||||
if (pkgName.charAt(i) == '.') {
|
||||
packageHierarchy.add(pkgName.substring(0, i));
|
||||
}
|
||||
}
|
||||
return packageHierarchy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.boot.modulith;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@@ -29,6 +31,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.java.Version;
|
||||
@@ -85,7 +88,16 @@ public class ModulithService {
|
||||
for (String f : files) {
|
||||
URI uri = URI.create(f);
|
||||
TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toASCIIString());
|
||||
projectFinder.find(docId).ifPresent(this::invalidate);
|
||||
projectFinder.find(docId).ifPresent(project -> {
|
||||
synchronized (project) {
|
||||
if (cache.containsKey(project)) {
|
||||
Path filePath = Paths.get(uri);
|
||||
if (IClasspathUtil.getProjectJavaSourceFoldersWithoutTests(project.getClasspath()).map(folder -> folder.toPath()).anyMatch(folderPath -> filePath.startsWith(folderPath))) {
|
||||
cache.remove(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user