From 502d4e8729e6e337db8973a342218dc3656e2bbf Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 09:06:17 +0000 Subject: [PATCH 01/11] Switched to cased string preserving the parts from the parsing --- .../org/apache/rat/utils/CasedString.java | 63 +++++++++++++------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java index 79b29d109..df7fcbad1 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java @@ -19,6 +19,7 @@ package org.apache.rat.utils; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.function.Function; @@ -32,32 +33,34 @@ */ public class CasedString { /** the string of the cased format. */ - private final String string; + private final String[] parts; /** the case of the string. */ private final StringCase stringCase; /** - * A method to join camel string fragments together. + * A method to join pascal string fragments together. */ - private static final Function CAMEL_JOINER = a -> { - StringBuilder sb = new StringBuilder(a[0].toLowerCase(Locale.ROOT)); - - for (int i = 1; i < a.length; i++) { - sb.append(WordUtils.capitalize(a[i].toLowerCase(Locale.ROOT))); - } + private static final Function PASCAL_JOINER = strings -> { + StringBuilder sb = new StringBuilder(); + Arrays.stream(strings).forEach(token -> sb.append(WordUtils.capitalize(token.toLowerCase(Locale.ROOT)))); return sb.toString(); }; + /** * An enumeration of supported string cases. These cases tag strings as having a specific format. */ public enum StringCase { /** * Camel case tags strings like 'CamelCase' or 'camelCase'. This conversion forces the first character to - * lower case. If specific capitalization rules are required use {@link WordUtils#capitalize(String)} to set the first - * character of the string. + * lower case. If the first character is desired to be upper case use PASCAL case instead. */ - CAMEL(Character::isUpperCase, true, CAMEL_JOINER), + CAMEL(Character::isUpperCase, true, PASCAL_JOINER.andThen(WordUtils::uncapitalize)), + /** + * Camel case tags strings like 'PascalCase' or 'pascalCase'. This conversion forces the first character to + * upper case. If the first character is desired to be lower case use CAMEL case instead. + */ + PASCAL(Character::isUpperCase, true, PASCAL_JOINER), /** * Snake case tags strings like 'Snake_Case'. This conversion does not change the capitalization of any characters * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, @@ -83,7 +86,15 @@ public enum StringCase { * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. */ - DOT(c -> c == '.', false, a -> String.join(".", a)); + DOT(c -> c == '.', false, a -> String.join(".", a)), + + /** + * Slash case tags phrases of words like 'phrase.case'. This conversion does not change the capitalization of any characters + * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, + * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. + */ + SLASH(c -> c == '/', false, a -> String.join("/", a)); + /** The segment value for a null string */ private static final String[] NULL_SEGMENT = new String[0]; @@ -158,17 +169,34 @@ public String[] getSegments(final String string) { * @param string The string. */ public CasedString(final StringCase stringCase, final String string) { - this.string = string == null ? null : stringCase.assemble(stringCase.getSegments(string.trim())); + this.parts = string == null ? StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); + this.stringCase = stringCase; + } + + /** + * A representation of a cased string and the identified case of that string. + * @param stringCase The {@code StringCase} that the {@code string} argument is in. + * @param parts The string parts. + */ + public CasedString(final StringCase stringCase, final String[] parts) { + this.parts = parts; this.stringCase = stringCase; } + public CasedString as(final StringCase stringCase) { + if (stringCase == this.stringCase) { + return this; + } + return new CasedString(stringCase, Arrays.copyOf(this.parts, this.parts.length)); + } + /** * Returns an array of each of the segments in this CasedString. Segments are defined as the strings between * the separators in the CasedString. For the CAMEL case the segments are determined by the presence of a capital letter. * @return the array of Strings that are segments of the cased string. */ public String[] getSegments() { - return stringCase.getSegments(string); + return parts; } /** @@ -178,10 +206,7 @@ public String[] getSegments() { * @return the String current string represented in the new format. */ public String toCase(final StringCase stringCase) { - if (stringCase == this.stringCase) { - return string; - } - return string == null ? null : stringCase.joiner.apply(getSegments()); + return parts == StringCase.NULL_SEGMENT ? null : stringCase.joiner.apply(getSegments()); } /** @@ -190,6 +215,6 @@ public String toCase(final StringCase stringCase) { */ @Override public String toString() { - return string; + return toCase(stringCase); } } From 4dc631112b11ed288e0c48ab2f3ddf818902484b Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 11:41:32 +0000 Subject: [PATCH 02/11] Update CasedString.java Reworked implementation --- .../org/apache/rat/utils/CasedString.java | 242 +++++++----------- 1 file changed, 88 insertions(+), 154 deletions(-) diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java index df7fcbad1..6734946cc 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java @@ -32,189 +32,123 @@ * @since 0.17 */ public class CasedString { - /** the string of the cased format. */ private final String[] parts; - /** the case of the string. */ private final StringCase stringCase; - - /** - * A method to join pascal string fragments together. - */ - private static final Function PASCAL_JOINER = strings -> { + private static final Function PASCAL_JOINER = (strings) -> { StringBuilder sb = new StringBuilder(); - Arrays.stream(strings).forEach(token -> sb.append(WordUtils.capitalize(token.toLowerCase(Locale.ROOT)))); + Arrays.stream(strings).map((s) -> s == null ? "" : s).forEach((token) -> sb.append(WordUtils.capitalize(token.toLowerCase(Locale.ROOT)))); return sb.toString(); }; + public CasedString(StringCase stringCase, String string) { + this.parts = string == null ? CasedString.StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); + this.stringCase = stringCase; + } + + public CasedString(StringCase stringCase, String[] parts) { + this.parts = parts; + this.stringCase = stringCase; + } - /** - * An enumeration of supported string cases. These cases tag strings as having a specific format. - */ - public enum StringCase { - /** - * Camel case tags strings like 'CamelCase' or 'camelCase'. This conversion forces the first character to - * lower case. If the first character is desired to be upper case use PASCAL case instead. - */ - CAMEL(Character::isUpperCase, true, PASCAL_JOINER.andThen(WordUtils::uncapitalize)), - /** - * Camel case tags strings like 'PascalCase' or 'pascalCase'. This conversion forces the first character to - * upper case. If the first character is desired to be lower case use CAMEL case instead. - */ - PASCAL(Character::isUpperCase, true, PASCAL_JOINER), - /** - * Snake case tags strings like 'Snake_Case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - SNAKE(c -> c == '_', false, a -> String.join("_", a)), - /** - * Kebab case tags strings like 'kebab-case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - KEBAB(c -> c == '-', false, a -> String.join("-", a)), - - /** - * Phrase case tags phrases of words like 'phrase case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - PHRASE(Character::isWhitespace, false, a -> String.join(" ", a)), - - /** - * Dot case tags phrases of words like 'phrase.case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - DOT(c -> c == '.', false, a -> String.join(".", a)), - - /** - * Slash case tags phrases of words like 'phrase.case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - SLASH(c -> c == '/', false, a -> String.join("/", a)); - - - /** The segment value for a null string */ - private static final String[] NULL_SEGMENT = new String[0]; - /** The segment value for an empty string */ - private static final String[] EMPTY_SEGMENT = {""}; - - /** test for split position character. */ + public CasedString as(StringCase stringCase) { + return stringCase.name.equals(this.stringCase.name) ? this : new CasedString(stringCase, (String[])Arrays.copyOf(this.parts, this.parts.length)); + } + + public String[] getSegments() { + return this.parts; + } + + public String toCase(StringCase stringCase) { + return this.parts == CasedString.StringCase.NULL_SEGMENT ? null : (String)stringCase.joiner.apply(this.getSegments()); + } + + public String toString() { + return this.toCase(this.stringCase); + } + + public static class StringCase { + public static final StringCase CAMEL; + public static final StringCase PASCAL; + public static final StringCase SNAKE; + public static final StringCase KEBAB; + public static final StringCase PHRASE; + public static final StringCase DOT; + public static final StringCase SLASH; + private static final String[] NULL_SEGMENT; + private static final String[] EMPTY_SEGMENT; + private final String name; private final Predicate splitter; - /** if {@code true} split position character will be preserved in following segment. */ private final boolean preserveSplit; - /** a function to joining the segments into this case type. */ private final Function joiner; + private final Function postProcess; + + public StringCase(String name, Predicate splitter, boolean preserveSplit, Function joiner) { + this(name, splitter, preserveSplit, joiner, Function.identity()); + } - /** - * Defines a String Case. - * @param splitter The predicate that determines when a new word in the cased string begins. - * @param preserveSplit if {@code true} the character that the splitter detected is preserved as the first character of the new word. - * @param joiner The function to merge a list of strings into the cased String. - */ - StringCase(final Predicate splitter, final boolean preserveSplit, final Function joiner) { + public StringCase(String name, char delimiter) { + this(name, (c) -> c == delimiter, false, simpleJoiner(delimiter)); + } + + public static Function simpleJoiner(char delimiter) { + return (s) -> String.join(String.valueOf(delimiter), (CharSequence[])Arrays.stream(s).filter(Objects::nonNull).toArray((x$0) -> new String[x$0])); + } + + public StringCase(String name, Predicate splitter, boolean preserveSplit, Function joiner, Function postProcess) { + this.name = name; this.splitter = splitter; this.preserveSplit = preserveSplit; this.joiner = joiner; + this.postProcess = postProcess; + } + + public String toString() { + return this.name; } - /** - * Creates a cased string from a collection of segments. - * @param segments the segments to create the CasedString from. - * @return a CasedString - */ - public String assemble(final String[] segments) { - return segments.length == 0 ? null : this.joiner.apply(segments); + public String assemble(String[] segments) { + return (String)this.joiner.apply(segments); } - /** - * Returns an array of each of the segments in this CasedString. Segments are defined as the strings between - * the separators in the CasedString. For the CAMEL case the segments are determined by the presence of a capital letter. - * @return the array of Strings that are segments of the cased string. - */ - public String[] getSegments(final String string) { + public String[] getSegments(String string) { if (string == null) { return NULL_SEGMENT; - } - if (string.isEmpty()) { + } else if (string.isEmpty()) { return EMPTY_SEGMENT; - } - List lst = new ArrayList<>(); - StringBuilder sb = new StringBuilder(); - for (char c : string.toCharArray()) { - if (splitter.test(c)) { - if (!sb.isEmpty()) { + } else { + List lst = new ArrayList(); + StringBuilder sb = new StringBuilder(); + + for(char c : string.toCharArray()) { + if (this.splitter.test(c)) { lst.add(sb.toString()); sb.setLength(0); - } - if (preserveSplit) { + if (this.preserveSplit) { + sb.append(c); + } + } else { sb.append(c); } - } else { - sb.append(c); } - } - if (!sb.isEmpty()) { - lst.add(sb.toString()); - } - return lst.toArray(new String[0]); - } - } - - /** - * A representation of a cased string and the identified case of that string. - * @param stringCase The {@code StringCase} that the {@code string} argument is in. - * @param string The string. - */ - public CasedString(final StringCase stringCase, final String string) { - this.parts = string == null ? StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); - this.stringCase = stringCase; - } - /** - * A representation of a cased string and the identified case of that string. - * @param stringCase The {@code StringCase} that the {@code string} argument is in. - * @param parts The string parts. - */ - public CasedString(final StringCase stringCase, final String[] parts) { - this.parts = parts; - this.stringCase = stringCase; - } + if (!sb.isEmpty()) { + lst.add(sb.toString()); + } - public CasedString as(final StringCase stringCase) { - if (stringCase == this.stringCase) { - return this; + return (String[])lst.stream().map(this.postProcess).filter(Objects::nonNull).toArray((x$0) -> new String[x$0]); + } } - return new CasedString(stringCase, Arrays.copyOf(this.parts, this.parts.length)); - } - - /** - * Returns an array of each of the segments in this CasedString. Segments are defined as the strings between - * the separators in the CasedString. For the CAMEL case the segments are determined by the presence of a capital letter. - * @return the array of Strings that are segments of the cased string. - */ - public String[] getSegments() { - return parts; - } - - /** - * Converts this cased string into a {@code String} of another format. - * The upper/lower case of the characters within the string are not modified. - * @param stringCase The format to convert to. - * @return the String current string represented in the new format. - */ - public String toCase(final StringCase stringCase) { - return parts == StringCase.NULL_SEGMENT ? null : stringCase.joiner.apply(getSegments()); - } - /** - * Returns the string representation provided in the constructor. - * @return the string representation. - */ - @Override - public String toString() { - return toCase(stringCase); + static { + CAMEL = new StringCase("CAMEL", Character::isUpperCase, true, CasedString.PASCAL_JOINER.andThen(WordUtils::uncapitalize), (x) -> (String)StringUtils.defaultIfEmpty(x, (CharSequence)null)); + PASCAL = new StringCase("PASCAL", Character::isUpperCase, true, CasedString.PASCAL_JOINER, (x) -> (String)StringUtils.defaultIfEmpty(x, (CharSequence)null)); + SNAKE = new StringCase("SNAKE", '_'); + KEBAB = new StringCase("KEBAB", '-'); + PHRASE = new StringCase("PHRASE", Character::isWhitespace, false, simpleJoiner(' ')); + DOT = new StringCase("DOT", '.'); + SLASH = new StringCase("SLASH", '/'); + NULL_SEGMENT = new String[0]; + EMPTY_SEGMENT = new String[]{""}; + } } } From f59e485549a0290d82bece3c034fab4a6f95241e Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 12:12:28 +0000 Subject: [PATCH 03/11] updated CasedString added tests --- .../org/apache/rat/utils/CasedString.java | 102 ++++++++- .../apache/rat/utils/CasedStringTests.java | 211 ++++++++++++++++++ 2 files changed, 305 insertions(+), 8 deletions(-) create mode 100644 apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java index 6734946cc..572fc91aa 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java @@ -22,9 +22,11 @@ import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.WordUtils; /** @@ -40,60 +42,123 @@ public class CasedString { return sb.toString(); }; + /** + * Creates a cased string by parsing the string argument for the specific case. + * @param stringCase the case of the string being parsed. + * @param string the string to parse. + */ public CasedString(StringCase stringCase, String string) { this.parts = string == null ? CasedString.StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); this.stringCase = stringCase; } - public CasedString(StringCase stringCase, String[] parts) { - this.parts = parts; + /** + * Creates a cased string of the specified case and segments + * @param stringCase the case of the string. + * @param segments the segments of the string. + */ + public CasedString(StringCase stringCase, String[] segments) { + this.parts = segments; this.stringCase = stringCase; } + /** + * Converts this cased string into another format. + * @param stringCase the desired format. + * @return the new CasedString. + */ public CasedString as(StringCase stringCase) { return stringCase.name.equals(this.stringCase.name) ? this : new CasedString(stringCase, (String[])Arrays.copyOf(this.parts, this.parts.length)); } + /** + * Gets the segments of this cased string. + * @return the segments of this cased string. + */ public String[] getSegments() { return this.parts; } + /** + * Generates a string from this cased string but with the desired case. + * @param stringCase the desired case. + * @return this cased string in the desired case. + */ public String toCase(StringCase stringCase) { - return this.parts == CasedString.StringCase.NULL_SEGMENT ? null : (String)stringCase.joiner.apply(this.getSegments()); + return this.parts == CasedString.StringCase.NULL_SEGMENT ? null : (String)stringCase.assemble(this.getSegments()); } + @Override public String toString() { return this.toCase(this.stringCase); } + /** + * The definition of a String case. + */ public static class StringCase { + /** The camel case. Example: "HelloWorld"*/ public static final StringCase CAMEL; + /** The pascal case. Example: "helloWorld" */ public static final StringCase PASCAL; + /** The Snake case. Example: "hello_world" */ public static final StringCase SNAKE; + /** The Kebab case. Example: "hello-world" */ public static final StringCase KEBAB; + /** The phrase case. Example: "hello world" */ public static final StringCase PHRASE; + /** The dot case. Example: "hello.world" */ public static final StringCase DOT; + /** The slash case. Example: "hello/world" */ public static final StringCase SLASH; + /** A marker for the parsing of a NULL string. */ private static final String[] NULL_SEGMENT; + /** An empty segment marker. */ private static final String[] EMPTY_SEGMENT; + /** The name of this case */ private final String name; + /** The predicate that determines if a character is a spliter character. A splitter character + * is the character that signals the start of a new segment. + */ private final Predicate splitter; + /** + * If {@code true} the spliter character is preserved as part of the subsequent section otherwise, + * the spliter character is discarded. + */ private final boolean preserveSplit; + /** The function that converts segments into the String representation */ private final Function joiner; + /** A function to provide post-processing on the joined string */ private final Function postProcess; + /** + * Constructs a StringCase + * @param name the name of the case. + * @param splitter the splitter to determine when to split a string. + * @param preserveSplit the preserveSplit flag. + * @param joiner the joiner to assemble the String from the segments. + */ public StringCase(String name, Predicate splitter, boolean preserveSplit, Function joiner) { this(name, splitter, preserveSplit, joiner, Function.identity()); } + /** + * Constructs a String case for the common cases where the delimiter is not preserved in the segments. + * @param name the name of the case. + * @param delimiter the delimter between segments. + */ public StringCase(String name, char delimiter) { this(name, (c) -> c == delimiter, false, simpleJoiner(delimiter)); } - public static Function simpleJoiner(char delimiter) { - return (s) -> String.join(String.valueOf(delimiter), (CharSequence[])Arrays.stream(s).filter(Objects::nonNull).toArray((x$0) -> new String[x$0])); - } - + /** + * Constructs a StingCase. + * @param name the name of the string case. + * @param splitter the splitter to detect segments. + * @param preserveSplit the flag to preserve the splitter character. + * @param joiner the joiner to assemble a String from segments. + * @param postProcess the post-process applied to the string from the joiner. + */ public StringCase(String name, Predicate splitter, boolean preserveSplit, Function joiner, Function postProcess) { this.name = name; this.splitter = splitter; @@ -102,14 +167,35 @@ public StringCase(String name, Predicate splitter, boolean preserveSp this.postProcess = postProcess; } + /** + * A simple joiner that assembles a String from a collection of segments. + * Correctly handles the case where there are zero length segments. + * @param delimiter the delimiter to use between the segments. + * @return the assembled string. + */ + public static Function simpleJoiner(char delimiter) { + return (s) -> String.join(String.valueOf(delimiter), (CharSequence[])Arrays.stream(s).filter(Objects::nonNull).toArray(String[]::new)); + } + + @Override public String toString() { return this.name; } + /** + * Assembles segments into a String. + * @param segments the segments to assemble. + * @return the complete String. + */ public String assemble(String[] segments) { return (String)this.joiner.apply(segments); } + /** + * Parses a String into segments. + * @param string the string to parse + * @return the segments from the string. + */ public String[] getSegments(String string) { if (string == null) { return NULL_SEGMENT; @@ -135,7 +221,7 @@ public String[] getSegments(String string) { lst.add(sb.toString()); } - return (String[])lst.stream().map(this.postProcess).filter(Objects::nonNull).toArray((x$0) -> new String[x$0]); + return (String[])lst.stream().map(this.postProcess).filter(Objects::nonNull).toArray(String[]::new); } } diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java new file mode 100644 index 000000000..86baa93e8 --- /dev/null +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java @@ -0,0 +1,211 @@ +package org.apache.rat.utils; + +import org.apache.commons.text.WordUtils; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Predicate; + +public class CasedStringTests { + + + @Test + + + /** + * Handles converting from one string case to another (e.g. camel case to snake case). + * @since 0.17 + */ + public class CasedString { + /** the string of the cased format. */ + private final String[] parts; + /** the case of the string. */ + private final org.apache.rat.utils.CasedString.StringCase stringCase; + + /** + * A method to join pascal string fragments together. + */ + private static final Function PASCAL_JOINER = strings -> { + StringBuilder sb = new StringBuilder(); + Arrays.stream(strings).forEach(token -> sb.append(WordUtils.capitalize(token.toLowerCase(Locale.ROOT)))); + return sb.toString(); + }; + + + /** + * An enumeration of supported string cases. These cases tag strings as having a specific format. + */ + public enum StringCase { + /** + * Camel case tags strings like 'CamelCase' or 'camelCase'. This conversion forces the first character to + * lower case. If the first character is desired to be upper case use PASCAL case instead. + */ + CAMEL(Character::isUpperCase, true, PASCAL_JOINER.andThen(WordUtils::uncapitalize)), + /** + * Camel case tags strings like 'PascalCase' or 'pascalCase'. This conversion forces the first character to + * upper case. If the first character is desired to be lower case use CAMEL case instead. + */ + PASCAL(Character::isUpperCase, true, PASCAL_JOINER), + /** + * Snake case tags strings like 'Snake_Case'. This conversion does not change the capitalization of any characters + * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, + * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. + */ + SNAKE(c -> c == '_', false, a -> String.join("_", a)), + /** + * Kebab case tags strings like 'kebab-case'. This conversion does not change the capitalization of any characters + * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, + * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. + */ + KEBAB(c -> c == '-', false, a -> String.join("-", a)), + + /** + * Phrase case tags phrases of words like 'phrase case'. This conversion does not change the capitalization of any characters + * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, + * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. + */ + PHRASE(Character::isWhitespace, false, a -> String.join(" ", a)), + + /** + * Dot case tags phrases of words like 'phrase.case'. This conversion does not change the capitalization of any characters + * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, + * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. + */ + DOT(c -> c == '.', false, a -> String.join(".", a)), + + /** + * Slash case tags phrases of words like 'phrase.case'. This conversion does not change the capitalization of any characters + * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, + * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. + */ + SLASH(c -> c == '/', false, a -> String.join("/", a)); + + + /** The segment value for a null string */ + private static final String[] NULL_SEGMENT = new String[0]; + /** The segment value for an empty string */ + private static final String[] EMPTY_SEGMENT = {""}; + + /** test for split position character. */ + private final Predicate splitter; + /** if {@code true} split position character will be preserved in following segment. */ + private final boolean preserveSplit; + /** a function to joining the segments into this case type. */ + private final Function joiner; + + /** + * Defines a String Case. + * @param splitter The predicate that determines when a new word in the cased string begins. + * @param preserveSplit if {@code true} the character that the splitter detected is preserved as the first character of the new word. + * @param joiner The function to merge a list of strings into the cased String. + */ + StringCase(final Predicate splitter, final boolean preserveSplit, final Function joiner) { + this.splitter = splitter; + this.preserveSplit = preserveSplit; + this.joiner = joiner; + } + + /** + * Creates a cased string from a collection of segments. + * @param segments the segments to create the CasedString from. + * @return a CasedString + */ + public String assemble(final String[] segments) { + return segments.length == 0 ? null : this.joiner.apply(segments); + } + + /** + * Returns an array of each of the segments in this CasedString. Segments are defined as the strings between + * the separators in the CasedString. For the CAMEL case the segments are determined by the presence of a capital letter. + * @return the array of Strings that are segments of the cased string. + */ + public String[] getSegments(final String string) { + if (string == null) { + return NULL_SEGMENT; + } + if (string.isEmpty()) { + return EMPTY_SEGMENT; + } + List lst = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + for (char c : string.toCharArray()) { + if (splitter.test(c)) { + if (!sb.isEmpty()) { + lst.add(sb.toString()); + sb.setLength(0); + } + if (preserveSplit) { + sb.append(c); + } + } else { + sb.append(c); + } + } + if (!sb.isEmpty()) { + lst.add(sb.toString()); + } + return lst.toArray(new String[0]); + } + } + + /** + * A representation of a cased string and the identified case of that string. + * @param stringCase The {@code StringCase} that the {@code string} argument is in. + * @param string The string. + */ + public CasedString(final org.apache.rat.utils.CasedString.StringCase stringCase, final String string) { + this.parts = string == null ? org.apache.rat.utils.CasedString.StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); + this.stringCase = stringCase; + } + + /** + * A representation of a cased string and the identified case of that string. + * @param stringCase The {@code StringCase} that the {@code string} argument is in. + * @param parts The string parts. + */ + public CasedString(final org.apache.rat.utils.CasedString.StringCase stringCase, final String[] parts) { + this.parts = parts; + this.stringCase = stringCase; + } + + public org.apache.rat.utils.CasedString as(final org.apache.rat.utils.CasedString.StringCase stringCase) { + if (stringCase == this.stringCase) { + return this; + } + return new org.apache.rat.utils.CasedString(stringCase, Arrays.copyOf(this.parts, this.parts.length)); + } + + /** + * Returns an array of each of the segments in this CasedString. Segments are defined as the strings between + * the separators in the CasedString. For the CAMEL case the segments are determined by the presence of a capital letter. + * @return the array of Strings that are segments of the cased string. + */ + public String[] getSegments() { + return parts; + } + + /** + * Converts this cased string into a {@code String} of another format. + * The upper/lower case of the characters within the string are not modified. + * @param stringCase The format to convert to. + * @return the String current string represented in the new format. + */ + public String toCase(final org.apache.rat.utils.CasedString.StringCase stringCase) { + return parts == org.apache.rat.utils.CasedString.StringCase.NULL_SEGMENT ? null : stringCase.joiner.apply(getSegments()); + } + + /** + * Returns the string representation provided in the constructor. + * @return the string representation. + */ + @Override + public String toString() { + return toCase(stringCase); + } + } + +} From 2e23fa02a816e31cb7e1dc892b1d650d177b1fb2 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 12:14:10 +0000 Subject: [PATCH 04/11] Update CasedStringTests.java --- .../apache/rat/utils/CasedStringTests.java | 395 +++++++++--------- 1 file changed, 197 insertions(+), 198 deletions(-) diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java index 86baa93e8..b493512a7 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java @@ -10,202 +10,201 @@ import java.util.function.Function; import java.util.function.Predicate; -public class CasedStringTests { - - - @Test - - - /** - * Handles converting from one string case to another (e.g. camel case to snake case). - * @since 0.17 - */ - public class CasedString { - /** the string of the cased format. */ - private final String[] parts; - /** the case of the string. */ - private final org.apache.rat.utils.CasedString.StringCase stringCase; - - /** - * A method to join pascal string fragments together. - */ - private static final Function PASCAL_JOINER = strings -> { - StringBuilder sb = new StringBuilder(); - Arrays.stream(strings).forEach(token -> sb.append(WordUtils.capitalize(token.toLowerCase(Locale.ROOT)))); - return sb.toString(); - }; - - - /** - * An enumeration of supported string cases. These cases tag strings as having a specific format. - */ - public enum StringCase { - /** - * Camel case tags strings like 'CamelCase' or 'camelCase'. This conversion forces the first character to - * lower case. If the first character is desired to be upper case use PASCAL case instead. - */ - CAMEL(Character::isUpperCase, true, PASCAL_JOINER.andThen(WordUtils::uncapitalize)), - /** - * Camel case tags strings like 'PascalCase' or 'pascalCase'. This conversion forces the first character to - * upper case. If the first character is desired to be lower case use CAMEL case instead. - */ - PASCAL(Character::isUpperCase, true, PASCAL_JOINER), - /** - * Snake case tags strings like 'Snake_Case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - SNAKE(c -> c == '_', false, a -> String.join("_", a)), - /** - * Kebab case tags strings like 'kebab-case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - KEBAB(c -> c == '-', false, a -> String.join("-", a)), - - /** - * Phrase case tags phrases of words like 'phrase case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - PHRASE(Character::isWhitespace, false, a -> String.join(" ", a)), - - /** - * Dot case tags phrases of words like 'phrase.case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - DOT(c -> c == '.', false, a -> String.join(".", a)), - - /** - * Slash case tags phrases of words like 'phrase.case'. This conversion does not change the capitalization of any characters - * in the string. If specific capitalization is required use {@link String#toUpperCase()}, {@link String#toLowerCase()}, - * * or the commons-text methods {@link WordUtils#capitalize(String)}, or {@link WordUtils#uncapitalize(String)} as required. - */ - SLASH(c -> c == '/', false, a -> String.join("/", a)); - - - /** The segment value for a null string */ - private static final String[] NULL_SEGMENT = new String[0]; - /** The segment value for an empty string */ - private static final String[] EMPTY_SEGMENT = {""}; - - /** test for split position character. */ - private final Predicate splitter; - /** if {@code true} split position character will be preserved in following segment. */ - private final boolean preserveSplit; - /** a function to joining the segments into this case type. */ - private final Function joiner; - - /** - * Defines a String Case. - * @param splitter The predicate that determines when a new word in the cased string begins. - * @param preserveSplit if {@code true} the character that the splitter detected is preserved as the first character of the new word. - * @param joiner The function to merge a list of strings into the cased String. - */ - StringCase(final Predicate splitter, final boolean preserveSplit, final Function joiner) { - this.splitter = splitter; - this.preserveSplit = preserveSplit; - this.joiner = joiner; - } - - /** - * Creates a cased string from a collection of segments. - * @param segments the segments to create the CasedString from. - * @return a CasedString - */ - public String assemble(final String[] segments) { - return segments.length == 0 ? null : this.joiner.apply(segments); - } - - /** - * Returns an array of each of the segments in this CasedString. Segments are defined as the strings between - * the separators in the CasedString. For the CAMEL case the segments are determined by the presence of a capital letter. - * @return the array of Strings that are segments of the cased string. - */ - public String[] getSegments(final String string) { - if (string == null) { - return NULL_SEGMENT; - } - if (string.isEmpty()) { - return EMPTY_SEGMENT; - } - List lst = new ArrayList<>(); - StringBuilder sb = new StringBuilder(); - for (char c : string.toCharArray()) { - if (splitter.test(c)) { - if (!sb.isEmpty()) { - lst.add(sb.toString()); - sb.setLength(0); - } - if (preserveSplit) { - sb.append(c); - } - } else { - sb.append(c); - } - } - if (!sb.isEmpty()) { - lst.add(sb.toString()); - } - return lst.toArray(new String[0]); - } - } - - /** - * A representation of a cased string and the identified case of that string. - * @param stringCase The {@code StringCase} that the {@code string} argument is in. - * @param string The string. - */ - public CasedString(final org.apache.rat.utils.CasedString.StringCase stringCase, final String string) { - this.parts = string == null ? org.apache.rat.utils.CasedString.StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); - this.stringCase = stringCase; - } - - /** - * A representation of a cased string and the identified case of that string. - * @param stringCase The {@code StringCase} that the {@code string} argument is in. - * @param parts The string parts. - */ - public CasedString(final org.apache.rat.utils.CasedString.StringCase stringCase, final String[] parts) { - this.parts = parts; - this.stringCase = stringCase; - } - - public org.apache.rat.utils.CasedString as(final org.apache.rat.utils.CasedString.StringCase stringCase) { - if (stringCase == this.stringCase) { - return this; - } - return new org.apache.rat.utils.CasedString(stringCase, Arrays.copyOf(this.parts, this.parts.length)); - } - - /** - * Returns an array of each of the segments in this CasedString. Segments are defined as the strings between - * the separators in the CasedString. For the CAMEL case the segments are determined by the presence of a capital letter. - * @return the array of Strings that are segments of the cased string. - */ - public String[] getSegments() { - return parts; - } - - /** - * Converts this cased string into a {@code String} of another format. - * The upper/lower case of the characters within the string are not modified. - * @param stringCase The format to convert to. - * @return the String current string represented in the new format. - */ - public String toCase(final org.apache.rat.utils.CasedString.StringCase stringCase) { - return parts == org.apache.rat.utils.CasedString.StringCase.NULL_SEGMENT ? null : stringCase.joiner.apply(getSegments()); - } - - /** - * Returns the string representation provided in the constructor. - * @return the string representation. - */ - @Override - public String toString() { - return toCase(stringCase); - } - } - +public class CasedStringTest { + + @MethodSource("testSegmentationData") + @ParameterizedTest + void testSegmentation(String pattern, CasedString.StringCase stringCase, String[] expected) { + CasedString casedString = new CasedString(stringCase, pattern); + assertThat(casedString.getSegments()).isEqualTo(expected); + } + + static Stream testSegmentationData() { + List lst = new ArrayList<>(); + lst.add(Arguments.of("CamelCase", CasedString.StringCase.CAMEL, new String[]{"Camel", "Case"})); + lst.add(Arguments.of("CamelPMDCase", CasedString.StringCase.CAMEL, + new String[]{"Camel", "P", "M", "D", "Case"})); + lst.add(Arguments.of("camelCase", CasedString.StringCase.CAMEL, new String[]{"camel", "Case"})); + lst.add(Arguments.of("camelPMDCase", CasedString.StringCase.CAMEL, + new String[]{"camel", "P", "M", "D", "Case"})); + lst.add(Arguments.of("PascalCase", CasedString.StringCase.PASCAL, new String[]{"Pascal", "Case"})); + lst.add(Arguments.of("PascalPMDCase", CasedString.StringCase.PASCAL, + new String[]{"Pascal", "P", "M", "D", "Case"})); + lst.add(Arguments.of("pascalCase", CasedString.StringCase.PASCAL, new String[]{"pascal", "Case"})); + lst.add(Arguments.of("pascalPMDCase", CasedString.StringCase.PASCAL, + new String[]{"pascal", "P", "M", "D", "Case"})); + lst.add(Arguments.of("snake_case", CasedString.StringCase.SNAKE, new String[]{"snake", "case"})); + lst.add(Arguments.of("snake_Case", CasedString.StringCase.SNAKE, new String[]{"snake", "Case"})); + lst.add(Arguments.of("snake__Case", CasedString.StringCase.SNAKE, new String[]{"snake", "", "Case"})); + lst.add(Arguments.of("kebab-case", CasedString.StringCase.KEBAB, new String[]{"kebab", "case"})); + lst.add(Arguments.of("kebab-Case", CasedString.StringCase.KEBAB, new String[]{"kebab", "Case"})); + lst.add(Arguments.of("kebab--case", CasedString.StringCase.KEBAB, new String[]{"kebab", "", "case"})); + lst.add(Arguments.of("phrase case", CasedString.StringCase.PHRASE, new String[]{"phrase", "case"})); + lst.add(Arguments.of("phrase Case", CasedString.StringCase.PHRASE, new String[]{"phrase", "Case"})); + lst.add(Arguments.of("phrase case", CasedString.StringCase.PHRASE, new String[]{"phrase", "", "case"})); + lst.add(Arguments.of("dot.case", CasedString.StringCase.DOT, new String[]{"dot", "case"})); + lst.add(Arguments.of("dot..case", CasedString.StringCase.DOT, new String[]{"dot", "", "case"})); + lst.add(Arguments.of("dot.Case", CasedString.StringCase.DOT, new String[]{"dot", "Case"})); + return lst.stream(); + } + + @MethodSource("testToCaseData") + @ParameterizedTest(name = "{index} {0} {1}") + void testToCase(CasedString casedString, CasedString.StringCase stringCase, String expected) { + assertThat(casedString.toCase(stringCase)).isEqualTo(expected); + } + + static Stream testToCaseData() { + List lst = new ArrayList<>(); + + CasedString underTest = new CasedString(CasedString.StringCase.CAMEL, "camelCase"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "camelCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "camel_Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "camel-Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "camel Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "camel.Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "CamelCase")); + + underTest = new CasedString(CasedString.StringCase.SNAKE, "snake_case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "snakeCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "snake_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "snake-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "snake case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "snake.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "SnakeCase")); + + underTest = new CasedString(CasedString.StringCase.KEBAB, "kebab-case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "kebabCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "kebab_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "kebab-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "kebab case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "kebab.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "KebabCase")); + + underTest = new CasedString(CasedString.StringCase.PHRASE, "phrase case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "phraseCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "phrase_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "phrase-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "phrase case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "phrase.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "PhraseCase")); + + underTest = new CasedString(CasedString.StringCase.DOT, "dot.case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "dotCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "dot_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "dot-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "dot case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "dot.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "DotCase")); + + underTest = new CasedString(CasedString.StringCase.PASCAL, "PascalCase"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "pascalCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "Pascal_Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "Pascal-Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "Pascal Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "Pascal.Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "PascalCase")); + + underTest = new CasedString(CasedString.StringCase.DOT, "one..two"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "oneTwo")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "one__two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "one--two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "one two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "one..two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "OneTwo")); + + return lst.stream(); + } + + @MethodSource("testAssembleData") + @ParameterizedTest(name = "{index} {0} {1}") + void testAssemble(CasedString.StringCase underTest, String[] data, String expected) { + assertThat(underTest.assemble(data)).isEqualTo(expected); + } + + static Stream testAssembleData() { + List lst = new ArrayList<>(); + String[] emptyFirst = {"", "one", "two"}; + String[] emptyMiddle = {"one", "", "two"}; + String[] emptyEnd = {"one", "two", ""}; + String[] nullFirst = {null, "one", "two"}; + String[] nullMiddle = {"one", null, "two"}; + String[] nullEnd = {"one", "two", null}; + String[] doubleEmpty = {"one", "", "", "two"}; + String[] doubleNull = {"one", null, null, "two"}; + + CasedString.StringCase underTest = CasedString.StringCase.CAMEL; + lst.add(Arguments.of(underTest, emptyFirst, "oneTwo")); + lst.add(Arguments.of(underTest, emptyMiddle, "oneTwo")); + lst.add(Arguments.of(underTest, emptyEnd, "oneTwo")); + lst.add(Arguments.of(underTest, nullFirst, "oneTwo")); + lst.add(Arguments.of(underTest, nullMiddle, "oneTwo")); + lst.add(Arguments.of(underTest, nullEnd, "oneTwo")); + lst.add(Arguments.of(underTest, doubleEmpty, "oneTwo")); + lst.add(Arguments.of(underTest, doubleNull, "oneTwo")); + + underTest = CasedString.StringCase.PASCAL; + lst.add(Arguments.of(underTest, emptyFirst, "OneTwo")); + lst.add(Arguments.of(underTest, emptyMiddle, "OneTwo")); + lst.add(Arguments.of(underTest, emptyEnd, "OneTwo")); + lst.add(Arguments.of(underTest, nullFirst, "OneTwo")); + lst.add(Arguments.of(underTest, nullMiddle, "OneTwo")); + lst.add(Arguments.of(underTest, nullEnd, "OneTwo")); + lst.add(Arguments.of(underTest, doubleEmpty, "OneTwo")); + lst.add(Arguments.of(underTest, doubleNull, "OneTwo")); + + underTest = CasedString.StringCase.SNAKE; + lst.add(Arguments.of(underTest, emptyFirst, "_one_two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one__two")); + lst.add(Arguments.of(underTest, emptyEnd, "one_two_")); + lst.add(Arguments.of(underTest, nullFirst, "one_two")); + lst.add(Arguments.of(underTest, nullMiddle, "one_two")); + lst.add(Arguments.of(underTest, nullEnd, "one_two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one___two")); + lst.add(Arguments.of(underTest, doubleNull, "one_two")); + + underTest = CasedString.StringCase.KEBAB; + lst.add(Arguments.of(underTest, emptyFirst, "-one-two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one--two")); + lst.add(Arguments.of(underTest, emptyEnd, "one-two-")); + lst.add(Arguments.of(underTest, nullFirst, "one-two")); + lst.add(Arguments.of(underTest, nullMiddle, "one-two")); + lst.add(Arguments.of(underTest, nullEnd, "one-two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one---two")); + lst.add(Arguments.of(underTest, doubleNull, "one-two")); + + underTest = CasedString.StringCase.PHRASE; + lst.add(Arguments.of(underTest, emptyFirst, " one two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one two")); + lst.add(Arguments.of(underTest, emptyEnd, "one two ")); + lst.add(Arguments.of(underTest, nullFirst, "one two")); + lst.add(Arguments.of(underTest, nullMiddle, "one two")); + lst.add(Arguments.of(underTest, nullEnd, "one two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one two")); + lst.add(Arguments.of(underTest, doubleNull, "one two")); + + underTest = CasedString.StringCase.DOT; + lst.add(Arguments.of(underTest, emptyFirst, ".one.two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one..two")); + lst.add(Arguments.of(underTest, emptyEnd, "one.two.")); + lst.add(Arguments.of(underTest, nullFirst, "one.two")); + lst.add(Arguments.of(underTest, nullMiddle, "one.two")); + lst.add(Arguments.of(underTest, nullEnd, "one.two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one...two")); + lst.add(Arguments.of(underTest, doubleNull, "one.two")); + + underTest = CasedString.StringCase.SLASH; + lst.add(Arguments.of(underTest, emptyFirst, "/one/two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one//two")); + lst.add(Arguments.of(underTest, emptyEnd, "one/two/")); + lst.add(Arguments.of(underTest, nullFirst, "one/two")); + lst.add(Arguments.of(underTest, nullMiddle, "one/two")); + lst.add(Arguments.of(underTest, nullEnd, "one/two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one///two")); + lst.add(Arguments.of(underTest, doubleNull, "one/two")); + + return lst.stream(); + } } + + From f06136608039c4b46c4ba26644978c231d0a6263 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 12:16:57 +0000 Subject: [PATCH 05/11] updated tests --- .../org/apache/rat/utils/CasedStringTests.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java index b493512a7..228371683 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java @@ -1,16 +1,16 @@ package org.apache.rat.utils; -import org.apache.commons.text.WordUtils; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Locale; -import java.util.function.Function; -import java.util.function.Predicate; +import java.util.stream.Stream; -public class CasedStringTest { +import static org.assertj.core.api.Assertions.assertThat; + +public class CasedStringTests { @MethodSource("testSegmentationData") @ParameterizedTest @@ -206,5 +206,3 @@ static Stream testAssembleData() { return lst.stream(); } } - - From 9e4878667b5933105c1cd1c3ade4b0b5520de2ab Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 15:08:54 +0000 Subject: [PATCH 06/11] updated tests --- .../apache/rat/utils/CasedStringTests.java | 386 +++++++++--------- 1 file changed, 193 insertions(+), 193 deletions(-) diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java index 228371683..2dd184740 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java @@ -12,197 +12,197 @@ public class CasedStringTests { - @MethodSource("testSegmentationData") - @ParameterizedTest - void testSegmentation(String pattern, CasedString.StringCase stringCase, String[] expected) { - CasedString casedString = new CasedString(stringCase, pattern); - assertThat(casedString.getSegments()).isEqualTo(expected); - } - - static Stream testSegmentationData() { - List lst = new ArrayList<>(); - lst.add(Arguments.of("CamelCase", CasedString.StringCase.CAMEL, new String[]{"Camel", "Case"})); - lst.add(Arguments.of("CamelPMDCase", CasedString.StringCase.CAMEL, - new String[]{"Camel", "P", "M", "D", "Case"})); - lst.add(Arguments.of("camelCase", CasedString.StringCase.CAMEL, new String[]{"camel", "Case"})); - lst.add(Arguments.of("camelPMDCase", CasedString.StringCase.CAMEL, - new String[]{"camel", "P", "M", "D", "Case"})); - lst.add(Arguments.of("PascalCase", CasedString.StringCase.PASCAL, new String[]{"Pascal", "Case"})); - lst.add(Arguments.of("PascalPMDCase", CasedString.StringCase.PASCAL, - new String[]{"Pascal", "P", "M", "D", "Case"})); - lst.add(Arguments.of("pascalCase", CasedString.StringCase.PASCAL, new String[]{"pascal", "Case"})); - lst.add(Arguments.of("pascalPMDCase", CasedString.StringCase.PASCAL, - new String[]{"pascal", "P", "M", "D", "Case"})); - lst.add(Arguments.of("snake_case", CasedString.StringCase.SNAKE, new String[]{"snake", "case"})); - lst.add(Arguments.of("snake_Case", CasedString.StringCase.SNAKE, new String[]{"snake", "Case"})); - lst.add(Arguments.of("snake__Case", CasedString.StringCase.SNAKE, new String[]{"snake", "", "Case"})); - lst.add(Arguments.of("kebab-case", CasedString.StringCase.KEBAB, new String[]{"kebab", "case"})); - lst.add(Arguments.of("kebab-Case", CasedString.StringCase.KEBAB, new String[]{"kebab", "Case"})); - lst.add(Arguments.of("kebab--case", CasedString.StringCase.KEBAB, new String[]{"kebab", "", "case"})); - lst.add(Arguments.of("phrase case", CasedString.StringCase.PHRASE, new String[]{"phrase", "case"})); - lst.add(Arguments.of("phrase Case", CasedString.StringCase.PHRASE, new String[]{"phrase", "Case"})); - lst.add(Arguments.of("phrase case", CasedString.StringCase.PHRASE, new String[]{"phrase", "", "case"})); - lst.add(Arguments.of("dot.case", CasedString.StringCase.DOT, new String[]{"dot", "case"})); - lst.add(Arguments.of("dot..case", CasedString.StringCase.DOT, new String[]{"dot", "", "case"})); - lst.add(Arguments.of("dot.Case", CasedString.StringCase.DOT, new String[]{"dot", "Case"})); - return lst.stream(); - } - - @MethodSource("testToCaseData") - @ParameterizedTest(name = "{index} {0} {1}") - void testToCase(CasedString casedString, CasedString.StringCase stringCase, String expected) { - assertThat(casedString.toCase(stringCase)).isEqualTo(expected); - } - - static Stream testToCaseData() { - List lst = new ArrayList<>(); - - CasedString underTest = new CasedString(CasedString.StringCase.CAMEL, "camelCase"); - lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "camelCase")); - lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "camel_Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "camel-Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "camel Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "camel.Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "CamelCase")); - - underTest = new CasedString(CasedString.StringCase.SNAKE, "snake_case"); - lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "snakeCase")); - lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "snake_case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "snake-case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "snake case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "snake.case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "SnakeCase")); - - underTest = new CasedString(CasedString.StringCase.KEBAB, "kebab-case"); - lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "kebabCase")); - lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "kebab_case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "kebab-case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "kebab case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "kebab.case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "KebabCase")); - - underTest = new CasedString(CasedString.StringCase.PHRASE, "phrase case"); - lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "phraseCase")); - lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "phrase_case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "phrase-case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "phrase case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "phrase.case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "PhraseCase")); - - underTest = new CasedString(CasedString.StringCase.DOT, "dot.case"); - lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "dotCase")); - lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "dot_case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "dot-case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "dot case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "dot.case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "DotCase")); - - underTest = new CasedString(CasedString.StringCase.PASCAL, "PascalCase"); - lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "pascalCase")); - lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "Pascal_Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "Pascal-Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "Pascal Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "Pascal.Case")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "PascalCase")); - - underTest = new CasedString(CasedString.StringCase.DOT, "one..two"); - lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "oneTwo")); - lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "one__two")); - lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "one--two")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "one two")); - lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "one..two")); - lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "OneTwo")); - - return lst.stream(); - } - - @MethodSource("testAssembleData") - @ParameterizedTest(name = "{index} {0} {1}") - void testAssemble(CasedString.StringCase underTest, String[] data, String expected) { - assertThat(underTest.assemble(data)).isEqualTo(expected); - } - - static Stream testAssembleData() { - List lst = new ArrayList<>(); - String[] emptyFirst = {"", "one", "two"}; - String[] emptyMiddle = {"one", "", "two"}; - String[] emptyEnd = {"one", "two", ""}; - String[] nullFirst = {null, "one", "two"}; - String[] nullMiddle = {"one", null, "two"}; - String[] nullEnd = {"one", "two", null}; - String[] doubleEmpty = {"one", "", "", "two"}; - String[] doubleNull = {"one", null, null, "two"}; - - CasedString.StringCase underTest = CasedString.StringCase.CAMEL; - lst.add(Arguments.of(underTest, emptyFirst, "oneTwo")); - lst.add(Arguments.of(underTest, emptyMiddle, "oneTwo")); - lst.add(Arguments.of(underTest, emptyEnd, "oneTwo")); - lst.add(Arguments.of(underTest, nullFirst, "oneTwo")); - lst.add(Arguments.of(underTest, nullMiddle, "oneTwo")); - lst.add(Arguments.of(underTest, nullEnd, "oneTwo")); - lst.add(Arguments.of(underTest, doubleEmpty, "oneTwo")); - lst.add(Arguments.of(underTest, doubleNull, "oneTwo")); - - underTest = CasedString.StringCase.PASCAL; - lst.add(Arguments.of(underTest, emptyFirst, "OneTwo")); - lst.add(Arguments.of(underTest, emptyMiddle, "OneTwo")); - lst.add(Arguments.of(underTest, emptyEnd, "OneTwo")); - lst.add(Arguments.of(underTest, nullFirst, "OneTwo")); - lst.add(Arguments.of(underTest, nullMiddle, "OneTwo")); - lst.add(Arguments.of(underTest, nullEnd, "OneTwo")); - lst.add(Arguments.of(underTest, doubleEmpty, "OneTwo")); - lst.add(Arguments.of(underTest, doubleNull, "OneTwo")); - - underTest = CasedString.StringCase.SNAKE; - lst.add(Arguments.of(underTest, emptyFirst, "_one_two")); - lst.add(Arguments.of(underTest, emptyMiddle, "one__two")); - lst.add(Arguments.of(underTest, emptyEnd, "one_two_")); - lst.add(Arguments.of(underTest, nullFirst, "one_two")); - lst.add(Arguments.of(underTest, nullMiddle, "one_two")); - lst.add(Arguments.of(underTest, nullEnd, "one_two")); - lst.add(Arguments.of(underTest, doubleEmpty, "one___two")); - lst.add(Arguments.of(underTest, doubleNull, "one_two")); - - underTest = CasedString.StringCase.KEBAB; - lst.add(Arguments.of(underTest, emptyFirst, "-one-two")); - lst.add(Arguments.of(underTest, emptyMiddle, "one--two")); - lst.add(Arguments.of(underTest, emptyEnd, "one-two-")); - lst.add(Arguments.of(underTest, nullFirst, "one-two")); - lst.add(Arguments.of(underTest, nullMiddle, "one-two")); - lst.add(Arguments.of(underTest, nullEnd, "one-two")); - lst.add(Arguments.of(underTest, doubleEmpty, "one---two")); - lst.add(Arguments.of(underTest, doubleNull, "one-two")); - - underTest = CasedString.StringCase.PHRASE; - lst.add(Arguments.of(underTest, emptyFirst, " one two")); - lst.add(Arguments.of(underTest, emptyMiddle, "one two")); - lst.add(Arguments.of(underTest, emptyEnd, "one two ")); - lst.add(Arguments.of(underTest, nullFirst, "one two")); - lst.add(Arguments.of(underTest, nullMiddle, "one two")); - lst.add(Arguments.of(underTest, nullEnd, "one two")); - lst.add(Arguments.of(underTest, doubleEmpty, "one two")); - lst.add(Arguments.of(underTest, doubleNull, "one two")); - - underTest = CasedString.StringCase.DOT; - lst.add(Arguments.of(underTest, emptyFirst, ".one.two")); - lst.add(Arguments.of(underTest, emptyMiddle, "one..two")); - lst.add(Arguments.of(underTest, emptyEnd, "one.two.")); - lst.add(Arguments.of(underTest, nullFirst, "one.two")); - lst.add(Arguments.of(underTest, nullMiddle, "one.two")); - lst.add(Arguments.of(underTest, nullEnd, "one.two")); - lst.add(Arguments.of(underTest, doubleEmpty, "one...two")); - lst.add(Arguments.of(underTest, doubleNull, "one.two")); - - underTest = CasedString.StringCase.SLASH; - lst.add(Arguments.of(underTest, emptyFirst, "/one/two")); - lst.add(Arguments.of(underTest, emptyMiddle, "one//two")); - lst.add(Arguments.of(underTest, emptyEnd, "one/two/")); - lst.add(Arguments.of(underTest, nullFirst, "one/two")); - lst.add(Arguments.of(underTest, nullMiddle, "one/two")); - lst.add(Arguments.of(underTest, nullEnd, "one/two")); - lst.add(Arguments.of(underTest, doubleEmpty, "one///two")); - lst.add(Arguments.of(underTest, doubleNull, "one/two")); - - return lst.stream(); - } + @MethodSource("testSegmentationData") + @ParameterizedTest + void testSegmentation(String pattern, CasedString.StringCase stringCase, String[] expected) { + CasedString casedString = new CasedString(stringCase, pattern); + assertThat(casedString.getSegments()).isEqualTo(expected); + } + + static Stream testSegmentationData() { + List lst = new ArrayList<>(); + lst.add(Arguments.of("CamelCase", CasedString.StringCase.CAMEL, new String[]{"Camel", "Case"})); + lst.add(Arguments.of("CamelPMDCase", CasedString.StringCase.CAMEL, + new String[]{"Camel", "P", "M", "D", "Case"})); + lst.add(Arguments.of("camelCase", CasedString.StringCase.CAMEL, new String[]{"camel", "Case"})); + lst.add(Arguments.of("camelPMDCase", CasedString.StringCase.CAMEL, + new String[]{"camel", "P", "M", "D", "Case"})); + lst.add(Arguments.of("PascalCase", CasedString.StringCase.PASCAL, new String[]{"Pascal", "Case"})); + lst.add(Arguments.of("PascalPMDCase", CasedString.StringCase.PASCAL, + new String[]{"Pascal", "P", "M", "D", "Case"})); + lst.add(Arguments.of("pascalCase", CasedString.StringCase.PASCAL, new String[]{"pascal", "Case"})); + lst.add(Arguments.of("pascalPMDCase", CasedString.StringCase.PASCAL, + new String[]{"pascal", "P", "M", "D", "Case"})); + lst.add(Arguments.of("snake_case", CasedString.StringCase.SNAKE, new String[]{"snake", "case"})); + lst.add(Arguments.of("snake_Case", CasedString.StringCase.SNAKE, new String[]{"snake", "Case"})); + lst.add(Arguments.of("snake__Case", CasedString.StringCase.SNAKE, new String[]{"snake", "", "Case"})); + lst.add(Arguments.of("kebab-case", CasedString.StringCase.KEBAB, new String[]{"kebab", "case"})); + lst.add(Arguments.of("kebab-Case", CasedString.StringCase.KEBAB, new String[]{"kebab", "Case"})); + lst.add(Arguments.of("kebab--case", CasedString.StringCase.KEBAB, new String[]{"kebab", "", "case"})); + lst.add(Arguments.of("phrase case", CasedString.StringCase.PHRASE, new String[]{"phrase", "case"})); + lst.add(Arguments.of("phrase Case", CasedString.StringCase.PHRASE, new String[]{"phrase", "Case"})); + lst.add(Arguments.of("phrase case", CasedString.StringCase.PHRASE, new String[]{"phrase", "", "case"})); + lst.add(Arguments.of("dot.case", CasedString.StringCase.DOT, new String[]{"dot", "case"})); + lst.add(Arguments.of("dot..case", CasedString.StringCase.DOT, new String[]{"dot", "", "case"})); + lst.add(Arguments.of("dot.Case", CasedString.StringCase.DOT, new String[]{"dot", "Case"})); + return lst.stream(); + } + + @MethodSource("testToCaseData") + @ParameterizedTest(name = "{index} {0} {1}") + void testToCase(CasedString casedString, CasedString.StringCase stringCase, String expected) { + assertThat(casedString.toCase(stringCase)).isEqualTo(expected); + } + + static Stream testToCaseData() { + List lst = new ArrayList<>(); + + CasedString underTest = new CasedString(CasedString.StringCase.CAMEL, "camelCase"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "camelCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "camel_Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "camel-Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "camel Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "camel.Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "CamelCase")); + + underTest = new CasedString(CasedString.StringCase.SNAKE, "snake_case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "snakeCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "snake_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "snake-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "snake case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "snake.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "SnakeCase")); + + underTest = new CasedString(CasedString.StringCase.KEBAB, "kebab-case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "kebabCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "kebab_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "kebab-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "kebab case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "kebab.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "KebabCase")); + + underTest = new CasedString(CasedString.StringCase.PHRASE, "phrase case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "phraseCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "phrase_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "phrase-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "phrase case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "phrase.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "PhraseCase")); + + underTest = new CasedString(CasedString.StringCase.DOT, "dot.case"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "dotCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "dot_case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "dot-case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "dot case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "dot.case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "DotCase")); + + underTest = new CasedString(CasedString.StringCase.PASCAL, "PascalCase"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "pascalCase")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "Pascal_Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "Pascal-Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "Pascal Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "Pascal.Case")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "PascalCase")); + + underTest = new CasedString(CasedString.StringCase.DOT, "one..two"); + lst.add(Arguments.of(underTest, CasedString.StringCase.CAMEL, "oneTwo")); + lst.add(Arguments.of(underTest, CasedString.StringCase.SNAKE, "one__two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.KEBAB, "one--two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PHRASE, "one two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.DOT, "one..two")); + lst.add(Arguments.of(underTest, CasedString.StringCase.PASCAL, "OneTwo")); + + return lst.stream(); + } + + @MethodSource("testAssembleData") + @ParameterizedTest(name = "{index} {0} {1}") + void testAssemble(CasedString.StringCase underTest, String[] data, String expected) { + assertThat(underTest.assemble(data)).isEqualTo(expected); + } + + static Stream testAssembleData() { + List lst = new ArrayList<>(); + String[] emptyFirst = {"", "one", "two"}; + String[] emptyMiddle = {"one", "", "two"}; + String[] emptyEnd = {"one", "two", ""}; + String[] nullFirst = {null, "one", "two"}; + String[] nullMiddle = {"one", null, "two"}; + String[] nullEnd = {"one", "two", null}; + String[] doubleEmpty = {"one", "", "", "two"}; + String[] doubleNull = {"one", null, null, "two"}; + + CasedString.StringCase underTest = CasedString.StringCase.CAMEL; + lst.add(Arguments.of(underTest, emptyFirst, "oneTwo")); + lst.add(Arguments.of(underTest, emptyMiddle, "oneTwo")); + lst.add(Arguments.of(underTest, emptyEnd, "oneTwo")); + lst.add(Arguments.of(underTest, nullFirst, "oneTwo")); + lst.add(Arguments.of(underTest, nullMiddle, "oneTwo")); + lst.add(Arguments.of(underTest, nullEnd, "oneTwo")); + lst.add(Arguments.of(underTest, doubleEmpty, "oneTwo")); + lst.add(Arguments.of(underTest, doubleNull, "oneTwo")); + + underTest = CasedString.StringCase.PASCAL; + lst.add(Arguments.of(underTest, emptyFirst, "OneTwo")); + lst.add(Arguments.of(underTest, emptyMiddle, "OneTwo")); + lst.add(Arguments.of(underTest, emptyEnd, "OneTwo")); + lst.add(Arguments.of(underTest, nullFirst, "OneTwo")); + lst.add(Arguments.of(underTest, nullMiddle, "OneTwo")); + lst.add(Arguments.of(underTest, nullEnd, "OneTwo")); + lst.add(Arguments.of(underTest, doubleEmpty, "OneTwo")); + lst.add(Arguments.of(underTest, doubleNull, "OneTwo")); + + underTest = CasedString.StringCase.SNAKE; + lst.add(Arguments.of(underTest, emptyFirst, "_one_two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one__two")); + lst.add(Arguments.of(underTest, emptyEnd, "one_two_")); + lst.add(Arguments.of(underTest, nullFirst, "one_two")); + lst.add(Arguments.of(underTest, nullMiddle, "one_two")); + lst.add(Arguments.of(underTest, nullEnd, "one_two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one___two")); + lst.add(Arguments.of(underTest, doubleNull, "one_two")); + + underTest = CasedString.StringCase.KEBAB; + lst.add(Arguments.of(underTest, emptyFirst, "-one-two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one--two")); + lst.add(Arguments.of(underTest, emptyEnd, "one-two-")); + lst.add(Arguments.of(underTest, nullFirst, "one-two")); + lst.add(Arguments.of(underTest, nullMiddle, "one-two")); + lst.add(Arguments.of(underTest, nullEnd, "one-two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one---two")); + lst.add(Arguments.of(underTest, doubleNull, "one-two")); + + underTest = CasedString.StringCase.PHRASE; + lst.add(Arguments.of(underTest, emptyFirst, " one two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one two")); + lst.add(Arguments.of(underTest, emptyEnd, "one two ")); + lst.add(Arguments.of(underTest, nullFirst, "one two")); + lst.add(Arguments.of(underTest, nullMiddle, "one two")); + lst.add(Arguments.of(underTest, nullEnd, "one two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one two")); + lst.add(Arguments.of(underTest, doubleNull, "one two")); + + underTest = CasedString.StringCase.DOT; + lst.add(Arguments.of(underTest, emptyFirst, ".one.two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one..two")); + lst.add(Arguments.of(underTest, emptyEnd, "one.two.")); + lst.add(Arguments.of(underTest, nullFirst, "one.two")); + lst.add(Arguments.of(underTest, nullMiddle, "one.two")); + lst.add(Arguments.of(underTest, nullEnd, "one.two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one...two")); + lst.add(Arguments.of(underTest, doubleNull, "one.two")); + + underTest = CasedString.StringCase.SLASH; + lst.add(Arguments.of(underTest, emptyFirst, "/one/two")); + lst.add(Arguments.of(underTest, emptyMiddle, "one//two")); + lst.add(Arguments.of(underTest, emptyEnd, "one/two/")); + lst.add(Arguments.of(underTest, nullFirst, "one/two")); + lst.add(Arguments.of(underTest, nullMiddle, "one/two")); + lst.add(Arguments.of(underTest, nullEnd, "one/two")); + lst.add(Arguments.of(underTest, doubleEmpty, "one///two")); + lst.add(Arguments.of(underTest, doubleNull, "one/two")); + + return lst.stream(); + } } From f8b1af907beda2fb1f59b64275bf67a599780e42 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 15:20:28 +0000 Subject: [PATCH 07/11] fixed license issue --- .../org/apache/rat/utils/CasedStringTests.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java index 2dd184740..29e4f72f8 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java @@ -1,3 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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.apache.rat.utils; import org.junit.jupiter.params.ParameterizedTest; From 2c1a9ac346e0af0c6e00cdd2237130051fcf3fb2 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 15:59:38 +0000 Subject: [PATCH 08/11] fixed CasedString checkstyle issues --- .../org/apache/rat/utils/CasedString.java | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java index 572fc91aa..3704fa585 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java @@ -34,11 +34,14 @@ * @since 0.17 */ public class CasedString { - private final String[] parts; + /** The segments of the cased string */ + private final String[] segments; + /** The case of the string as parsed */ private final StringCase stringCase; - private static final Function PASCAL_JOINER = (strings) -> { + /** A joiner used for the pascal and camel cases. */ + private static final Function PASCAL_JOINER = strings -> { StringBuilder sb = new StringBuilder(); - Arrays.stream(strings).map((s) -> s == null ? "" : s).forEach((token) -> sb.append(WordUtils.capitalize(token.toLowerCase(Locale.ROOT)))); + Arrays.stream(strings).map(s -> s == null ? "" : s).forEach(token -> sb.append(WordUtils.capitalize(token.toLowerCase(Locale.ROOT)))); return sb.toString(); }; @@ -47,8 +50,8 @@ public class CasedString { * @param stringCase the case of the string being parsed. * @param string the string to parse. */ - public CasedString(StringCase stringCase, String string) { - this.parts = string == null ? CasedString.StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); + public CasedString(final StringCase stringCase, final String string) { + this.segments = string == null ? CasedString.StringCase.NULL_SEGMENT : stringCase.getSegments(string.trim()); this.stringCase = stringCase; } @@ -57,8 +60,8 @@ public CasedString(StringCase stringCase, String string) { * @param stringCase the case of the string. * @param segments the segments of the string. */ - public CasedString(StringCase stringCase, String[] segments) { - this.parts = segments; + public CasedString(final StringCase stringCase, final String[] segments) { + this.segments = segments; this.stringCase = stringCase; } @@ -67,8 +70,8 @@ public CasedString(StringCase stringCase, String[] segments) { * @param stringCase the desired format. * @return the new CasedString. */ - public CasedString as(StringCase stringCase) { - return stringCase.name.equals(this.stringCase.name) ? this : new CasedString(stringCase, (String[])Arrays.copyOf(this.parts, this.parts.length)); + public CasedString as(final StringCase stringCase) { + return stringCase.name.equals(this.stringCase.name) ? this : new CasedString(stringCase, (String[]) Arrays.copyOf(this.segments, this.segments.length)); } /** @@ -76,7 +79,7 @@ public CasedString as(StringCase stringCase) { * @return the segments of this cased string. */ public String[] getSegments() { - return this.parts; + return this.segments; } /** @@ -84,8 +87,8 @@ public String[] getSegments() { * @param stringCase the desired case. * @return this cased string in the desired case. */ - public String toCase(StringCase stringCase) { - return this.parts == CasedString.StringCase.NULL_SEGMENT ? null : (String)stringCase.assemble(this.getSegments()); + public String toCase(final StringCase stringCase) { + return this.segments == CasedString.StringCase.NULL_SEGMENT ? null : (String) stringCase.assemble(this.getSegments()); } @Override @@ -138,7 +141,7 @@ public static class StringCase { * @param preserveSplit the preserveSplit flag. * @param joiner the joiner to assemble the String from the segments. */ - public StringCase(String name, Predicate splitter, boolean preserveSplit, Function joiner) { + public StringCase(final String name, final Predicate splitter, final boolean preserveSplit, final Function joiner) { this(name, splitter, preserveSplit, joiner, Function.identity()); } @@ -147,8 +150,8 @@ public StringCase(String name, Predicate splitter, boolean preserveSp * @param name the name of the case. * @param delimiter the delimter between segments. */ - public StringCase(String name, char delimiter) { - this(name, (c) -> c == delimiter, false, simpleJoiner(delimiter)); + public StringCase(final String name, final char delimiter) { + this(name, c -> c == delimiter, false, simpleJoiner(delimiter)); } /** @@ -157,9 +160,10 @@ public StringCase(String name, char delimiter) { * @param splitter the splitter to detect segments. * @param preserveSplit the flag to preserve the splitter character. * @param joiner the joiner to assemble a String from segments. - * @param postProcess the post-process applied to the string from the joiner. + * @param postProcess the post-process applied to the segments after the splitter has created them. */ - public StringCase(String name, Predicate splitter, boolean preserveSplit, Function joiner, Function postProcess) { + public StringCase(final String name, final Predicate splitter, final boolean preserveSplit, final Function joiner, + final Function postProcess) { this.name = name; this.splitter = splitter; this.preserveSplit = preserveSplit; @@ -173,8 +177,8 @@ public StringCase(String name, Predicate splitter, boolean preserveSp * @param delimiter the delimiter to use between the segments. * @return the assembled string. */ - public static Function simpleJoiner(char delimiter) { - return (s) -> String.join(String.valueOf(delimiter), (CharSequence[])Arrays.stream(s).filter(Objects::nonNull).toArray(String[]::new)); + public static Function simpleJoiner(final char delimiter) { + return s -> String.join(String.valueOf(delimiter), (CharSequence[]) Arrays.stream(s).filter(Objects::nonNull).toArray(String[]::new)); } @Override @@ -187,8 +191,8 @@ public String toString() { * @param segments the segments to assemble. * @return the complete String. */ - public String assemble(String[] segments) { - return (String)this.joiner.apply(segments); + public String assemble(final String[] segments) { + return (String) this.joiner.apply(segments); } /** @@ -196,7 +200,7 @@ public String assemble(String[] segments) { * @param string the string to parse * @return the segments from the string. */ - public String[] getSegments(String string) { + public String[] getSegments(final String string) { if (string == null) { return NULL_SEGMENT; } else if (string.isEmpty()) { @@ -205,7 +209,7 @@ public String[] getSegments(String string) { List lst = new ArrayList(); StringBuilder sb = new StringBuilder(); - for(char c : string.toCharArray()) { + for (char c : string.toCharArray()) { if (this.splitter.test(c)) { lst.add(sb.toString()); sb.setLength(0); @@ -221,13 +225,15 @@ public String[] getSegments(String string) { lst.add(sb.toString()); } - return (String[])lst.stream().map(this.postProcess).filter(Objects::nonNull).toArray(String[]::new); + return (String[]) lst.stream().map(this.postProcess).filter(Objects::nonNull).toArray(String[]::new); } } static { - CAMEL = new StringCase("CAMEL", Character::isUpperCase, true, CasedString.PASCAL_JOINER.andThen(WordUtils::uncapitalize), (x) -> (String)StringUtils.defaultIfEmpty(x, (CharSequence)null)); - PASCAL = new StringCase("PASCAL", Character::isUpperCase, true, CasedString.PASCAL_JOINER, (x) -> (String)StringUtils.defaultIfEmpty(x, (CharSequence)null)); + CAMEL = new StringCase("CAMEL", Character::isUpperCase, true, CasedString.PASCAL_JOINER.andThen(WordUtils::uncapitalize), + x -> (String) StringUtils.defaultIfEmpty(x, (CharSequence) null)); + PASCAL = new StringCase("PASCAL", Character::isUpperCase, true, CasedString.PASCAL_JOINER, + x -> (String) StringUtils.defaultIfEmpty(x, (CharSequence) null)); SNAKE = new StringCase("SNAKE", '_'); KEBAB = new StringCase("KEBAB", '-'); PHRASE = new StringCase("PHRASE", Character::isWhitespace, false, simpleJoiner(' ')); From 690535b3d7505e2313d2e6fbac26d2bbe2a80e08 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 16:13:51 +0000 Subject: [PATCH 09/11] cleaned up sonar issues --- .../main/java/org/apache/rat/utils/CasedString.java | 11 ++++++----- .../java/org/apache/rat/utils/CasedStringTests.java | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java index 3704fa585..f9b9a73c0 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java @@ -25,6 +25,7 @@ import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.UnaryOperator; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.WordUtils; @@ -132,7 +133,7 @@ public static class StringCase { /** The function that converts segments into the String representation */ private final Function joiner; /** A function to provide post-processing on the joined string */ - private final Function postProcess; + private final UnaryOperator postProcess; /** * Constructs a StringCase @@ -142,7 +143,7 @@ public static class StringCase { * @param joiner the joiner to assemble the String from the segments. */ public StringCase(final String name, final Predicate splitter, final boolean preserveSplit, final Function joiner) { - this(name, splitter, preserveSplit, joiner, Function.identity()); + this(name, splitter, preserveSplit, joiner, UnaryOperator.identity()); } /** @@ -163,7 +164,7 @@ public StringCase(final String name, final char delimiter) { * @param postProcess the post-process applied to the segments after the splitter has created them. */ public StringCase(final String name, final Predicate splitter, final boolean preserveSplit, final Function joiner, - final Function postProcess) { + final UnaryOperator postProcess) { this.name = name; this.splitter = splitter; this.preserveSplit = preserveSplit; @@ -192,7 +193,7 @@ public String toString() { * @return the complete String. */ public String assemble(final String[] segments) { - return (String) this.joiner.apply(segments); + return this.joiner.apply(segments); } /** @@ -206,7 +207,7 @@ public String[] getSegments(final String string) { } else if (string.isEmpty()) { return EMPTY_SEGMENT; } else { - List lst = new ArrayList(); + List lst = new ArrayList<>(); StringBuilder sb = new StringBuilder(); for (char c : string.toCharArray()) { diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java index 29e4f72f8..3270e4d48 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java @@ -28,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat; -public class CasedStringTests { +class CasedStringTests { @MethodSource("testSegmentationData") @ParameterizedTest From ed2c64d46892effb3df798046e9f47f4ecd51a21 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 17:00:45 +0000 Subject: [PATCH 10/11] added additional tests --- .../org/apache/rat/utils/CasedString.java | 21 +++++++++++++++---- .../apache/rat/utils/CasedStringTests.java | 11 +++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java index f9b9a73c0..e18b9df48 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java @@ -34,7 +34,7 @@ * Handles converting from one string case to another (e.g. camel case to snake case). * @since 0.17 */ -public class CasedString { +public final class CasedString { /** The segments of the cased string */ private final String[] segments; /** The case of the string as parsed */ @@ -89,7 +89,7 @@ public String[] getSegments() { * @return this cased string in the desired case. */ public String toCase(final StringCase stringCase) { - return this.segments == CasedString.StringCase.NULL_SEGMENT ? null : (String) stringCase.assemble(this.getSegments()); + return this.segments == CasedString.StringCase.NULL_SEGMENT ? null : stringCase.assemble(this.getSegments()); } @Override @@ -97,10 +97,22 @@ public String toString() { return this.toCase(this.stringCase); } + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + CasedString that = (CasedString) o; + return Objects.deepEquals(getSegments(), that.getSegments()) && Objects.equals(stringCase, that.stringCase); + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(getSegments()), stringCase); + } + /** * The definition of a String case. */ - public static class StringCase { + public final static class StringCase { /** The camel case. Example: "HelloWorld"*/ public static final StringCase CAMEL; /** The pascal case. Example: "helloWorld" */ @@ -226,10 +238,11 @@ public String[] getSegments(final String string) { lst.add(sb.toString()); } - return (String[]) lst.stream().map(this.postProcess).filter(Objects::nonNull).toArray(String[]::new); + return lst.stream().map(this.postProcess).filter(Objects::nonNull).toArray(String[]::new); } } + static { CAMEL = new StringCase("CAMEL", Character::isUpperCase, true, CasedString.PASCAL_JOINER.andThen(WordUtils::uncapitalize), x -> (String) StringUtils.defaultIfEmpty(x, (CharSequence) null)); diff --git a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java index 3270e4d48..2d4ff9938 100644 --- a/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java +++ b/apache-rat-core/src/test/java/org/apache/rat/utils/CasedStringTests.java @@ -18,6 +18,7 @@ */ package org.apache.rat.utils; +import org.junit.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -28,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; -class CasedStringTests { +public class CasedStringTests { @MethodSource("testSegmentationData") @ParameterizedTest @@ -223,4 +224,12 @@ static Stream testAssembleData() { return lst.stream(); } + + @Test + public void asTest() { + CasedString underTest = new CasedString(CasedString.StringCase.CAMEL, "camelCase"); + assertThat(underTest.as(CasedString.StringCase.CAMEL)).isEqualTo(underTest); + CasedString expected = new CasedString(CasedString.StringCase.KEBAB, "camel-Case"); + assertThat(underTest.as(CasedString.StringCase.KEBAB)).isEqualTo(expected); + } } From a0e900679a160537530dcec2cdcf197d74a753a0 Mon Sep 17 00:00:00 2001 From: Claude Warren Date: Sun, 8 Mar 2026 17:44:34 +0000 Subject: [PATCH 11/11] fixed checkstyle issues --- .../src/main/java/org/apache/rat/utils/CasedString.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java index e18b9df48..e87347408 100644 --- a/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java +++ b/apache-rat-core/src/main/java/org/apache/rat/utils/CasedString.java @@ -98,8 +98,10 @@ public String toString() { } @Override - public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + public boolean equals(final Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } CasedString that = (CasedString) o; return Objects.deepEquals(getSegments(), that.getSegments()) && Objects.equals(stringCase, that.stringCase); } @@ -112,7 +114,7 @@ public int hashCode() { /** * The definition of a String case. */ - public final static class StringCase { + public static final class StringCase { /** The camel case. Example: "HelloWorld"*/ public static final StringCase CAMEL; /** The pascal case. Example: "helloWorld" */