diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java index 6ffe741d9..71a340722 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java @@ -306,7 +306,7 @@ public ArchitecturallyEvidentType getArchitecturallyEvidentType(Class type) { 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 @@ Violations isValidDependencyWithin(ApplicationModules modules) { 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 @@ ApplicationModule getExistingModuleOf(JavaClass javaClass, ApplicationModules mo */ @Override public String toString() { - return type.format(FormatableType.of(source), FormatableType.of(target)); + return type.format(FormattableType.of(source), FormattableType.of(target)); } /* diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ArchitecturallyEvidentType.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ArchitecturallyEvidentType.java index d4f40f91b..abb645770 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ArchitecturallyEvidentType.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ArchitecturallyEvidentType.java @@ -99,7 +99,7 @@ public JavaClass getType() { * @return will never be {@literal null}. */ String getAbbreviatedFullName() { - return FormatableType.of(getType()).getAbbreviatedFullName(); + return FormattableType.of(getType()).getAbbreviatedFullName(); } /** diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/DependencyType.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/DependencyType.java index 3079036d1..a86e90c54 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/DependencyType.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/DependencyType.java @@ -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 String format(FormatableType source, FormatableType target) { 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 String format(FormatableType source, FormatableType target) { 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 DependencyType defaultOr(Supplier supplier) { 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 @@ static DependencyType forDependency(Dependency dependency) { 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. * diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/FormatableType.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/FormatableType.java index 8f6d8e951..a08db767f 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/FormatableType.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/FormatableType.java @@ -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 @@ * 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 CACHE = new ConcurrentHashMap<>(); - - private final String type; - private final Supplier 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 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 static FormatableType of(JavaClass type) { Assert.notNull(type, "JavaClass must not be null!"); - return CACHE.computeIfAbsent(type.getName(), FormatableType::new); + return FormattableType.of(type); } /** @@ -77,7 +49,7 @@ public static FormatableType of(JavaClass type) { * @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 static FormatableType of(Class type) { * @return will never be {@literal null}. */ public static String format(Iterable 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 @@ private FormatableType(String type) { * * @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 String getAbbreviatedFullName() { * @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(); } diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/FormattableType.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/FormattableType.java new file mode 100644 index 000000000..439faad30 --- /dev/null +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/FormattableType.java @@ -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 CACHE = new ConcurrentHashMap<>(); + + private final String type; + private final Supplier 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 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 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(".")); + } +} diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaAccessSource.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaAccessSource.java index 6b6eecdc6..fac643437 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaAccessSource.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaAccessSource.java @@ -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(); diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java index e83e15ea2..bcf3137a8 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java @@ -383,7 +383,7 @@ public Optional findAnnotation(Class annotationType 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)); diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/NamedInterface.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/NamedInterface.java index bcdfa7673..8e7c44d21 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/NamedInterface.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/NamedInterface.java @@ -117,7 +117,7 @@ static NamedInterface unnamed(JavaPackage javaPackage, boolean flatten) { // 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 @@ NamedInterface merge(NamedInterface other) { public String toString() { return "NamedInterface: name=%s, types=[%s]" // - .formatted(name, classes.isEmpty() ? "" : " " + FormatableType.format(classes) + " "); + .formatted(name, classes.isEmpty() ? "" : " " + FormattableType.format(classes) + " "); } /** diff --git a/spring-modulith-docs/src/main/java/org/springframework/modulith/docs/Asciidoctor.java b/spring-modulith-docs/src/main/java/org/springframework/modulith/docs/Asciidoctor.java index 7a1e5c1ed..1cb16dce8 100644 --- a/spring-modulith-docs/src/main/java/org/springframework/modulith/docs/Asciidoctor.java +++ b/spring-modulith-docs/src/main/java/org/springframework/modulith/docs/Asciidoctor.java @@ -31,7 +31,7 @@ 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 @@ private String toOptionalLink(JavaClass source, Optional methodSignature 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) diff --git a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java index 81987c88b..776a9b5e5 100644 --- a/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java +++ b/spring-modulith-observability/src/main/java/org/springframework/modulith/observability/DefaultObservedModule.java @@ -23,7 +23,7 @@ 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 @@ private static String toString(Method method, ApplicationModule module) { 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() + "(…)"; diff --git a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java index fb4aa622e..7b0a9c92a 100644 --- a/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java +++ b/spring-modulith-runtime/src/main/java/org/springframework/modulith/runtime/autoconfigure/SpringModulithRuntimeAutoConfiguration.java @@ -36,7 +36,7 @@ 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 @@ public LoggingApplicationModuleInitializerAdapter(ApplicationModuleInitializer d 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)