Skip to content

Commit 2e2b371

Browse files
committed
Add auto-trimming support to configtree sources
Update `ConfigTreePropertySource` with an option to automatically trim trailing new-line characters. Closes gh-23826
1 parent cf673ce commit 2e2b371

File tree

4 files changed

+70
-10
lines changed

4 files changed

+70
-10
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Collections;
2222

2323
import org.springframework.boot.env.ConfigTreePropertySource;
24+
import org.springframework.boot.env.ConfigTreePropertySource.Option;
2425

2526
/**
2627
* {@link ConfigDataLoader} for config tree locations.
@@ -37,7 +38,7 @@ public ConfigData load(ConfigDataLoaderContext context, ConfigTreeConfigDataReso
3738
Path path = resource.getPath();
3839
ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, path);
3940
String name = "Config tree '" + path + "'";
40-
ConfigTreePropertySource source = new ConfigTreePropertySource(name, path);
41+
ConfigTreePropertySource source = new ConfigTreePropertySource(name, path, Option.AUTO_TRIM_TRAILING_NEW_LINE);
4142
return new ConfigData(Collections.singletonList(source));
4243
}
4344

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/ConfigTreePropertySource.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,12 @@ public enum Option {
145145
/**
146146
* Convert file and directory names to lowercase.
147147
*/
148-
USE_LOWERCASE_NAMES
148+
USE_LOWERCASE_NAMES,
149+
150+
/**
151+
* Automatically attempt trim trailing new-line characters.
152+
*/
153+
AUTO_TRIM_TRAILING_NEW_LINE
149154

150155
}
151156

@@ -173,17 +178,22 @@ private static final class PropertyFile {
173178

174179
private final PropertyFileContent cachedContent;
175180

181+
private final boolean autoTrimTrailingNewLine;
182+
176183
private PropertyFile(Path path, Set<Option> options) {
177184
this.path = path;
178185
this.resource = new PathResource(path);
179186
this.origin = new TextResourceOrigin(this.resource, START_OF_FILE);
187+
this.autoTrimTrailingNewLine = options.contains(Option.AUTO_TRIM_TRAILING_NEW_LINE);
180188
this.cachedContent = options.contains(Option.ALWAYS_READ) ? null
181-
: new PropertyFileContent(path, this.resource, this.origin, true);
189+
: new PropertyFileContent(path, this.resource, this.origin, true, this.autoTrimTrailingNewLine);
182190
}
183191

184192
PropertyFileContent getContent() {
185-
return (this.cachedContent != null) ? this.cachedContent
186-
: new PropertyFileContent(this.path, this.resource, this.origin, false);
193+
if (this.cachedContent != null) {
194+
return this.cachedContent;
195+
}
196+
return new PropertyFileContent(this.path, this.resource, this.origin, false, this.autoTrimTrailingNewLine);
187197
}
188198

189199
Origin getOrigin() {
@@ -247,17 +257,21 @@ private static final class PropertyFileContent implements Value, OriginProvider
247257

248258
private final Resource resource;
249259

260+
private final Origin origin;
261+
250262
private final boolean cacheContent;
251263

252-
private volatile byte[] content;
264+
private final boolean autoTrimTrailingNewLine;
253265

254-
private final Origin origin;
266+
private volatile byte[] content;
255267

256-
private PropertyFileContent(Path path, Resource resource, Origin origin, boolean cacheContent) {
268+
private PropertyFileContent(Path path, Resource resource, Origin origin, boolean cacheContent,
269+
boolean autoTrimTrailingNewLine) {
257270
this.path = path;
258271
this.resource = resource;
259272
this.origin = origin;
260273
this.cacheContent = cacheContent;
274+
this.autoTrimTrailingNewLine = autoTrimTrailingNewLine;
261275
}
262276

263277
@Override
@@ -282,7 +296,28 @@ public CharSequence subSequence(int start, int end) {
282296

283297
@Override
284298
public String toString() {
285-
return new String(getBytes());
299+
String string = new String(getBytes());
300+
if (this.autoTrimTrailingNewLine) {
301+
string = autoTrimTrailingNewLine(string);
302+
}
303+
return string;
304+
}
305+
306+
private String autoTrimTrailingNewLine(String string) {
307+
if (!string.endsWith("\n")) {
308+
return string;
309+
}
310+
int numberOfLines = 0;
311+
for (char ch : string.toCharArray()) {
312+
if (ch == '\n') {
313+
numberOfLines++;
314+
}
315+
}
316+
if (numberOfLines > 1) {
317+
return string;
318+
}
319+
return (string.endsWith("\r\n")) ? string.substring(0, string.length() - 2)
320+
: string.substring(0, string.length() - 1);
286321
}
287322

288323
@Override

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoaderTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public class ConfigTreeConfigDataLoaderTests {
5050
void loadReturnsConfigDataWithPropertySource() throws IOException {
5151
File file = this.directory.resolve("hello").toFile();
5252
file.getParentFile().mkdirs();
53-
FileCopyUtils.copy("world".getBytes(StandardCharsets.UTF_8), file);
53+
FileCopyUtils.copy("world\n".getBytes(StandardCharsets.UTF_8), file);
5454
ConfigTreeConfigDataResource location = new ConfigTreeConfigDataResource(this.directory.toString());
5555
ConfigData configData = this.loader.load(this.loaderContext, location);
5656
assertThat(configData.getPropertySources().size()).isEqualTo(1);

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/ConfigTreePropertySourceTests.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,30 @@ void getPropertyWhenLowercaseReturnsValue() throws Exception {
199199
assertThat(propertySource.getProperty("spring")).hasToString("boot");
200200
}
201201

202+
@Test
203+
void getPropertyAsStringWhenMultiLinePropertyReturnsNonTrimmed() throws Exception {
204+
addProperty("a", "a\nb\n");
205+
ConfigTreePropertySource propertySource = new ConfigTreePropertySource("test", this.directory,
206+
Option.AUTO_TRIM_TRAILING_NEW_LINE);
207+
assertThat(propertySource.getProperty("a").toString()).isEqualTo("a\nb\n");
208+
}
209+
210+
@Test
211+
void getPropertyAsStringWhenPropertyEndsWithNewLineReturnsTrimmed() throws Exception {
212+
addProperty("a", "a\n");
213+
ConfigTreePropertySource propertySource = new ConfigTreePropertySource("test", this.directory,
214+
Option.AUTO_TRIM_TRAILING_NEW_LINE);
215+
assertThat(propertySource.getProperty("a").toString()).isEqualTo("a");
216+
}
217+
218+
@Test
219+
void getPropertyAsStringWhenPropertyEndsWithWindowsNewLineReturnsTrimmed() throws Exception {
220+
addProperty("a", "a\r\n");
221+
ConfigTreePropertySource propertySource = new ConfigTreePropertySource("test", this.directory,
222+
Option.AUTO_TRIM_TRAILING_NEW_LINE);
223+
assertThat(propertySource.getProperty("a").toString()).isEqualTo("a");
224+
}
225+
202226
private ConfigTreePropertySource getFlatPropertySource() throws IOException {
203227
addProperty("a", "A");
204228
addProperty("b", "B");

0 commit comments

Comments
 (0)