GH-813 - Introduce FormattableType.

As replacement for misspelled Formatable type.
This commit is contained in:
Oliver Drotbohm
2024-09-20 22:02:17 +02:00
parent 9d818b467c
commit 438f17b56d
11 changed files with 250 additions and 120 deletions

View File

@@ -306,7 +306,7 @@ public class ApplicationModule implements Comparable<ApplicationModule> {
return getType(type.getName())
.map(it -> ArchitecturallyEvidentType.of(it, getSpringBeansInternal()))
.orElseThrow(() -> new IllegalArgumentException("Couldn't find type %s in module %s!".formatted(
FormatableType.of(type).getAbbreviatedFullName(this), getName())));
FormattableType.of(type).getAbbreviatedFullName(this), getName())));
}
/**
@@ -1267,8 +1267,8 @@ public class ApplicationModule implements Comparable<ApplicationModule> {
var violationText = INVALID_SUB_MODULE_REFERENCE
.formatted(originModule.getName(), targetModule.getName(),
FormatableType.of(source).getAbbreviatedFullName(originModule),
FormatableType.of(target).getAbbreviatedFullName(targetModule));
FormattableType.of(source).getAbbreviatedFullName(originModule),
FormattableType.of(target).getAbbreviatedFullName(targetModule));
return violations.and(new Violation(violationText));
}
@@ -1289,7 +1289,7 @@ public class ApplicationModule implements Comparable<ApplicationModule> {
*/
@Override
public String toString() {
return type.format(FormatableType.of(source), FormatableType.of(target));
return type.format(FormattableType.of(source), FormattableType.of(target));
}
/*

View File

@@ -99,7 +99,7 @@ public abstract class ArchitecturallyEvidentType {
* @return will never be {@literal null}.
*/
String getAbbreviatedFullName() {
return FormatableType.of(getType()).getAbbreviatedFullName();
return FormattableType.of(getType()).getAbbreviatedFullName();
}
/**

View File

@@ -52,6 +52,15 @@ public enum DependencyType {
public String format(FormatableType source, FormatableType target) {
return String.format("Component %s using %s", source.getAbbreviatedFullName(), target.getAbbreviatedFullName());
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.core.DependencyType#format(org.springframework.modulith.core.FormattableType, org.springframework.modulith.core.FormattableType)
*/
@Override
public String format(FormattableType source, FormattableType target) {
return String.format("Component %s using %s", source.getAbbreviatedFullName(), target.getAbbreviatedFullName());
}
},
/**
@@ -68,6 +77,16 @@ public enum DependencyType {
return String.format("Entity %s depending on %s", source.getAbbreviatedFullName(),
target.getAbbreviatedFullName());
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.core.DependencyType#format(org.springframework.modulith.core.FormattableType, org.springframework.modulith.core.FormattableType)
*/
@Override
public String format(FormattableType source, FormattableType target) {
return String.format("Entity %s depending on %s", source.getAbbreviatedFullName(),
target.getAbbreviatedFullName());
}
},
/**
@@ -85,6 +104,16 @@ public enum DependencyType {
return String.format("%s listening to events of type %s", source.getAbbreviatedFullName(),
target.getAbbreviatedFullName());
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.core.DependencyType#format(org.springframework.modulith.core.FormattableType, org.springframework.modulith.core.FormattableType)
*/
@Override
public String format(FormattableType source, FormattableType target) {
return String.format("%s listening to events of type %s", source.getAbbreviatedFullName(),
target.getAbbreviatedFullName());
}
},
DEFAULT {
@@ -106,6 +135,15 @@ public enum DependencyType {
public String format(FormatableType source, FormatableType target) {
return String.format("%s depending on %s", source.getAbbreviatedFullName(), target.getAbbreviatedFullName());
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.core.DependencyType#format(org.springframework.modulith.core.FormattableType, org.springframework.modulith.core.FormattableType)
*/
@Override
public String format(FormattableType source, FormattableType target) {
return String.format("%s depending on %s", source.getAbbreviatedFullName(), target.getAbbreviatedFullName());
}
};
/**
@@ -137,8 +175,14 @@ public enum DependencyType {
return forParameter(dependency.getTargetClass());
}
/**
* @deprecated since 1.3, prefer {@link #format(FormattableType, FormattableType)}.
*/
@Deprecated
public abstract String format(FormatableType source, FormatableType target);
public abstract String format(FormattableType source, FormattableType target);
/**
* Returns all {@link DependencyType}s except the given ones.
*

View File

@@ -15,18 +15,8 @@
*/
package org.springframework.modulith.core;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.function.SingletonSupplier;
import com.tngtech.archunit.core.domain.JavaClass;
@@ -34,28 +24,10 @@ import com.tngtech.archunit.core.domain.JavaClass;
* Wrapper around {@link JavaClass} that allows creating additional formatted names.
*
* @author Oliver Drotbohm
* @deprecated since 1.3, use {@link FormattableType} instead.
*/
public class FormatableType {
private static final Map<String, FormatableType> CACHE = new ConcurrentHashMap<>();
private final String type;
private final Supplier<String> abbreviatedName;
/**
* Creates a new {@link FormatableType} for the given source {@link String} and lazily computed abbreviated name.
*
* @param type must not be {@literal null} or empty.
* @param abbreviatedName must not be {@literal null}.
*/
private FormatableType(String type, Supplier<String> abbreviatedName) {
Assert.hasText(type, "Type string must not be null or empty!");
Assert.notNull(abbreviatedName, "Computed abbreviated name must not be null!");
this.type = type;
this.abbreviatedName = abbreviatedName;
}
@Deprecated
public abstract class FormatableType {
/**
* Creates a new {@link FormatableType} for the given {@link JavaClass}.
@@ -67,7 +39,7 @@ public class FormatableType {
Assert.notNull(type, "JavaClass must not be null!");
return CACHE.computeIfAbsent(type.getName(), FormatableType::new);
return FormattableType.of(type);
}
/**
@@ -77,7 +49,7 @@ public class FormatableType {
* @return will never be {@literal null}.
*/
public static FormatableType of(Class<?> type) {
return CACHE.computeIfAbsent(type.getName(), FormatableType::new);
return FormattableType.of(type);
}
/**
@@ -87,35 +59,7 @@ public class FormatableType {
* @return will never be {@literal null}.
*/
public static String format(Iterable<JavaClass> types) {
Assert.notNull(types, "Types must not be null!");
return StreamSupport.stream(types.spliterator(), false)
.map(FormatableType::of)
.map(FormatableType::getAbbreviatedFullName)
.collect(Collectors.joining(", "));
}
/**
* Creates a new {@link FormatableType} for the given fully-qualified type name.
*
* @param type must not be {@literal null} or empty.
*/
private FormatableType(String type) {
Assert.hasText(type, "Type must not be null or empty!");
this.type = type;
this.abbreviatedName = SingletonSupplier.of(() -> {
String abbreviatedPackage = Stream //
.of(ClassUtils.getPackageName(type).split("\\.")) //
.map(it -> it.substring(0, 1)) //
.collect(Collectors.joining("."));
return abbreviatedPackage.concat(".") //
.concat(ClassUtils.getShortName(getFullName()));
});
return FormattableType.format(types);
}
/**
@@ -124,9 +68,7 @@ public class FormatableType {
*
* @return will never be {@literal null}.
*/
public String getAbbreviatedFullName() {
return abbreviatedName.get();
}
public abstract String getAbbreviatedFullName();
/**
* Returns the abbreviated full name of the type abbreviating only the part of the given {@link ApplicationModule}'s
@@ -135,48 +77,12 @@ public class FormatableType {
* @param module can be {@literal null}.
* @return will never be {@literal null}.
*/
public String getAbbreviatedFullName(@Nullable ApplicationModule module) {
if (module == null) {
return getAbbreviatedFullName();
}
String basePackageName = module.getBasePackage().getName();
if (!StringUtils.hasText(basePackageName)) {
return getAbbreviatedFullName();
}
String typePackageName = ClassUtils.getPackageName(type);
if (basePackageName.equals(typePackageName)) {
return getAbbreviatedFullName();
}
if (!typePackageName.startsWith(basePackageName)) {
return getFullName();
}
return abbreviate(basePackageName) //
.concat(typePackageName.substring(basePackageName.length())) //
.concat(".") //
.concat(ClassUtils.getShortName(getFullName()));
}
public abstract String getAbbreviatedFullName(@Nullable ApplicationModule module);
/**
* Returns the type's full name.
*
* @return will never be {@literal null}.
*/
public String getFullName() {
return type.replace("$", ".");
}
private static String abbreviate(String source) {
return Stream //
.of(source.split("\\.")) //
.map(it -> it.substring(0, 1)) //
.collect(Collectors.joining("."));
}
public abstract String getFullName();
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2020-2024 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.springframework.modulith.core;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.function.SingletonSupplier;
import com.tngtech.archunit.core.domain.JavaClass;
/**
* Wrapper around {@link JavaClass} that allows creating additional formatted names.
*
* @author Oliver Drotbohm
*/
@SuppressWarnings("deprecation")
public class FormattableType extends FormatableType {
private static final Map<String, FormattableType> CACHE = new ConcurrentHashMap<>();
private final String type;
private final Supplier<String> abbreviatedName;
/**
* Creates a new {@link FormatableType} for the given source {@link String} and lazily computed abbreviated name.
*
* @param type must not be {@literal null} or empty.
* @param abbreviatedName must not be {@literal null}.
*/
private FormattableType(String type, Supplier<String> abbreviatedName) {
Assert.hasText(type, "Type string must not be null or empty!");
Assert.notNull(abbreviatedName, "Computed abbreviated name must not be null!");
this.type = type;
this.abbreviatedName = abbreviatedName;
}
/**
* Creates a new {@link FormatableType} for the given fully-qualified type name.
*
* @param type must not be {@literal null} or empty.
*/
private FormattableType(String type) {
Assert.hasText(type, "Type must not be null or empty!");
this.type = type;
this.abbreviatedName = SingletonSupplier.of(() -> {
String abbreviatedPackage = Stream //
.of(ClassUtils.getPackageName(type).split("\\.")) //
.map(it -> it.substring(0, 1)) //
.collect(Collectors.joining("."));
return abbreviatedPackage.concat(".") //
.concat(ClassUtils.getShortName(getFullName()));
});
}
/**
* Creates a new {@link FormatableType} for the given {@link JavaClass}.
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
*/
public static FormattableType of(JavaClass type) {
Assert.notNull(type, "JavaClass must not be null!");
return CACHE.computeIfAbsent(type.getName(), FormattableType::new);
}
/**
* Creates a new {@link FormatableType} for the given {@link Class}.
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
*/
public static FormattableType of(Class<?> type) {
return CACHE.computeIfAbsent(type.getName(), FormattableType::new);
}
/**
* Formats the given {@link JavaClass}es by rendering a comma-separated list with the abbreviated class names.
*
* @param types must not be {@literal null}.
* @return will never be {@literal null}.
*/
public static String format(Iterable<JavaClass> types) {
Assert.notNull(types, "Types must not be null!");
return StreamSupport.stream(types.spliterator(), false)
.map(FormattableType::of)
.map(FormattableType::getAbbreviatedFullName)
.collect(Collectors.joining(", "));
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.core.FormatableType#getAbbreviatedFullName()
*/
@Override
public String getAbbreviatedFullName() {
return abbreviatedName.get();
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.core.FormatableType#getAbbreviatedFullName(org.springframework.modulith.core.ApplicationModule)
*/
@Override
public String getAbbreviatedFullName(@Nullable ApplicationModule module) {
if (module == null) {
return getAbbreviatedFullName();
}
String basePackageName = module.getBasePackage().getName();
if (!StringUtils.hasText(basePackageName)) {
return getAbbreviatedFullName();
}
String typePackageName = ClassUtils.getPackageName(type);
if (basePackageName.equals(typePackageName)) {
return getAbbreviatedFullName();
}
if (!typePackageName.startsWith(basePackageName)) {
return getFullName();
}
return abbreviate(basePackageName) //
.concat(typePackageName.substring(basePackageName.length())) //
.concat(".") //
.concat(ClassUtils.getShortName(getFullName()));
}
/*
* (non-Javadoc)
* @see org.springframework.modulith.core.FormatableType#getFullName()
*/
@Override
public String getFullName() {
return type.replace("$", ".");
}
private static String abbreviate(String source) {
return Stream //
.of(source.split("\\.")) //
.map(it -> it.substring(0, 1)) //
.collect(Collectors.joining("."));
}
}

View File

@@ -30,7 +30,7 @@ class JavaAccessSource implements Source {
private final static Pattern LAMBDA_EXTRACTOR = Pattern.compile("lambda\\$(.*)\\$.*");
private final FormatableType type;
private final FormattableType type;
private final JavaCodeUnit method;
private final String name;
@@ -41,7 +41,7 @@ class JavaAccessSource implements Source {
*/
public JavaAccessSource(JavaAccess<?> access) {
this.type = FormatableType.of(access.getOriginOwner());
this.type = FormattableType.of(access.getOriginOwner());
this.method = access.getOrigin();
String name = method.getName();

View File

@@ -383,7 +383,7 @@ public class JavaPackage implements DescribedIterable<JavaClass>, Comparable<Jav
if (annotatedTypes.size() > 1) {
throw new IllegalStateException(MULTIPLE_TYPES_ANNOTATED_WITH.formatted(name,
FormatableType.of(annotationType).getAbbreviatedFullName(), annotatedTypes));
FormattableType.of(annotationType).getAbbreviatedFullName(), annotatedTypes));
}
return annotatedTypes.isEmpty() ? Optional.empty() : Optional.of(annotatedTypes.get(0));

View File

@@ -117,7 +117,7 @@ public class NamedInterface implements Iterable<JavaClass> {
// Illegal in the base package
Assert.state(withDefaultedNamedInterface.isEmpty(),
() -> "Cannot use named interface defaulting for type(s) %s located in base package!"
.formatted(FormatableType.format(withDefaultedNamedInterface)));
.formatted(FormattableType.format(withDefaultedNamedInterface)));
}
return new NamedInterface(UNNAMED_NAME, basePackageClasses
@@ -227,7 +227,7 @@ public class NamedInterface implements Iterable<JavaClass> {
public String toString() {
return "NamedInterface: name=%s, types=[%s]" //
.formatted(name, classes.isEmpty() ? "" : " " + FormatableType.format(classes) + " ");
.formatted(name, classes.isEmpty() ? "" : " " + FormattableType.format(classes) + " ");
}
/**

View File

@@ -31,7 +31,7 @@ import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.core.ArchitecturallyEvidentType;
import org.springframework.modulith.core.DependencyType;
import org.springframework.modulith.core.EventType;
import org.springframework.modulith.core.FormatableType;
import org.springframework.modulith.core.FormattableType;
import org.springframework.modulith.core.Source;
import org.springframework.modulith.core.SpringBean;
import org.springframework.modulith.docs.ConfigurationProperties.ModuleProperty;
@@ -254,7 +254,7 @@ class Asciidoctor {
var module = modules.getModuleByType(source).orElse(null);
var typeAndMethod = toCode(
toTypeAndMethod(FormatableType.of(source).getAbbreviatedFullName(module), methodSignature));
toTypeAndMethod(FormattableType.of(source).getAbbreviatedFullName(module), methodSignature));
if (module == null
|| !source.getModifiers().contains(JavaModifier.PUBLIC)

View File

@@ -23,7 +23,7 @@ import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.aop.framework.Advised;
import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.core.FormatableType;
import org.springframework.modulith.core.FormattableType;
import org.springframework.modulith.core.SpringBean;
import org.springframework.util.Assert;
@@ -151,8 +151,8 @@ class DefaultObservedModule implements ObservedModule {
private static String toString(Class<?> type, Method method, ApplicationModule module) {
var typeName = module.getType(type.getName())
.map(FormatableType::of)
.map(FormatableType::getAbbreviatedFullName)
.map(FormattableType::of)
.map(FormattableType::getAbbreviatedFullName)
.orElseGet(() -> type.getName());
return typeName + "." + method.getName() + "(…)";

View File

@@ -36,7 +36,7 @@ import org.springframework.modulith.ApplicationModuleInitializer;
import org.springframework.modulith.core.ApplicationModule;
import org.springframework.modulith.core.ApplicationModules;
import org.springframework.modulith.core.ApplicationModulesFactory;
import org.springframework.modulith.core.FormatableType;
import org.springframework.modulith.core.FormattableType;
import org.springframework.modulith.runtime.ApplicationModulesRuntime;
import org.springframework.modulith.runtime.ApplicationRuntime;
import org.springframework.util.Assert;
@@ -123,7 +123,7 @@ class SpringModulithRuntimeAutoConfiguration {
public void initialize() {
var listenerType = AopUtils.getTargetClass(delegate);
var formattable = FormatableType.of(listenerType);
var formattable = FormattableType.of(listenerType);
var formattedListenerType = modules.getModuleByType(listenerType)
.map(formattable::getAbbreviatedFullName)