Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into 135-support-data-cl…
Browse files Browse the repository at this point in the history
…asses-draft
  • Loading branch information
ljacqu committed Jan 18, 2024
2 parents c4af00c + 518e002 commit 2e42127
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/maven_jdk11.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up JDK 11
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '11'
distribution: 'adopt'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maven_jdk8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v4

- name: Set up JDK 8
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: '8'
distribution: 'adopt'
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/ch/jalu/configme/properties/MapProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ public class MapProperty<V> extends BaseProperty<Map<String, V>> {

private final PropertyType<V> valueType;

/**
* Constructor. Builds a {@link MapProperty} with an empty map as default value.
*
* @param path the path of the property
* @param valueType the property type of the values
*/
public MapProperty(@NotNull String path, @NotNull PropertyType<V> valueType) {
super(path, Collections.emptyMap());
Objects.requireNonNull(valueType, "valueType");
this.valueType = valueType;
}

/**
* Constructor.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;
Expand Down Expand Up @@ -60,6 +61,10 @@ public class InlineArrayPropertyType<T> implements PropertyType<T[]> {
public static final InlineArrayPropertyType<String> STRING =
new InlineArrayPropertyType<>(StringType.STRING, "\n", false, String[]::new);

/** Big Decimal values, comma-separated. */
public static final InlineArrayPropertyType<BigDecimal> BIG_DECIMAL =
new InlineArrayPropertyType<>(NumberType.BIG_DECIMAL, ",", true, BigDecimal[]::new);


private final PropertyType<T> entryType;
private final String separator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ public void transferComments(@NotNull Node valueNode, @NotNull Node keyNode) {
}

protected @NotNull Node createStringNode(@NotNull String value) {
return new ScalarNode(Tag.STR, value, null, null, DumperOptions.ScalarStyle.PLAIN);
DumperOptions.ScalarStyle scalarStyle = value.contains("\n")
? DumperOptions.ScalarStyle.LITERAL // Used for strings that span multiple lines
: DumperOptions.ScalarStyle.PLAIN; // Used for single line string
return new ScalarNode(Tag.STR, value, null, null, scalarStyle);
}

protected @NotNull Node createNumberNode(@NotNull Number value) {
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/ch/jalu/configme/properties/MapPropertyTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ch.jalu.configme.TestUtils;
import ch.jalu.configme.properties.convertresult.ConvertErrorRecorder;
import ch.jalu.configme.properties.convertresult.PropertyValue;
import ch.jalu.configme.properties.types.NumberType;
import ch.jalu.configme.properties.types.PropertyType;
import ch.jalu.configme.properties.types.StringType;
import ch.jalu.configme.resource.PropertyReader;
Expand All @@ -27,6 +28,7 @@
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.collection.IsMapWithSize.anEmptyMap;
import static org.mockito.BDDMockito.given;

/**
Expand Down Expand Up @@ -111,6 +113,20 @@ void shouldKeepOrderInExportValue() {
assertThat(((Map<Integer, String>) exportValue).keySet(), contains("first", "second", "third", "fourth"));
}

@Test
void shouldUseEmptyMapAsDefaultValue() {
// given
MapProperty<Integer> property = new MapProperty<>("test", NumberType.INTEGER);

//when
Map<String, Integer> actualDefaultValue = property.getDefaultValue();
String actualPath = property.getPath();

// then
assertThat(actualDefaultValue, anEmptyMap());
assertThat(actualPath, equalTo("test"));
}

private static Map<String, String> createSampleMap() {
Map<String, String> map = new HashMap<>();
map.put("test", "keks");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

Expand Down Expand Up @@ -170,6 +171,9 @@ private static TestData getTestData(InlineArrayPropertyType<?> converter) {
String someString = "An even longer String\twith a tab";
testData.setInputWithErrors(someString + "\n\n",
someString, "", "");
} else if (converter == InlineArrayPropertyType.BIG_DECIMAL) {
testData.setInputAndExpected("3, 4.5, -445.68234", "3, 4.5, -445.68234", BigDecimal.valueOf(3), BigDecimal.valueOf(4.5), BigDecimal.valueOf(-445.68234));
testData.setInputWithErrors("3, a, 4.5, -2, -b", BigDecimal.valueOf(3), BigDecimal.valueOf(4.5), BigDecimal.valueOf(-2));
} else {
throw new IllegalStateException("Unhandled converter '" + converter + "'");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package ch.jalu.configme.resource;

import ch.jalu.configme.TestUtils;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.configurationdata.ConfigurationDataBuilder;
import ch.jalu.configme.properties.Property;
import ch.jalu.configme.properties.PropertyInitializer;
import ch.jalu.configme.properties.StringProperty;
import ch.jalu.configme.properties.convertresult.PropertyValue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;

/**
* Tests that multi-line string values are properly read and exported.
*
* @see <a href="https://github.com/AuthMe/ConfigMe/issues/405">Issue #405</a>
*/
class YamlFileResourceMultilineStringTest {

@TempDir
public Path temporaryFolder;

@Test
void shouldReadAndExportMultipleLines() throws IOException {
// given
Path configFile = TestUtils.copyFileFromResources("/multiple_lines.yml", temporaryFolder);
PropertyResource resource = new YamlFileResource(configFile);

Property<String> linesProperty = PropertyInitializer.newProperty("lines", "");
ConfigurationData configurationData = ConfigurationDataBuilder.createConfiguration(Collections.singletonList(linesProperty));
configurationData.initializeValues(resource.createReader());

// when (read)
String linesValue = configurationData.getValue(linesProperty);

// then
assertThat(linesValue, equalTo("First row\n\nSecond row\nThird row\n"));

// when (write)
resource.exportProperties(configurationData);

// then
String lines = linesProperty.determineValue(resource.createReader()).getValue();
assertThat(lines, equalTo("First row\n\nSecond row\nThird row\n"));

byte[] fileBytes = Files.readAllBytes(configFile);
String fileContent = new String(fileBytes, StandardCharsets.UTF_8);
assertThat(fileContent,
equalTo(
"lines: |"
+ "\n First row"
+ "\n"
+ "\n Second row"
+ "\n Third row"
+ "\n"));
}

@Test
void shouldWriteMultipleLines() throws IOException {
// given
Path configFile = TestUtils.copyFileFromResources("/empty_file.yml", temporaryFolder);
PropertyResource resource = new YamlFileResource(configFile);

Property<String> l1Property = PropertyInitializer.newProperty("l1", "");
Property<String> l2Property = PropertyInitializer.newProperty("l2", "");
Property<String> l3Property = PropertyInitializer.newProperty("l3", "");
Property<String> l4Property = PropertyInitializer.newProperty("l4", "");

ConfigurationData configurationData = ConfigurationDataBuilder.createConfiguration(
Arrays.asList(l1Property, l2Property, l3Property, l4Property));
configurationData.initializeValues(resource.createReader());

// set multiple-line strings
configurationData.setValue(l1Property, "First row\nSecond row");
configurationData.setValue(l2Property, "First row\r\nSecond row");
configurationData.setValue(l3Property, "First text Second text");
configurationData.setValue(l4Property, "[{\r\n\"enabled\" : true \r\n}]");

// when
resource.exportProperties(configurationData);

// then
PropertyReader propertyReader = resource.createReader();
String l1 = l1Property.determineValue(propertyReader).getValue();
assertThat(l1, equalTo("First row\nSecond row"));

String l2 = l2Property.determineValue(propertyReader).getValue();
assertThat(l2, equalTo("First row\r\nSecond row"));

String l3 = l3Property.determineValue(propertyReader).getValue();
assertThat(l3, equalTo("First text Second text"));

String l4 = l4Property.determineValue(propertyReader).getValue();
assertThat(l4, equalTo("[{\r\n\"enabled\" : true \r\n}]"));

byte[] fileBytes = Files.readAllBytes(configFile);
String fileContent = new String(fileBytes, StandardCharsets.UTF_8);

assertThat(fileContent,
equalTo(
"l1: |-"
+ "\n First row"
+ "\n Second row"
+ "\nl2: \"First row\\r\\nSecond row\""
+ "\nl3: First text Second text"
+ "\nl4: \"[{\\r\\n\\\"enabled\\\" : true \\r\\n}]\""
+ "\n"));
}

@ParameterizedTest
@MethodSource("getStringsToExport")
void shouldExportIdenticalValue(String value) {
// given
Property<String> property = new StringProperty("test.string", "#Default");
ConfigurationData configurationData = ConfigurationDataBuilder.createConfiguration(Collections.singletonList(property));
configurationData.setValue(property, value);

Path file = temporaryFolder.resolve("test.yml");
YamlFileResource resource = new YamlFileResource(file);

// when
resource.exportProperties(configurationData);
configurationData.setValue(property, "#Overwritten");
configurationData.initializeValues(resource.createReader());

// then
assertThat(configurationData.getValue(property), equalTo(value));
}

/**
* Multi-line string properties with CRLF are parsed as LF.
*/
@Test
void shouldReadStringWithCarriageReturnsAndDiscardThem() throws IOException {
// given
String contents = "shopping:"
+ "\r\n items: |"
+ "\r\n Potatoes"
+ "\r\n Sausages"
+ "\r\n Onions";
Path file = temporaryFolder.resolve("test.yml");
Files.write(file, contents.getBytes());
YamlFileReader reader = new YamlFileReader(file);

Property<String> property = new StringProperty("shopping.items", "undefined");

// when
PropertyValue<String> result = property.determineValue(reader);

// then
assertThat(result.getValue(), equalTo("Potatoes\nSausages\nOnions"));
assertThat(result.isValidInResource(), equalTo(true));
}

/*
* Some of these texts aren't exported as scalar blocks despite having a newline in them; SnakeYAML checks the
* contents and decides whether the literal scalar style can be applied. The purpose of these test cases is to
* ensure that the strings are exported and re-read to the IDENTICAL value (including any and all whitespace),
* and more distantly, to ensure that no values break the export (though that is the responsibility of SnakeYAML).
*/
private static List<String> getStringsToExport() {
List<String> cases = new ArrayList<>();

// Whitespace cases
cases.add(" Text with\ninitial whitespace");
cases.add("Ending spaces\nin this text ");
cases.add("Text with\nending whitespace\n\n");
cases.add("\nStart with a new line\nEnd with no new line");
cases.add("\tFirst line\nhas a tab");
cases.add("Indent the end\nwith a tab\t");

// Carriage returns
cases.add("\rCR lines\rCR lines");
cases.add("More CR lines\r");
cases.add("Here are\r\nWindows lines");
cases.add("Another\r\nWin line text\r\n");

// Other characters (typically chars that have a special meaning in YAML)
cases.add("Second line\n# looks like a comment");
cases.add("test: true\nhmm");
cases.add("??? hi\n--- hello");
cases.add("!!int 3\n!!string");
cases.add("|MD table|Col_2|\n|---|---|\n|row1|rowA|\n|row2|rowB|");
cases.add("\\ testing\n\\|some combinations\\n\n: test");

return cases;
}
}
Loading

0 comments on commit 2e42127

Please sign in to comment.