Use stricter document properties separator logic
Update `OriginTrackedPropertiesLoader` with stricter logic around the document separator. If the preceding or following lines are comments then the separator will be ignored. Closes gh-22963
This commit is contained in:
@@ -886,7 +886,8 @@ For `application.properties` files a special `#---` comment is used to mark the
|
||||
spring.application.name=MyCloudApp
|
||||
----
|
||||
|
||||
NOTE: Property file separators must not have any leading or trailing whitespace and must have exactly three hyphen characters.
|
||||
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.
|
||||
|
||||
TIP: Multi-document property files are often used in conjunction with activation properties such as `spring.config.activate.on-profile`.
|
||||
See the <<boot-features-external-config-file-activation-properties, next section>> for details.
|
||||
|
||||
@@ -73,46 +73,58 @@ class OriginTrackedPropertiesLoader {
|
||||
* @throws IOException on read error
|
||||
*/
|
||||
List<Document> load(boolean expandLists) throws IOException {
|
||||
List<Document> result = new ArrayList<>();
|
||||
List<Document> documents = new ArrayList<>();
|
||||
Document document = new Document();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
try (CharacterReader reader = new CharacterReader(this.resource)) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
while (reader.read()) {
|
||||
if (reader.isPoundCharacter()) {
|
||||
if (isNewDocument(reader)) {
|
||||
if (!document.isEmpty()) {
|
||||
result.add(document);
|
||||
documents.add(document);
|
||||
}
|
||||
document = new Document();
|
||||
}
|
||||
else {
|
||||
if (document.isEmpty() && !documents.isEmpty()) {
|
||||
document = documents.remove(documents.size() - 1);
|
||||
}
|
||||
reader.setLastLineComment(true);
|
||||
reader.skipComment();
|
||||
}
|
||||
}
|
||||
String key = loadKey(buffer, reader).trim();
|
||||
if (expandLists && key.endsWith("[]")) {
|
||||
key = key.substring(0, key.length() - 2);
|
||||
int index = 0;
|
||||
do {
|
||||
OriginTrackedValue value = loadValue(buffer, reader, true);
|
||||
document.put(key + "[" + (index++) + "]", value);
|
||||
if (!reader.isEndOfLine()) {
|
||||
reader.read();
|
||||
}
|
||||
}
|
||||
while (!reader.isEndOfLine());
|
||||
}
|
||||
else {
|
||||
OriginTrackedValue value = loadValue(buffer, reader, false);
|
||||
document.put(key, value);
|
||||
reader.setLastLineComment(false);
|
||||
loadKeyAndValue(expandLists, document, reader, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!document.isEmpty() && !result.contains(document)) {
|
||||
result.add(document);
|
||||
if (!document.isEmpty() && !documents.contains(document)) {
|
||||
documents.add(document);
|
||||
}
|
||||
return documents;
|
||||
}
|
||||
|
||||
private void loadKeyAndValue(boolean expandLists, Document document, CharacterReader reader, StringBuilder buffer)
|
||||
throws IOException {
|
||||
String key = loadKey(buffer, reader).trim();
|
||||
if (expandLists && key.endsWith("[]")) {
|
||||
key = key.substring(0, key.length() - 2);
|
||||
int index = 0;
|
||||
do {
|
||||
OriginTrackedValue value = loadValue(buffer, reader, true);
|
||||
document.put(key + "[" + (index++) + "]", value);
|
||||
if (!reader.isEndOfLine()) {
|
||||
reader.read();
|
||||
}
|
||||
}
|
||||
while (!reader.isEndOfLine());
|
||||
}
|
||||
else {
|
||||
OriginTrackedValue value = loadValue(buffer, reader, false);
|
||||
document.put(key, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String loadKey(StringBuilder buffer, CharacterReader reader) throws IOException {
|
||||
@@ -149,6 +161,9 @@ class OriginTrackedPropertiesLoader {
|
||||
}
|
||||
|
||||
boolean isNewDocument(CharacterReader reader) throws IOException {
|
||||
if (reader.isLastLineComment()) {
|
||||
return false;
|
||||
}
|
||||
boolean result = reader.getLocation().getColumn() == 0 && reader.isPoundCharacter();
|
||||
result = result && readAndExpect(reader, reader::isHyphenCharacter);
|
||||
result = result && readAndExpect(reader, reader::isHyphenCharacter);
|
||||
@@ -179,6 +194,8 @@ class OriginTrackedPropertiesLoader {
|
||||
|
||||
private int character;
|
||||
|
||||
private boolean lastLineComment;
|
||||
|
||||
CharacterReader(Resource resource) throws IOException {
|
||||
this.reader = new LineNumberReader(
|
||||
new InputStreamReader(resource.getInputStream(), StandardCharsets.ISO_8859_1));
|
||||
@@ -222,6 +239,14 @@ class OriginTrackedPropertiesLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private void setLastLineComment(boolean lastLineComment) {
|
||||
this.lastLineComment = lastLineComment;
|
||||
}
|
||||
|
||||
private boolean isLastLineComment() {
|
||||
return this.lastLineComment;
|
||||
}
|
||||
|
||||
private void skipComment() throws IOException {
|
||||
while (this.character != '\n' && this.character != -1) {
|
||||
this.character = this.reader.read();
|
||||
|
||||
@@ -286,6 +286,13 @@ class OriginTrackedPropertiesLoaderTests {
|
||||
assertThat(getValue(value)).isEqualTo("trailing ");
|
||||
}
|
||||
|
||||
@Test
|
||||
void existingCommentsAreNotTreatedAsMultiDoc() throws Exception {
|
||||
this.resource = new ClassPathResource("existing-non-multi-document.properties", getClass());
|
||||
this.documents = new OriginTrackedPropertiesLoader(this.resource).load();
|
||||
assertThat(this.documents.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
private OriginTrackedValue getFromFirst(String key) {
|
||||
return this.documents.get(0).asMap().get(key);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#---
|
||||
# Test
|
||||
#---
|
||||
|
||||
spring=boot
|
||||
|
||||
#---
|
||||
# Test
|
||||
|
||||
boot=bar
|
||||
|
||||
|
||||
# Test
|
||||
#---
|
||||
|
||||
bar=ok
|
||||
Reference in New Issue
Block a user