Introduce support for sorted Properties

This commit introduces an internal SortedProperties class that is a
specialization of java.util.Properties which sorts properties
alphanumerically based on their keys.

This can be useful when storing a java.util.Properties instance in a
properties file, since it allows such files to be generated in a
repeatable manner with consistent ordering of properties.

Comments in generated properties files can also be optionally omitted.

An instance of SortedProperties can be created via two new
createSortedProperties() factory methods in
org.springframework.core.CollectionFactory.

Closes gh-23018
This commit is contained in:
Sam Brannen
2019-05-28 11:29:23 +02:00
parent 98079a364d
commit c39c4211df
3 changed files with 418 additions and 0 deletions

View File

@@ -0,0 +1,213 @@
/*
* Copyright 2002-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Properties;
import org.junit.Test;
import static java.util.Arrays.stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/**
* Unit tests for {@link SortedProperties}.
*
* @author Sam Brannen
* @since 5.2
*/
public class SortedPropertiesTests {
@Test
public void keys() {
assertKeys(createSortedProps());
}
@Test
public void keysFromPrototype() {
assertKeys(createSortedPropsFromPrototype());
}
@Test
public void keySet() {
assertKeySet(createSortedProps());
}
@Test
public void keySetFromPrototype() {
assertKeySet(createSortedPropsFromPrototype());
}
@Test
public void entrySet() {
assertEntrySet(createSortedProps());
}
@Test
public void entrySetFromPrototype() {
assertEntrySet(createSortedPropsFromPrototype());
}
@Test
public void sortsPropertiesUsingOutputStream() throws IOException {
SortedProperties sortedProperties = createSortedProps();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
sortedProperties.store(baos, "custom comment");
String[] lines = lines(baos);
assertThat(lines).hasSize(7);
assertThat(lines[0]).isEqualTo("#custom comment");
assertThat(lines[1]).as("timestamp").startsWith("#");
assertPropsAreSorted(lines);
}
@Test
public void sortsPropertiesUsingWriter() throws IOException {
SortedProperties sortedProperties = createSortedProps();
StringWriter writer = new StringWriter();
sortedProperties.store(writer, "custom comment");
String[] lines = lines(writer);
assertThat(lines).hasSize(7);
assertThat(lines[0]).isEqualTo("#custom comment");
assertThat(lines[1]).as("timestamp").startsWith("#");
assertPropsAreSorted(lines);
}
@Test
public void sortsPropertiesAndOmitsCommentsUsingOutputStream() throws IOException {
SortedProperties sortedProperties = createSortedProps(true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
sortedProperties.store(baos, "custom comment");
String[] lines = lines(baos);
assertThat(lines).hasSize(5);
assertPropsAreSorted(lines);
}
@Test
public void sortsPropertiesAndOmitsCommentsUsingWriter() throws IOException {
SortedProperties sortedProperties = createSortedProps(true);
StringWriter writer = new StringWriter();
sortedProperties.store(writer, "custom comment");
String[] lines = lines(writer);
assertThat(lines).hasSize(5);
assertPropsAreSorted(lines);
}
@Test
public void storingAsXmlSortsPropertiesAndOmitsComments() throws IOException {
SortedProperties sortedProperties = createSortedProps(true);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
sortedProperties.storeToXML(baos, "custom comment");
String[] lines = lines(baos);
assertThat(lines).containsExactly( //
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>", //
"<!DOCTYPE properties SYSTEM \"http://java.sun.com/dtd/properties.dtd\">", //
"<properties>", //
"<entry key=\"color\">blue</entry>", //
"<entry key=\"fragrance\">sweet</entry>", //
"<entry key=\"fruit\">apple</entry>", //
"<entry key=\"size\">medium</entry>", //
"<entry key=\"vehicle\">car</entry>", //
"</properties>" //
);
}
private SortedProperties createSortedProps() {
return createSortedProps(false);
}
private SortedProperties createSortedProps(boolean omitComments) {
SortedProperties sortedProperties = new SortedProperties(omitComments);
populateProperties(sortedProperties);
return sortedProperties;
}
private SortedProperties createSortedPropsFromPrototype() {
Properties properties = new Properties();
populateProperties(properties);
return new SortedProperties(properties, false);
}
private void populateProperties(Properties properties) {
properties.setProperty("color", "blue");
properties.setProperty("fragrance", "sweet");
properties.setProperty("fruit", "apple");
properties.setProperty("size", "medium");
properties.setProperty("vehicle", "car");
}
private String[] lines(ByteArrayOutputStream baos) {
return lines(new String(baos.toByteArray(), StandardCharsets.ISO_8859_1));
}
private String[] lines(StringWriter writer) {
return lines(writer.toString());
}
private String[] lines(String input) {
return input.trim().split(SortedProperties.EOL);
}
private void assertKeys(Properties properties) {
assertThat(Collections.list(properties.keys())) //
.containsExactly("color", "fragrance", "fruit", "size", "vehicle");
}
private void assertKeySet(Properties properties) {
assertThat(properties.keySet()).containsExactly("color", "fragrance", "fruit", "size", "vehicle");
}
private void assertEntrySet(Properties properties) {
assertThat(properties.entrySet()).containsExactly( //
entry("color", "blue"), //
entry("fragrance", "sweet"), //
entry("fruit", "apple"), //
entry("size", "medium"), //
entry("vehicle", "car") //
);
}
private void assertPropsAreSorted(String[] lines) {
assertThat(stream(lines).filter(s -> !s.startsWith("#"))).containsExactly( //
"color=blue", //
"fragrance=sweet", //
"fruit=apple", //
"size=medium", //
"vehicle=car"//
);
}
}