Commit cf673cee authored by Phillip Webb's avatar Phillip Webb

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
parent 0588e989
...@@ -886,7 +886,8 @@ For `application.properties` files a special `#---` comment is used to mark the ...@@ -886,7 +886,8 @@ For `application.properties` files a special `#---` comment is used to mark the
spring.application.name=MyCloudApp 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`. 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. See the <<boot-features-external-config-file-activation-properties, next section>> for details.
......
...@@ -73,22 +73,41 @@ class OriginTrackedPropertiesLoader { ...@@ -73,22 +73,41 @@ class OriginTrackedPropertiesLoader {
* @throws IOException on read error * @throws IOException on read error
*/ */
List<Document> load(boolean expandLists) throws IOException { List<Document> load(boolean expandLists) throws IOException {
List<Document> result = new ArrayList<>(); List<Document> documents = new ArrayList<>();
Document document = new Document(); Document document = new Document();
try (CharacterReader reader = new CharacterReader(this.resource)) {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
try (CharacterReader reader = new CharacterReader(this.resource)) {
while (reader.read()) { while (reader.read()) {
if (reader.isPoundCharacter()) { if (reader.isPoundCharacter()) {
if (isNewDocument(reader)) { if (isNewDocument(reader)) {
if (!document.isEmpty()) { if (!document.isEmpty()) {
result.add(document); documents.add(document);
} }
document = new Document(); document = new Document();
} }
else { else {
if (document.isEmpty() && !documents.isEmpty()) {
document = documents.remove(documents.size() - 1);
}
reader.setLastLineComment(true);
reader.skipComment(); reader.skipComment();
} }
} }
else {
reader.setLastLineComment(false);
loadKeyAndValue(expandLists, document, reader, buffer);
}
}
}
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(); String key = loadKey(buffer, reader).trim();
if (expandLists && key.endsWith("[]")) { if (expandLists && key.endsWith("[]")) {
key = key.substring(0, key.length() - 2); key = key.substring(0, key.length() - 2);
...@@ -108,13 +127,6 @@ class OriginTrackedPropertiesLoader { ...@@ -108,13 +127,6 @@ class OriginTrackedPropertiesLoader {
} }
} }
}
if (!document.isEmpty() && !result.contains(document)) {
result.add(document);
}
return result;
}
private String loadKey(StringBuilder buffer, CharacterReader reader) throws IOException { private String loadKey(StringBuilder buffer, CharacterReader reader) throws IOException {
buffer.setLength(0); buffer.setLength(0);
boolean previousWhitespace = false; boolean previousWhitespace = false;
...@@ -149,6 +161,9 @@ class OriginTrackedPropertiesLoader { ...@@ -149,6 +161,9 @@ class OriginTrackedPropertiesLoader {
} }
boolean isNewDocument(CharacterReader reader) throws IOException { boolean isNewDocument(CharacterReader reader) throws IOException {
if (reader.isLastLineComment()) {
return false;
}
boolean result = reader.getLocation().getColumn() == 0 && reader.isPoundCharacter(); boolean result = reader.getLocation().getColumn() == 0 && reader.isPoundCharacter();
result = result && readAndExpect(reader, reader::isHyphenCharacter); result = result && readAndExpect(reader, reader::isHyphenCharacter);
result = result && readAndExpect(reader, reader::isHyphenCharacter); result = result && readAndExpect(reader, reader::isHyphenCharacter);
...@@ -179,6 +194,8 @@ class OriginTrackedPropertiesLoader { ...@@ -179,6 +194,8 @@ class OriginTrackedPropertiesLoader {
private int character; private int character;
private boolean lastLineComment;
CharacterReader(Resource resource) throws IOException { CharacterReader(Resource resource) throws IOException {
this.reader = new LineNumberReader( this.reader = new LineNumberReader(
new InputStreamReader(resource.getInputStream(), StandardCharsets.ISO_8859_1)); new InputStreamReader(resource.getInputStream(), StandardCharsets.ISO_8859_1));
...@@ -222,6 +239,14 @@ class OriginTrackedPropertiesLoader { ...@@ -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 { private void skipComment() throws IOException {
while (this.character != '\n' && this.character != -1) { while (this.character != '\n' && this.character != -1) {
this.character = this.reader.read(); this.character = this.reader.read();
......
...@@ -286,6 +286,13 @@ class OriginTrackedPropertiesLoaderTests { ...@@ -286,6 +286,13 @@ class OriginTrackedPropertiesLoaderTests {
assertThat(getValue(value)).isEqualTo("trailing "); 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) { private OriginTrackedValue getFromFirst(String key) {
return this.documents.get(0).asMap().get(key); return this.documents.get(0).asMap().get(key);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment