Skip to content

Commit 1cdbd68

Browse files
committed
Revise FilePatternResourceHintsRegistrar API and improve documentation
This commit revises the FilePatternResourceHintsRegistrar API and introduces List<String> overrides of various var-args methods used with the new builder API. Closes gh-29161
1 parent 453c0e5 commit 1cdbd68

File tree

2 files changed

+137
-73
lines changed

2 files changed

+137
-73
lines changed

spring-core/src/main/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrar.java

Lines changed: 135 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -27,60 +27,52 @@
2727

2828
/**
2929
* Register the necessary resource hints for loading files from the classpath,
30-
* based on a file name prefix and an extension with convenience to support
30+
* based on file name prefixes and file extensions with convenience to support
3131
* multiple classpath locations.
3232
*
3333
* <p>Only registers hints for matching classpath locations, which allows for
3434
* several locations to be provided without contributing unnecessary hints.
3535
*
3636
* @author Stephane Nicoll
37+
* @author Sam Brannen
3738
* @since 6.0
3839
*/
3940
public class FilePatternResourceHintsRegistrar {
4041

41-
private final List<String> names;
42+
private final List<String> classpathLocations;
4243

43-
private final List<String> locations;
44+
private final List<String> filePrefixes;
45+
46+
private final List<String> fileExtensions;
4447

45-
private final List<String> extensions;
4648

4749
/**
48-
* Create a new instance for the specified file names, locations, and file
49-
* extensions.
50-
* @param names the file names
51-
* @param locations the classpath locations
52-
* @param extensions the file extensions (starting with a dot)
50+
* Create a new instance for the specified file prefixes, classpath locations,
51+
* and file extensions.
52+
* @param filePrefixes the file prefixes
53+
* @param classpathLocations the classpath locations
54+
* @param fileExtensions the file extensions (starting with a dot)
5355
* @deprecated as of 6.0.12 in favor of {@linkplain #forClassPathLocations(String...) the builder}
5456
*/
5557
@Deprecated(since = "6.0.12", forRemoval = true)
56-
public FilePatternResourceHintsRegistrar(List<String> names, List<String> locations,
57-
List<String> extensions) {
58-
this.names = Builder.validateFilePrefixes(names.toArray(String[]::new));
59-
this.locations = Builder.validateClasspathLocations(locations.toArray(String[]::new));
60-
this.extensions = Builder.validateFileExtensions(extensions.toArray(String[]::new));
61-
}
58+
public FilePatternResourceHintsRegistrar(List<String> filePrefixes, List<String> classpathLocations,
59+
List<String> fileExtensions) {
6260

63-
/**
64-
* Configure the registrar with the specified
65-
* {@linkplain Builder#withClasspathLocations(String...) classpath locations}.
66-
* @param locations the classpath locations
67-
* @return a {@link Builder} to further configure the registrar
68-
* @since 6.0.12
69-
*/
70-
public static Builder forClassPathLocations(String... locations) {
71-
Assert.notEmpty(locations, "At least one classpath location should be specified");
72-
return new Builder().withClasspathLocations(locations);
61+
this.classpathLocations = validateClasspathLocations(classpathLocations);
62+
this.filePrefixes = validateFilePrefixes(filePrefixes);
63+
this.fileExtensions = validateFileExtensions(fileExtensions);
7364
}
7465

66+
7567
@Deprecated(since = "6.0.12", forRemoval = true)
7668
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
7769
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
7870
List<String> includes = new ArrayList<>();
79-
for (String location : this.locations) {
71+
for (String location : this.classpathLocations) {
8072
if (classLoaderToUse.getResource(location) != null) {
81-
for (String extension : this.extensions) {
82-
for (String name : this.names) {
83-
includes.add(location + name + "*" + extension);
73+
for (String filePrefix : this.filePrefixes) {
74+
for (String fileExtension : this.fileExtensions) {
75+
includes.add(location + filePrefix + "*" + fileExtension);
8476
}
8577
}
8678
}
@@ -90,6 +82,68 @@ public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader
9082
}
9183
}
9284

85+
86+
/**
87+
* Configure the registrar with the specified
88+
* {@linkplain Builder#withClasspathLocations(String...) classpath locations}.
89+
* @param classpathLocations the classpath locations
90+
* @return a {@link Builder} to further configure the registrar
91+
* @since 6.0.12
92+
* @see #forClassPathLocations(List)
93+
*/
94+
public static Builder forClassPathLocations(String... classpathLocations) {
95+
return forClassPathLocations(Arrays.asList(classpathLocations));
96+
}
97+
98+
/**
99+
* Configure the registrar with the specified
100+
* {@linkplain Builder#withClasspathLocations(List) classpath locations}.
101+
* @param classpathLocations the classpath locations
102+
* @return a {@link Builder} to further configure the registrar
103+
* @since 6.0.12
104+
* @see #forClassPathLocations(String...)
105+
*/
106+
public static Builder forClassPathLocations(List<String> classpathLocations) {
107+
return new Builder().withClasspathLocations(classpathLocations);
108+
}
109+
110+
private static List<String> validateClasspathLocations(List<String> classpathLocations) {
111+
Assert.notEmpty(classpathLocations, "At least one classpath location must be specified");
112+
List<String> parsedLocations = new ArrayList<>();
113+
for (String location : classpathLocations) {
114+
if (location.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
115+
location = location.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
116+
}
117+
if (location.startsWith("/")) {
118+
location = location.substring(1);
119+
}
120+
if (!location.isEmpty() && !location.endsWith("/")) {
121+
location = location + "/";
122+
}
123+
parsedLocations.add(location);
124+
}
125+
return parsedLocations;
126+
}
127+
128+
private static List<String> validateFilePrefixes(List<String> filePrefixes) {
129+
for (String filePrefix : filePrefixes) {
130+
if (filePrefix.contains("*")) {
131+
throw new IllegalArgumentException("File prefix '" + filePrefix + "' cannot contain '*'");
132+
}
133+
}
134+
return filePrefixes;
135+
}
136+
137+
private static List<String> validateFileExtensions(List<String> fileExtensions) {
138+
for (String fileExtension : fileExtensions) {
139+
if (!fileExtension.startsWith(".")) {
140+
throw new IllegalArgumentException("Extension '" + fileExtension + "' must start with '.'");
141+
}
142+
}
143+
return fileExtensions;
144+
}
145+
146+
93147
/**
94148
* Builder for {@link FilePatternResourceHintsRegistrar}.
95149
* @since 6.0.12
@@ -103,15 +157,34 @@ public static final class Builder {
103157
private final List<String> fileExtensions = new ArrayList<>();
104158

105159

160+
private Builder() {
161+
// no-op
162+
}
163+
164+
106165
/**
107-
* Consider the specified classpath locations. A location can either be
108-
* a special "classpath" pseudo location or a standard location, such as
109-
* {@code com/example/resources}. An empty String represents the root of
110-
* the classpath.
166+
* Consider the specified classpath locations.
167+
* <p>A location can either be a special {@value ResourceUtils#CLASSPATH_URL_PREFIX}
168+
* pseudo location or a standard location, such as {@code com/example/resources}.
169+
* An empty String represents the root of the classpath.
111170
* @param classpathLocations the classpath locations to consider
112171
* @return this builder
172+
* @see #withClasspathLocations(List)
113173
*/
114174
public Builder withClasspathLocations(String... classpathLocations) {
175+
return withClasspathLocations(Arrays.asList(classpathLocations));
176+
}
177+
178+
/**
179+
* Consider the specified classpath locations.
180+
* <p>A location can either be a special {@value ResourceUtils#CLASSPATH_URL_PREFIX}
181+
* pseudo location or a standard location, such as {@code com/example/resources}.
182+
* An empty String represents the root of the classpath.
183+
* @param classpathLocations the classpath locations to consider
184+
* @return this builder
185+
* @see #withClasspathLocations(String...)
186+
*/
187+
public Builder withClasspathLocations(List<String> classpathLocations) {
115188
this.classpathLocations.addAll(validateClasspathLocations(classpathLocations));
116189
return this;
117190
}
@@ -122,8 +195,21 @@ public Builder withClasspathLocations(String... classpathLocations) {
122195
* character.
123196
* @param filePrefixes the file prefixes to consider
124197
* @return this builder
198+
* @see #withFilePrefixes(List)
125199
*/
126200
public Builder withFilePrefixes(String... filePrefixes) {
201+
return withFilePrefixes(Arrays.asList(filePrefixes));
202+
}
203+
204+
/**
205+
* Consider the specified file prefixes. Any file whose name starts with one
206+
* of the specified prefixes is considered. A prefix cannot contain the {@code *}
207+
* character.
208+
* @param filePrefixes the file prefixes to consider
209+
* @return this builder
210+
* @see #withFilePrefixes(String...)
211+
*/
212+
public Builder withFilePrefixes(List<String> filePrefixes) {
127213
this.filePrefixes.addAll(validateFilePrefixes(filePrefixes));
128214
return this;
129215
}
@@ -133,14 +219,26 @@ public Builder withFilePrefixes(String... filePrefixes) {
133219
* {@code .} character.
134220
* @param fileExtensions the file extensions to consider
135221
* @return this builder
222+
* @see #withFileExtensions(List)
136223
*/
137224
public Builder withFileExtensions(String... fileExtensions) {
225+
return withFileExtensions(Arrays.asList(fileExtensions));
226+
}
227+
228+
/**
229+
* Consider the specified file extensions. A file extension must start with a
230+
* {@code .} character.
231+
* @param fileExtensions the file extensions to consider
232+
* @return this builder
233+
* @see #withFileExtensions(String...)
234+
*/
235+
public Builder withFileExtensions(List<String> fileExtensions) {
138236
this.fileExtensions.addAll(validateFileExtensions(fileExtensions));
139237
return this;
140238
}
141239

142-
FilePatternResourceHintsRegistrar build() {
143-
Assert.notEmpty(this.classpathLocations, "At least one location should be specified");
240+
241+
private FilePatternResourceHintsRegistrar build() {
144242
return new FilePatternResourceHintsRegistrar(this.filePrefixes,
145243
this.classpathLocations, this.fileExtensions);
146244
}
@@ -150,47 +248,13 @@ FilePatternResourceHintsRegistrar build() {
150248
* classpath location that resolves against the {@code ClassLoader}, files
151249
* with the configured file prefixes and extensions are registered.
152250
* @param hints the hints contributed so far for the deployment unit
153-
* @param classLoader the classloader, or {@code null} if even the system ClassLoader isn't accessible
251+
* @param classLoader the classloader, or {@code null} if even the system
252+
* ClassLoader isn't accessible
154253
*/
155254
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
156255
build().registerHints(hints, classLoader);
157256
}
158257

159-
private static List<String> validateClasspathLocations(String... locations) {
160-
List<String> parsedLocations = new ArrayList<>();
161-
for (String location : locations) {
162-
if (location.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
163-
location = location.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
164-
}
165-
if (location.startsWith("/")) {
166-
location = location.substring(1);
167-
}
168-
if (!location.isEmpty() && !location.endsWith("/")) {
169-
location = location + "/";
170-
}
171-
parsedLocations.add(location);
172-
}
173-
return parsedLocations;
174-
}
175-
176-
private static List<String> validateFilePrefixes(String... filePrefixes) {
177-
for (String filePrefix : filePrefixes) {
178-
if (filePrefix.contains("*")) {
179-
throw new IllegalArgumentException("File prefix '" + filePrefix + "' cannot contain '*'");
180-
}
181-
}
182-
return Arrays.asList(filePrefixes);
183-
}
184-
185-
private static List<String> validateFileExtensions(String... fileExtensions) {
186-
for (String fileExtension : fileExtensions) {
187-
if (!fileExtension.startsWith(".")) {
188-
throw new IllegalArgumentException("Extension '" + fileExtension + "' should start with '.'");
189-
}
190-
}
191-
return Arrays.asList(fileExtensions);
192-
}
193-
194258
}
195259

196260
}

spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class FilePatternResourceHintsRegistrarTests {
4040
@Test
4141
void configureWithNoClasspathLocation() {
4242
assertThatIllegalArgumentException().isThrownBy(FilePatternResourceHintsRegistrar::forClassPathLocations)
43-
.withMessageContaining("At least one classpath location should be specified");
43+
.withMessageContaining("At least one classpath location must be specified");
4444
}
4545

4646
@Test
@@ -54,7 +54,7 @@ void configureWithInvalidFilePrefix() {
5454
void configureWithInvalidFileExtension() {
5555
Builder builder = FilePatternResourceHintsRegistrar.forClassPathLocations("");
5656
assertThatIllegalArgumentException().isThrownBy(() -> builder.withFileExtensions("txt"))
57-
.withMessageContaining("should start with '.'");
57+
.withMessageContaining("must start with '.'");
5858
}
5959

6060
@Test

0 commit comments

Comments
 (0)