From 33e379c4a2a8f50f4d2490943537d0d1b6f831f6 Mon Sep 17 00:00:00 2001 From: Guirong Hu Date: Tue, 27 Sep 2022 19:02:36 +0800 Subject: [PATCH 1/2] Use exclamation character for the document separator prefix See gh-32521 --- .../asciidoc/features/external-config.adoc | 4 +- .../env/OriginTrackedPropertiesLoader.java | 42 +++++++---------- .../OriginTrackedPropertiesLoaderTests.java | 47 +++++++++++++++++-- .../existing-non-multi-document.properties | 16 +++++++ 4 files changed, 77 insertions(+), 32 deletions(-) diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc index 13a964897b..fb147302fd 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/external-config.adoc @@ -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 <> for details. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java index adf07a191b..4d2cb2c84b 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java @@ -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() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java index 13d1af165c..5a591c4de2 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedPropertiesLoaderTests.java @@ -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 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 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 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 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 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 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 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 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 loaded = new OriginTrackedPropertiesLoader(new ByteArrayResource(content.getBytes())).load(); + assertThat(loaded).hasSize(2); + } + } + @Test void getPropertyWithWhitespaceAfterKey() { OriginTrackedValue value = getFromFirst("bar"); diff --git a/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties index b5e003745c..faab368e37 100644 --- a/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties +++ b/spring-boot-project/spring-boot/src/test/resources/org/springframework/boot/env/existing-non-multi-document.properties @@ -14,3 +14,19 @@ boot=bar #--- bar=ok + +!--- +! Test +!--- + +ok=well + +!--- +! Test + +well=hello + +! Test +!--- + +hello=world From 2c7114fa17f2631259e9408be97bf543a29c1526 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Tue, 18 Oct 2022 17:06:36 -0700 Subject: [PATCH 2/2] Polish 'Use exclamation character for the document separator prefix' See gh-32521 --- .../boot/env/OriginTrackedPropertiesLoader.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java index 4d2cb2c84b..4d67069207 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedPropertiesLoader.java @@ -42,6 +42,7 @@ import org.springframework.util.Assert; * @author Madhura Bhave * @author Phillip Webb * @author Thiago Hirata + * @author Guirong Hu */ class OriginTrackedPropertiesLoader { @@ -90,12 +91,12 @@ class OriginTrackedPropertiesLoader { if (document.isEmpty() && !documents.isEmpty()) { document = documents.remove(documents.size() - 1); } - reader.setLastLineCommentPrefix(commentPrefixCharacter); + reader.setLastLineCommentPrefixCharacter(commentPrefixCharacter); reader.skipComment(); } } else { - reader.setLastLineCommentPrefix(-1); + reader.setLastLineCommentPrefixCharacter(-1); loadKeyAndValue(expandLists, document, reader, buffer); } } @@ -197,7 +198,7 @@ class OriginTrackedPropertiesLoader { private int character; - private int lastLineCommentPrefix; + private int lastLineCommentPrefixCharacter; CharacterReader(Resource resource) throws IOException { this.reader = new LineNumberReader( @@ -233,8 +234,8 @@ class OriginTrackedPropertiesLoader { } } - private void setLastLineCommentPrefix(int lastLineCommentPrefix) { - this.lastLineCommentPrefix = lastLineCommentPrefix; + private void setLastLineCommentPrefixCharacter(int lastLineCommentPrefixCharacter) { + this.lastLineCommentPrefixCharacter = lastLineCommentPrefixCharacter; } private void skipComment() throws IOException { @@ -307,7 +308,7 @@ class OriginTrackedPropertiesLoader { } boolean isSameLastLineCommentPrefix() { - return this.lastLineCommentPrefix == this.character; + return this.lastLineCommentPrefixCharacter == this.character; } boolean isCommentPrefixCharacter() {