Skip to content

Commit 17ff3f1

Browse files
committed
Use exclamation character for the document separator prefix
Closes gh-32185
1 parent 1e886bd commit 17ff3f1

File tree

4 files changed

+77
-32
lines changed

4 files changed

+77
-32
lines changed

spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,7 @@ For example, the following file has two logical documents:
497497
on-cloud-platform: "kubernetes"
498498
----
499499

500-
For `application.properties` files a special `#---` comment is used to mark the document splits:
500+
For `application.properties` files a special `#---` or `!---` comment is used to mark the document splits:
501501

502502
[source,properties,indent=0,subs="verbatim"]
503503
----
@@ -508,7 +508,7 @@ For `application.properties` files a special `#---` comment is used to mark the
508508
----
509509

510510
NOTE: Property file separators must not have any leading whitespace and must have exactly three hyphen characters.
511-
The lines immediately before and after the separator must not be comments.
511+
The lines immediately before and after the separator must not be same comment prefix.
512512

513513
TIP: Multi-document property files are often used in conjunction with activation properties such as `spring.config.activate.on-profile`.
514514
See the <<features#features.external-config.files.activation-properties, next section>> for details.

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

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -78,7 +78,8 @@ List<Document> load(boolean expandLists) throws IOException {
7878
StringBuilder buffer = new StringBuilder();
7979
try (CharacterReader reader = new CharacterReader(this.resource)) {
8080
while (reader.read()) {
81-
if (reader.isPoundCharacter()) {
81+
if (reader.isCommentPrefixCharacter()) {
82+
char commentPrefixCharacter = reader.getCharacter();
8283
if (isNewDocument(reader)) {
8384
if (!document.isEmpty()) {
8485
documents.add(document);
@@ -89,12 +90,12 @@ List<Document> load(boolean expandLists) throws IOException {
8990
if (document.isEmpty() && !documents.isEmpty()) {
9091
document = documents.remove(documents.size() - 1);
9192
}
92-
reader.setLastLineComment(true);
93+
reader.setLastLineCommentPrefix(commentPrefixCharacter);
9394
reader.skipComment();
9495
}
9596
}
9697
else {
97-
reader.setLastLineComment(false);
98+
reader.setLastLineCommentPrefix(-1);
9899
loadKeyAndValue(expandLists, document, reader, buffer);
99100
}
100101
}
@@ -161,10 +162,10 @@ private OriginTrackedValue loadValue(StringBuilder buffer, CharacterReader reade
161162
}
162163

163164
private boolean isNewDocument(CharacterReader reader) throws IOException {
164-
if (reader.isLastLineComment()) {
165+
if (reader.isSameLastLineCommentPrefix()) {
165166
return false;
166167
}
167-
boolean result = reader.getLocation().getColumn() == 0 && reader.isPoundCharacter();
168+
boolean result = reader.getLocation().getColumn() == 0;
168169
result = result && readAndExpect(reader, reader::isHyphenCharacter);
169170
result = result && readAndExpect(reader, reader::isHyphenCharacter);
170171
result = result && readAndExpect(reader, reader::isHyphenCharacter);
@@ -196,7 +197,7 @@ private static class CharacterReader implements Closeable {
196197

197198
private int character;
198199

199-
private boolean lastLineComment;
200+
private int lastLineCommentPrefix;
200201

201202
CharacterReader(Resource resource) throws IOException {
202203
this.reader = new LineNumberReader(
@@ -209,20 +210,11 @@ public void close() throws IOException {
209210
}
210211

211212
boolean read() throws IOException {
212-
return read(false);
213-
}
214-
215-
boolean read(boolean wrappedLine) throws IOException {
216213
this.escaped = false;
217214
this.character = this.reader.read();
218215
this.columnNumber++;
219216
if (this.columnNumber == 0) {
220217
skipWhitespace();
221-
if (!wrappedLine) {
222-
if (this.character == '!') {
223-
skipComment();
224-
}
225-
}
226218
}
227219
if (this.character == '\\') {
228220
this.escaped = true;
@@ -241,12 +233,8 @@ private void skipWhitespace() throws IOException {
241233
}
242234
}
243235

244-
private void setLastLineComment(boolean lastLineComment) {
245-
this.lastLineComment = lastLineComment;
246-
}
247-
248-
private boolean isLastLineComment() {
249-
return this.lastLineComment;
236+
private void setLastLineCommentPrefix(int lastLineCommentPrefix) {
237+
this.lastLineCommentPrefix = lastLineCommentPrefix;
250238
}
251239

252240
private void skipComment() throws IOException {
@@ -264,7 +252,7 @@ private void readEscaped() throws IOException {
264252
}
265253
else if (this.character == '\n') {
266254
this.columnNumber = -1;
267-
read(true);
255+
read();
268256
}
269257
else if (this.character == 'u') {
270258
readUnicode();
@@ -318,8 +306,12 @@ Location getLocation() {
318306
return new Location(this.reader.getLineNumber(), this.columnNumber);
319307
}
320308

321-
boolean isPoundCharacter() {
322-
return this.character == '#';
309+
boolean isSameLastLineCommentPrefix() {
310+
return this.lastLineCommentPrefix == this.character;
311+
}
312+
313+
boolean isCommentPrefixCharacter() {
314+
return this.character == '#' || this.character == '!';
323315
}
324316

325317
boolean isHyphenCharacter() {

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

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2021 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -183,33 +183,70 @@ void getImmediateMultiline() {
183183
}
184184

185185
@Test
186-
void loadWhenMultiDocumentWithoutWhitespaceLoadsMultiDoc() throws IOException {
186+
void loadWhenMultiDocumentWithPoundPrefixAndWithoutWhitespaceLoadsMultiDoc() throws IOException {
187187
String content = "a=a\n#---\nb=b";
188188
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
189189
assertThat(loaded).hasSize(2);
190190
}
191191

192192
@Test
193-
void loadWhenMultiDocumentWithLeadingWhitespaceLoadsSingleDoc() throws IOException {
193+
void loadWhenMultiDocumentWithExclamationPrefixAndWithoutWhitespaceLoadsMultiDoc() throws IOException {
194+
String content = "a=a\n!---\nb=b";
195+
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
196+
assertThat(loaded).hasSize(2);
197+
}
198+
199+
@Test
200+
void loadWhenMultiDocumentWithPoundPrefixAndLeadingWhitespaceLoadsSingleDoc() throws IOException {
194201
String content = "a=a\n \t#---\nb=b";
195202
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
196203
assertThat(loaded).hasSize(1);
197204
}
198205

199206
@Test
200-
void loadWhenMultiDocumentWithTrailingWhitespaceLoadsMultiDoc() throws IOException {
207+
void loadWhenMultiDocumentWithExclamationPrefixAndLeadingWhitespaceLoadsSingleDoc() throws IOException {
208+
String content = "a=a\n \t!---\nb=b";
209+
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
210+
assertThat(loaded).hasSize(1);
211+
}
212+
213+
@Test
214+
void loadWhenMultiDocumentWithPoundPrefixAndTrailingWhitespaceLoadsMultiDoc() throws IOException {
201215
String content = "a=a\n#--- \t \nb=b";
202216
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
203217
assertThat(loaded).hasSize(2);
204218
}
205219

206220
@Test
207-
void loadWhenMultiDocumentWithTrailingCharsLoadsSingleDoc() throws IOException {
221+
void loadWhenMultiDocumentWithExclamationPrefixAndTrailingWhitespaceLoadsMultiDoc() throws IOException {
222+
String content = "a=a\n!--- \t \nb=b";
223+
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
224+
assertThat(loaded).hasSize(2);
225+
}
226+
227+
@Test
228+
void loadWhenMultiDocumentWithPoundPrefixAndTrailingCharsLoadsSingleDoc() throws IOException {
208229
String content = "a=a\n#--- \tcomment\nb=b";
209230
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
210231
assertThat(loaded).hasSize(1);
211232
}
212233

234+
@Test
235+
void loadWhenMultiDocumentWithExclamationPrefixAndTrailingCharsLoadsSingleDoc() throws IOException {
236+
String content = "a=a\n!--- \tcomment\nb=b";
237+
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
238+
assertThat(loaded).hasSize(1);
239+
}
240+
241+
@Test
242+
void loadWhenMultiDocumentSeparatorPrefixDifferentFromCommentPrefixLoadsMultiDoc() throws IOException {
243+
String[] contents = new String[] { "a=a\n# comment\n!---\nb=b", "a=a\n! comment\n#---\nb=b" };
244+
for (String content : contents) {
245+
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
246+
assertThat(loaded).hasSize(2);
247+
}
248+
}
249+
213250
@Test
214251
void getPropertyWithWhitespaceAfterKey() {
215252
OriginTrackedValue value = getFromFirst("bar");

spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,19 @@ boot=bar
1414
#---
1515

1616
bar=ok
17+
18+
!---
19+
! Test
20+
!---
21+
22+
ok=well
23+
24+
!---
25+
! Test
26+
27+
well=hello
28+
29+
! Test
30+
!---
31+
32+
hello=world

0 commit comments

Comments
 (0)