GH-1192 - Migrate code base to jSpecify for nullness verification.

This commit is contained in:
Oliver Drotbohm
2025-02-17 17:10:47 +01:00
parent a136ff920b
commit 9ea2b24b53
134 changed files with 548 additions and 211 deletions

View File

@@ -25,9 +25,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.core.ApplicationModuleDependency;
import org.springframework.modulith.core.ApplicationModules;

View File

@@ -23,9 +23,9 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.jspecify.annotations.Nullable;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.lang.Nullable;
import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.docs.ConfigurationProperties.ConfigurationProperty;
import org.springframework.util.Assert;
@@ -124,7 +124,6 @@ class ConfigurationProperties implements Iterable<ConfigurationProperty> {
static record ConfigurationProperty(String name, @Nullable String description, String type, String sourceType,
@Nullable String defaultValue) {
@SuppressWarnings("null")
static Stream<ConfigurationProperty> of(Map<String, Object> source) {
String sourceType = getAsString(source, "sourceType");
@@ -133,9 +132,9 @@ class ConfigurationProperties implements Iterable<ConfigurationProperty> {
return Stream.empty();
}
ConfigurationProperty property = new ConfigurationProperty(getAsString(source, "name"),
ConfigurationProperty property = new ConfigurationProperty(getRequiredAsString(source, "name"),
getAsString(source, "description"),
getAsString(source, "type"),
getRequiredAsString(source, "type"),
sourceType,
getAsString(source, "defaultValue"));
@@ -146,6 +145,15 @@ class ConfigurationProperties implements Iterable<ConfigurationProperty> {
return StringUtils.hasText(sourceType);
}
private static String getRequiredAsString(Map<String, Object> source, String key) {
var value = getAsString(source, key);
Assert.notNull(value, "No value found for key %s in %s!".formatted(key, source));
return value;
}
private static @Nullable String getAsString(Map<String, Object> source, String key) {
Object value = source.get(key);

View File

@@ -36,7 +36,8 @@ import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.lang.Nullable;
import org.jspecify.annotations.Nullable;
import org.springframework.lang.Contract;
import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.core.DependencyDepth;
@@ -98,7 +99,7 @@ public class Documenter {
private boolean cleared;
private Map<ApplicationModule, Component> components;
private @Nullable Map<ApplicationModule, Component> components;
/**
* Creates a new {@link Documenter} for the {@link ApplicationModules} created for the given modulith type in the
@@ -223,6 +224,7 @@ public class Documenter {
* @return the current instance, will never be {@literal null}.
* @since 1.2.2
*/
@Contract("_, _ -> this")
public Documenter writeAggregatingDocument(DiagramOptions diagramOptions, CanvasOptions canvasOptions) {
Assert.notNull(diagramOptions, "DiagramOptions must not be null!");
@@ -485,18 +487,19 @@ public class Documenter {
.forEach(it -> it.addTags(DependencyType.USES_COMPONENT.toString()));
}
@SuppressWarnings("null")
private Map<ApplicationModule, Component> getComponents(DiagramOptions options) {
if (components == null) {
if (this.components == null) {
this.components = modules.stream() //
.collect(Collectors.toMap(Function.identity(),
it -> container.addComponent(options.defaultDisplayName.apply(it), "", "Module")));
this.components.forEach((key, value) -> addDependencies(key, value, options));
components.forEach((key, value) -> addDependencies(key, value, options));
}
return components;
return this.components;
}
private void addComponentsToView(ApplicationModule module, ComponentView view, DiagramOptions options) {
@@ -643,6 +646,11 @@ public class Documenter {
Styles styles) {
var component = components.get(module);
if (component == null) {
throw new IllegalStateException("Couldn't find component for module %s!".formatted(module));
}
var selector = options.colorSelector;
// Apply custom color if configured
@@ -1061,6 +1069,7 @@ public class Documenter {
return new CanvasOptions(groupers, apiBase, targetFileName, hideInternals, hideEmptyLines);
}
@Nullable
String getApiBase() {
return apiBase;
}
@@ -1083,7 +1092,10 @@ public class Documenter {
// Wipe entries without any beans
new HashSet<>(result.keySet()).forEach(key -> {
if (result.get(key).isEmpty()) {
var value = result.get(key);
if (value != null && value.isEmpty()) {
result.remove(key);
}
});
@@ -1161,10 +1173,10 @@ public class Documenter {
*
* @param name must not be {@literal null} or empty.
* @param predicate must not be {@literal null}.
* @param description must not be {@literal null} or empty.
* @param description can be {@literal null}.
* @return will never be {@literal null}.
*/
public static Grouping of(String name, Predicate<SpringBean> predicate, String description) {
public static Grouping of(String name, Predicate<SpringBean> predicate, @Nullable String description) {
return new Grouping(name, predicate, description);
}

View File

@@ -23,9 +23,9 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.json.BasicJsonParser;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.modulith.docs.metadata.MethodMetadata;
import org.springframework.modulith.docs.metadata.TypeMetadata;
import org.springframework.modulith.docs.util.BuildSystemUtils;
@@ -134,28 +134,39 @@ class SpringModulithDocumentationSource implements DocumentationSource {
@SuppressWarnings("unchecked")
private static TypeMetadata typeMetadata(Map<String, Object> source) {
var methods = source.containsKey("methods")
? ((List<Map<String, Object>>) source.get("methods")).stream()
.map(SpringModulithDocumentationSource::methodMetadata)
.toList()
var sourceMethods = (List<Map<String, Object>>) source.get("methods");
var methods = sourceMethods != null
? sourceMethods.stream().map(SpringModulithDocumentationSource::methodMetadata).toList()
: Collections.<MethodMetadata> emptyList();
return new TypeMetadata(
source.get("name").toString(),
getString(source, "comment"),
methods);
var name = source.get("name");
if (name == null) {
throw new IllegalArgumentException("Source map does not contain a name entry! %s".formatted(source));
}
return new TypeMetadata(name.toString(), getString(source, "comment"), methods);
}
private static MethodMetadata methodMetadata(Map<String, Object> source) {
return new MethodMetadata(
source.get("name").toString(),
source.get("signature").toString(),
getString(source, "comment"));
var name = source.get("name");
if (name == null) {
throw new IllegalArgumentException("No name found in source map! %s".formatted(source));
}
var signature = source.get("signature");
if (signature == null) {
throw new IllegalArgumentException("No signature found in source map! %s".formatted(source));
}
return new MethodMetadata(name.toString(), signature.toString(), getString(source, "comment"));
}
@Nullable
private static String getString(Map<String, Object> source, String key) {
private static @Nullable String getString(Map<String, Object> source, String key) {
Object result = source.get(key);

View File

@@ -19,7 +19,7 @@ import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.springframework.lang.Nullable;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**

View File

@@ -17,7 +17,7 @@ package org.springframework.modulith.docs.metadata;
import java.util.List;
import org.springframework.lang.Nullable;
import org.jspecify.annotations.Nullable;
/**
* Metadata about a Java type.

View File

@@ -1,5 +1,5 @@
/**
* Documentation support for Spring Modulith.
*/
@org.springframework.lang.NonNullApi
@org.jspecify.annotations.NullMarked
package org.springframework.modulith.docs;