Use exclamation character for the document separator prefix

See gh-32521
This commit is contained in:
Guirong Hu 2022-09-27 19:02:36 +08:00 committed by Phillip Webb
parent eaf854bd9c
commit 33e379c4a2
4 changed files with 77 additions and 32 deletions

View File

@ -498,7 +498,7 @@ For example, the following file has two logical documents:
on-cloud-platform: "kubernetes"
----
For `application.properties` files a special `#---` comment is used to mark the document splits:
For `application.properties` files a special `#---` or `!---` comment is used to mark the document splits:
[source,properties,indent=0,subs="verbatim"]
----
@ -509,7 +509,7 @@ For `application.properties` files a special `#---` comment is used to mark the
----
NOTE: Property file separators must not have any leading whitespace and must have exactly three hyphen characters.
The lines immediately before and after the separator must not be comments.
The lines immediately before and after the separator must not be same comment prefix.
TIP: Multi-document property files are often used in conjunction with activation properties such as `spring.config.activate.on-profile`.
See the <<features#features.external-config.files.activation-properties, next section>> for details.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2022 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.
@ -78,7 +78,8 @@ class OriginTrackedPropertiesLoader {
StringBuilder buffer = new StringBuilder();
try (CharacterReader reader = new CharacterReader(this.resource)) {
while (reader.read()) {
if (reader.isPoundCharacter()) {
if (reader.isCommentPrefixCharacter()) {
char commentPrefixCharacter = reader.getCharacter();
if (isNewDocument(reader)) {
if (!document.isEmpty()) {
documents.add(document);
@ -89,12 +90,12 @@ class OriginTrackedPropertiesLoader {
if (document.isEmpty() && !documents.isEmpty()) {
document = documents.remove(documents.size() - 1);
}
reader.setLastLineComment(true);
reader.setLastLineCommentPrefix(commentPrefixCharacter);
reader.skipComment();
}
}
else {
reader.setLastLineComment(false);
reader.setLastLineCommentPrefix(-1);
loadKeyAndValue(expandLists, document, reader, buffer);
}
}
@ -161,10 +162,10 @@ class OriginTrackedPropertiesLoader {
}
private boolean isNewDocument(CharacterReader reader) throws IOException {
if (reader.isLastLineComment()) {
if (reader.isSameLastLineCommentPrefix()) {
return false;
}
boolean result = reader.getLocation().getColumn() == 0 && reader.isPoundCharacter();
boolean result = reader.getLocation().getColumn() == 0;
result = result && readAndExpect(reader, reader::isHyphenCharacter);
result = result && readAndExpect(reader, reader::isHyphenCharacter);
result = result && readAndExpect(reader, reader::isHyphenCharacter);
@ -196,7 +197,7 @@ class OriginTrackedPropertiesLoader {
private int character;
private boolean lastLineComment;
private int lastLineCommentPrefix;
CharacterReader(Resource resource) throws IOException {
this.reader = new LineNumberReader(
@ -209,20 +210,11 @@ class OriginTrackedPropertiesLoader {
}
boolean read() throws IOException {
return read(false);
}
boolean read(boolean wrappedLine) throws IOException {
this.escaped = false;
this.character = this.reader.read();
this.columnNumber++;
if (this.columnNumber == 0) {
skipWhitespace();
if (!wrappedLine) {
if (this.character == '!') {
skipComment();
}
}
}
if (this.character == '\\') {
this.escaped = true;
@ -241,12 +233,8 @@ class OriginTrackedPropertiesLoader {
}
}
private void setLastLineComment(boolean lastLineComment) {
this.lastLineComment = lastLineComment;
}
private boolean isLastLineComment() {
return this.lastLineComment;
private void setLastLineCommentPrefix(int lastLineCommentPrefix) {
this.lastLineCommentPrefix = lastLineCommentPrefix;
}
private void skipComment() throws IOException {
@ -264,7 +252,7 @@ class OriginTrackedPropertiesLoader {
}
else if (this.character == '\n') {
this.columnNumber = -1;
read(true);
read();
}
else if (this.character == 'u') {
readUnicode();
@ -318,8 +306,12 @@ class OriginTrackedPropertiesLoader {
return new Location(this.reader.getLineNumber(), this.columnNumber);
}
boolean isPoundCharacter() {
return this.character == '#';
boolean isSameLastLineCommentPrefix() {
return this.lastLineCommentPrefix == this.character;
}
boolean isCommentPrefixCharacter() {
return this.character == '#' || this.character == '!';
}
boolean isHyphenCharacter() {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@ -183,33 +183,70 @@ class OriginTrackedPropertiesLoaderTests {
}
@Test
void loadWhenMultiDocumentWithoutWhitespaceLoadsMultiDoc() throws IOException {
void loadWhenMultiDocumentWithPoundPrefixAndWithoutWhitespaceLoadsMultiDoc() throws IOException {
String content = "a=a\n#---\nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(2);
}
@Test
void loadWhenMultiDocumentWithLeadingWhitespaceLoadsSingleDoc() throws IOException {
void loadWhenMultiDocumentWithExclamationPrefixAndWithoutWhitespaceLoadsMultiDoc() throws IOException {
String content = "a=a\n!---\nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(2);
}
@Test
void loadWhenMultiDocumentWithPoundPrefixAndLeadingWhitespaceLoadsSingleDoc() throws IOException {
String content = "a=a\n \t#---\nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(1);
}
@Test
void loadWhenMultiDocumentWithTrailingWhitespaceLoadsMultiDoc() throws IOException {
void loadWhenMultiDocumentWithExclamationPrefixAndLeadingWhitespaceLoadsSingleDoc() throws IOException {
String content = "a=a\n \t!---\nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(1);
}
@Test
void loadWhenMultiDocumentWithPoundPrefixAndTrailingWhitespaceLoadsMultiDoc() throws IOException {
String content = "a=a\n#--- \t \nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(2);
}
@Test
void loadWhenMultiDocumentWithTrailingCharsLoadsSingleDoc() throws IOException {
void loadWhenMultiDocumentWithExclamationPrefixAndTrailingWhitespaceLoadsMultiDoc() throws IOException {
String content = "a=a\n!--- \t \nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(2);
}
@Test
void loadWhenMultiDocumentWithPoundPrefixAndTrailingCharsLoadsSingleDoc() throws IOException {
String content = "a=a\n#--- \tcomment\nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(1);
}
@Test
void loadWhenMultiDocumentWithExclamationPrefixAndTrailingCharsLoadsSingleDoc() throws IOException {
String content = "a=a\n!--- \tcomment\nb=b";
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(1);
}
@Test
void loadWhenMultiDocumentSeparatorPrefixDifferentFromCommentPrefixLoadsMultiDoc() throws IOException {
String[] contents = new String[] { "a=a\n# comment\n!---\nb=b", "a=a\n! comment\n#---\nb=b" };
for (String content : contents) {
List<Document> loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load();
assertThat(loaded).hasSize(2);
}
}
@Test
void getPropertyWithWhitespaceAfterKey() {
OriginTrackedValue value = getFromFirst("bar");

View File

@ -14,3 +14,19 @@ boot=bar
#---
bar=ok
!---
! Test
!---
ok=well
!---
! Test
well=hello
! Test
!---
hello=world