diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsStrings.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsStrings.java index ac3d6483..02d8b374 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsStrings.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsStrings.java @@ -13,6 +13,12 @@ package pt.up.fe.specs.util; +import pt.up.fe.specs.util.collections.MultiMap; +import pt.up.fe.specs.util.exceptions.OverflowException; +import pt.up.fe.specs.util.parsing.LineParser; +import pt.up.fe.specs.util.utilities.LineStream; +import pt.up.fe.specs.util.utilities.StringLines; + import java.io.File; import java.io.UnsupportedEncodingException; import java.lang.reflect.ParameterizedType; @@ -22,17 +28,7 @@ import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.StringJoiner; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.function.Supplier; @@ -41,12 +37,6 @@ import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; -import pt.up.fe.specs.util.collections.MultiMap; -import pt.up.fe.specs.util.exceptions.OverflowException; -import pt.up.fe.specs.util.parsing.LineParser; -import pt.up.fe.specs.util.utilities.LineStream; -import pt.up.fe.specs.util.utilities.StringLines; - /** * Utility methods for parsing of values which, instead of throwing an * exception, return a default value if a parsing @@ -67,6 +57,7 @@ public class SpecsStrings { } private static final Map TIME_UNIT_SYMBOL; + static { TIME_UNIT_SYMBOL = new HashMap<>(); SpecsStrings.TIME_UNIT_SYMBOL.put(TimeUnit.DAYS, "days"); @@ -94,7 +85,7 @@ public static boolean isPrintableChar(char c) { * * @param integer a String representing an integer. * @return the intenger represented by the string, or 0 if it couldn't be - * parsed. + * parsed. */ public static int parseInt(String integer) { int intResult = 0; @@ -113,7 +104,7 @@ public static int parseInt(String integer) { * * @param integer a String representing an integer. * @return the integer represented by the string, or null if it couldn't be - * parsed. + * parsed. */ public static Integer parseInteger(String integer) { try { @@ -128,7 +119,7 @@ public static Integer parseInteger(String integer) { * * @param doublefloat a String representing a double. * @return the double represented by the string, or null if it couldn't be - * parsed. + * parsed. */ public static Optional valueOfDouble(String doublefloat) { try { @@ -147,7 +138,7 @@ public static short parseShort(String s) { * * @param afloat a String representing a float. * @return the float represented by the string, or null if it couldn't be - * parsed. + * parsed. */ public static Float parseFloat(String afloat) { return parseFloat(afloat, true); @@ -176,7 +167,7 @@ public static Float parseFloat(String afloat, boolean isStrict) { * * @param aDouble a String representing a double. * @return the double represented by the string, or null if it couldn't be - * parsed. + * parsed. */ public static Double parseDouble(String aDouble) { return parseDouble(aDouble, true); @@ -188,7 +179,7 @@ public static Double parseDouble(String aDouble) { * * @param aDouble a String representing a double. * @return the double represented by the string, or null if it couldn't be - * parsed. + * parsed. */ public static Double parseDouble(String aDouble, boolean isStrict) { try { @@ -244,7 +235,7 @@ public static BigInteger parseBigInteger(String intNumber) { * * @param booleanString a String representing a Boolean. * @return the Boolean represented by the string, or null if it couldn't be - * parsed. + * parsed. */ public static Boolean parseBoolean(String booleanString) { booleanString = booleanString.toLowerCase(); @@ -320,7 +311,7 @@ public static String toHexString(long decimalLong, int stringSize) { /** * @param string a string * @return the index of the first whitespace found in the given String, or -1 if - * none is found. + * none is found. */ public static int indexOfFirstWhitespace(String string) { return indexOf(string, Character::isWhitespace, false); @@ -1587,13 +1578,12 @@ public static String normalizeFileContents(String fileContents, boolean ignoreEm // Normalize new lines String normalizedString = fileContents.replaceAll("\r\n", "\n"); - // Remove empty lines - if (ignoreEmptyLines) { - normalizedString = StringLines.getLines(normalizedString).stream() - .map(String::trim) - .filter(line -> !line.isEmpty()) - .collect(Collectors.joining("\n")); - } + // Trim strings + normalizedString = StringLines.getLines(normalizedString).stream() + .map(String::trim) + // Remove empty lines + .filter(line -> !(ignoreEmptyLines && line.isEmpty())) + .collect(Collectors.joining("\n")); return normalizedString; @@ -1683,7 +1673,7 @@ public static String toDecimal(long number) { * Splits the given String according to a separator, and removes blank String * that can be created from the splitting. * - * @param strip if true, strips each splitted String + * @param strip if true, strips each splitted String */ public static List splitNonEmpty(String string, String separator, boolean strip) { return Arrays.stream(string.split(separator)) diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsStringsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsStringsTest.java index 00d446f9..5b25a31d 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsStringsTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/SpecsStringsTest.java @@ -1140,6 +1140,71 @@ void testParseDoubleStrict() { } } + @Nested + @DisplayName("normalizeFileContents") + class NormalizeFileContents { + + @Test + @DisplayName("trims whitespace-only lines to empty strings") + void testWhitespaceOnlyLinesAreTrimmed() { + String input = "hello\n \nworld"; + // ignoreEmptyLines=false: whitespace-only lines become empty lines, preserved + assertThat(SpecsStrings.normalizeFileContents(input, false)).isEqualTo("hello\n\nworld"); + } + + @Test + @DisplayName("preserves empty lines when ignoreEmptyLines is false") + void testPreservesEmptyLinesWhenNotIgnoring() { + String input = "line1\n\nline2\n\nline3"; + assertThat(SpecsStrings.normalizeFileContents(input, false)).isEqualTo("line1\n\nline2\n\nline3"); + } + + @Test + @DisplayName("removes empty lines when ignoreEmptyLines is true") + void testRemovesEmptyLinesWhenIgnoring() { + String input = "line1\n\nline2\n\nline3"; + assertThat(SpecsStrings.normalizeFileContents(input, true)).isEqualTo("line1\nline2\nline3"); + } + + @Test + @DisplayName("removes whitespace-only lines when ignoreEmptyLines is true") + void testRemovesWhitespaceOnlyLinesWhenIgnoring() { + String input = "line1\n \nline2"; + assertThat(SpecsStrings.normalizeFileContents(input, true)).isEqualTo("line1\nline2"); + } + + @Test + @DisplayName("preserves trailing empty line (from double newline) when ignoreEmptyLines is false") + void testTrailingEmptyLinePreservedWhenNotIgnoring() { + // A double trailing newline produces an empty last line that should be preserved + String input = "line1\nline2\n\n"; + assertThat(SpecsStrings.normalizeFileContents(input, false)).isEqualTo("line1\nline2\n"); + } + + @Test + @DisplayName("removes trailing empty line (from double newline) when ignoreEmptyLines is true") + void testTrailingEmptyLineRemovedWhenIgnoring() { + // A double trailing newline produces an empty last line that should be removed + String input = "line1\nline2\n\n"; + assertThat(SpecsStrings.normalizeFileContents(input, true)).isEqualTo("line1\nline2"); + } + + @Test + @DisplayName("normalizes CRLF line endings") + void testNormalizesCrLf() { + String input = "line1\r\nline2\r\nline3"; + assertThat(SpecsStrings.normalizeFileContents(input, false)).isEqualTo("line1\nline2\nline3"); + } + + @Test + @DisplayName("single-argument overload delegates to ignoreEmptyLines=false") + void testSingleArgOverload() { + String input = "line1\n\nline2"; + assertThat(SpecsStrings.normalizeFileContents(input)) + .isEqualTo(SpecsStrings.normalizeFileContents(input, false)); + } + } + // Static data providers for parameterized tests static List validIntegerInputs() { return Arrays.asList(