diff --git a/src/main/java/org/apache/commons/text/CaseUtils.java b/src/main/java/org/apache/commons/text/CaseUtils.java index c3a7591a38..bb439d7690 100644 --- a/src/main/java/org/apache/commons/text/CaseUtils.java +++ b/src/main/java/org/apache/commons/text/CaseUtils.java @@ -16,10 +16,10 @@ */ package org.apache.commons.text; -import java.util.HashSet; -import java.util.Set; +import java.util.regex.Pattern; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; /** @@ -28,13 +28,51 @@ *

This class tries to handle {@code null} input gracefully. * An exception will not be thrown for a {@code null} input. * Each method documents its behavior in more detail.

+ * Examples: + *
+ *                                                           "Two words" "foo bar" "Piñata Café"
+ * camelCase        toCamelCase(str)                         "twoWords"  "fooBar"  "pinataCafe"
+ * camelCase        toCamelCase(str, false, " ")             "twoWords"  "fooBar"  "piñataCafé"
+ * camel_Snake      toDelimitedCase(str, false, '_')         "two_Words" "foo_Bar" "pinata_Cafe"
+ * flatcase         toPascalCase(str).toLowerCase()          "twowords"  "foobar"  "pinatacafe"
+ * kebab-case       toKebabCase(str)                         "two-words" "foo-bar" "pinata-cafe"
+ * PascalCase       toPascalCase(str)                        "TwoWords"  "FooBar"  "PinataCafe"
+ * PascalCase       toCamelCase(str, true, " ")              "TwoWords"  "FooBar"  "PiñataCafé"
+ * SCREAMINGCASE    toPascalCase(str).toUpperCase()          "TWOWORDS"  "FOOBAR"  "PINATACAFE"
+ * SCREAMING-KEBAB  toDelimitedCase(str, '-').toUpperCase()  "TWO-WORDS" "FOO-BAR" "PINATA-CAFE"
+ * SCREAMING_SNAKE  toDelimitedCase(str, '_').toUpperCase()  "TWO_WORDS" "FOO_BAR" "PINATA_CAFE"
+ * snake_case       toSnakeCase(str)                         "two_words" "foo_bar" "pinata_cafe"
+ * Title_Case       toDelimitedCase(str, '_')                "Two_Words" "Foo_Bar" "Pinata_Cafe"
+ * Train-Case       toDelimitedCase(str, '-')                "Two-Words" "Foo-Bar" "Pinata-Cafe"
+ * 
+ * + * Note: Examples with {@code toUpperCase()} and {@code toLowerCase()} may be replaced with + * {@code StringUtils.upperCase(str)} or {@code StringUtils.lowerCase(str)} to be null-safe. * * @since 1.2 */ public class CaseUtils { /** - * Converts all the delimiter separated words in a String into camelCase, + * All lower ASCII alphanumeric characters. + */ + private static final Pattern ALPHANUMERIC = Pattern.compile("[0-9A-Za-z]"); + + /** + * All lower ASCII alphanumeric characters, single quote, and right single "curly" quote (\u2019). + */ + private static final Pattern ALPHANUMERIC_WITH_APOSTROPHE = Pattern.compile("[0-9A-Za-z'\u2019]"); + + /** + * All characters not included in ALPHANUMERIC + */ + private static final Pattern NON_ALPHANUMERIC = Pattern.compile("^[^0-9A-Za-z]*$"); + + private static final Pattern O_IRISH = Pattern.compile("(O')|(O\u2019)"); + + + /** + * Converts all the delimiter-separated words in a String into camelCase, * that is each word is made up of a title case character and then a series of * lowercase characters. * @@ -46,7 +84,7 @@ public class CaseUtils { *

A {@code null} input String returns {@code null}.

* *

A input string with only delimiter characters returns {@code ""}.

- * + *

* Capitalization uses the Unicode title case, normally equivalent to * upper case and cannot perform locale-sensitive mappings. * @@ -61,59 +99,214 @@ public class CaseUtils { * CaseUtils.toCamelCase(" @", false, new char[]{'@'}) = "" * * - * @param str the String to be converted to camelCase, may be null - * @param capitalizeFirstLetter boolean that determines if the first character of first word should be title case. - * @param delimiters set of characters to determine capitalization, null and/or empty array means whitespace + * @param str the String to be converted to camelCase, may be null + * @param capitalizeFirstLetter boolean. If true, set the first character of the first word to title case. + * @param delimiters set of characters to determine capitalization, null and/or empty array means whitespace * @return camelCase of String, {@code null} if null String input */ - public static String toCamelCase(String str, final boolean capitalizeFirstLetter, final char... delimiters) { + public static String toCamelCase(String str, final Boolean capitalizeFirstLetter, char... delimiters) { if (StringUtils.isEmpty(str)) { return str; } - str = str.toLowerCase(); - final int strLen = str.length(); - final int[] newCodePoints = new int[strLen]; - int outOffset = 0; - final Set delimiterSet = toDelimiterSet(delimiters); - boolean capitalizeNext = capitalizeFirstLetter; - for (int index = 0; index < strLen;) { - final int codePoint = str.codePointAt(index); - - if (delimiterSet.contains(codePoint)) { - capitalizeNext = outOffset != 0; - index += Character.charCount(codePoint); - } else if (capitalizeNext || outOffset == 0 && capitalizeFirstLetter) { - final int titleCaseCodePoint = Character.toTitleCase(codePoint); - newCodePoints[outOffset++] = titleCaseCodePoint; - index += Character.charCount(titleCaseCodePoint); - capitalizeNext = false; + boolean capitalizeFirst = BooleanUtils.isTrue(capitalizeFirstLetter); + if (ArrayUtils.isEmpty(delimiters)) { + delimiters = new char[]{' '}; + } + // The delimiter array in text.WordUtils.capitalize(String, char[]) is not working properly + // in the current (1.12) build. + // The following loop is a temporary fix. + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + if (str.charAt(i) != ' ' && ArrayUtils.contains(delimiters, str.charAt(i))) { + sb.append(' '); } else { - newCodePoints[outOffset++] = codePoint; - index += Character.charCount(codePoint); + int codepoint = str.codePointAt(i); + sb.append(Character.toChars(Character.toLowerCase(codepoint))); } } + str = sb.toString(); + delimiters = new char[]{' '}; + // End temporary fix. + if (capitalizeFirst) { + return StringUtils.deleteWhitespace(WordUtils.capitalize(str, delimiters)); + } else { + return WordUtils.uncapitalize(StringUtils.deleteWhitespace(WordUtils.capitalize(str, delimiters))); + } + } - return new String(newCodePoints, 0, outOffset); + /** + * Uses {@code toDelimitedCase()} to convert a string to camelCase.
+ * This method has different behavior from {@link #toCamelCase(String, Boolean, char[])} + * because all accented characters are normalized (accents removed).
+ * For example, {@code toCamelCase("Piñata Café")} will return {@code "pinataCafe"}, where + * {@code toCamelCase("Piñata Café", false, " ")} will return {@code "piñataCafé"}.
+ * Converts the first alphanumeric character of the string to lower case. + * Capitalizes first character of all other alphanumeric sequences. + * Converts all other characters in the sequence to lower case.
+ * Strips all non-alphanumeric characters or sequences of non-alphanumeric characters + * from the beginning and end of the string.
+ * Deletes all other non-alphanumeric characters or sequences of non-alphanumeric characters.
+ * + * @param str The text to convert. + * @return The convertedText. + * @see #toCamelCase(String, Boolean, char[]) + * @see #toDelimitedCase(String, Boolean, Character) + */ + public static String toCamelCase(String str) { + return StringUtils.deleteWhitespace(toDelimitedCase(str, false, ' ')); } /** - * Converts an array of delimiters to a hash set of code points. Code point of space(32) is added - * as the default value. The generated hash set provides O(1) lookup time. + * Converts a string to Delimited Case.
+ * This is identical to using {@code toDelimitedCase(str, true, separator);}
+ * Normalizes accented characters (removes accents).
+ * Capitalizes the first character of any alphanumeric sequence. + * Converts the rest of the characters in the sequence to lower case
+ * Strips all non-alphanumeric characters or sequences of non-alphanumeric characters + * from the beginning and end of the string.
+ * Converts all other non-alphanumeric characters or sequences of non-alphanumeric characters + * to the separator delimiter.
* - * @param delimiters set of characters to determine capitalization, null means whitespace - * @return Set + * @param str String: the text to convert. + * @param separator char: The separator to use as a delimiter. + * @return The Converted_Text. */ - private static Set toDelimiterSet(final char[] delimiters) { - final Set delimiterHashSet = new HashSet<>(); - delimiterHashSet.add(Character.codePointAt(new char[]{' '}, 0)); - if (ArrayUtils.isEmpty(delimiters)) { - return delimiterHashSet; + public static String toDelimitedCase(String str, Character separator) { + return toDelimitedCase(str, true, separator); + } + + /** + * Converts a string to Delimited Case.
+ * Normalizes accented characters (removes accents).
+ * If {@code capitalizeFirstLetter} is {@code true}, capitalizes the first character of the string. + * Otherwise, converts the first character of the string to lower case.
+ * Capitalizes the first character of any other alphanumeric sequence. + * Converts the rest of the characters in the sequence to lower case
+ * Strips all non-alphanumeric characters or sequences of non-alphanumeric characters + * from the beginning and end of the string.
+ * Converts all other non-alphanumeric characters or sequences of non-alphanumeric characters + * to the separator delimiter.
+ * + * @param str String: the text to convert. + * @param capitalizeFirstLetter boolean: If false, converts the first character of the string to lower case. + * @param separator char: The separator to use as a delimiter. + * @return The Converted_Text. + */ + public static String toDelimitedCase(String str, final Boolean capitalizeFirstLetter, Character separator) { + // This method sanitizes the input to run through toDelimitedEngine(). + if (StringUtils.isEmpty(str)) { + return str; + } + boolean capitalizeFirst = BooleanUtils.isNotFalse(capitalizeFirstLetter); + if (separator == null) { + if (capitalizeFirst) { + return toPascalCase(str); + } else { + return toCamelCase(str); + } + } + // return STRIP_ACCENTS_PATTERN.matcher(decomposed).replaceAll(EMPTY); + String normalized = O_IRISH.matcher(StringUtils.stripAccents(str).trim()).replaceAll("O "); + if (NON_ALPHANUMERIC.matcher(normalized).matches()) { + return ""; + } + int startIndex = 0; + for (int i = 0; i < normalized.length(); i++) { + if (ALPHANUMERIC.matcher(Character.toString(normalized.charAt(i))).matches()) { + startIndex = i; + break; + } } - for (int index = 0; index < delimiters.length; index++) { - delimiterHashSet.add(Character.codePointAt(delimiters, index)); + return toDelimitedEngine(normalized, capitalizeFirst, separator, startIndex); + } + + /** + * This is the engine that generates the return value of {@link #toDelimitedCase(String, Boolean, Character)} + * + * @param normalized String: the sanitized and normalized text to convert. + * @param capitalizeFirst boolean: If false, converts the first character of the string to lower case. + * @param separator char: The separator to use as a delimiter. + * @param startIndex int: The index of the first alphanumeric character. + * @return The Converted_Text. + */ + private static String toDelimitedEngine(String normalized, boolean capitalizeFirst, char separator, int startIndex) { + StringBuilder delimited = new StringBuilder(); + for (int i = startIndex; i < normalized.length(); i++) { + if (i > startIndex && + !ALPHANUMERIC_WITH_APOSTROPHE.matcher(Character.toString(normalized.charAt(i))).matches()) { + if (delimited.charAt(delimited.length() - 1) != separator) { + delimited.append(separator); + } + } else if (normalized.charAt(i) != '\'' && normalized.charAt(i) != '\u2019') { + if (i == startIndex && capitalizeFirst) { + delimited.append(Character.toUpperCase(normalized.charAt(i))); + } else if (i != startIndex && delimited.charAt(delimited.length() - 1) == separator) { + delimited.append(Character.toUpperCase(normalized.charAt(i))); + } else { + delimited.append(Character.toLowerCase(normalized.charAt(i))); + } + } + } + if (delimited.charAt(delimited.length() - 1) == separator) { + delimited.deleteCharAt(delimited.length() - 1); } - return delimiterHashSet; + + return delimited.toString(); + } + + /** + * Uses {@code toDelimitedCase()} to convert a string to kebab-case.
+ * Normalizes accented characters (removes accents).
+ * Converts all alphanumeric characters to lower case.
+ * Strips all non-alphanumeric characters or sequences of non-alphanumeric characters + * from the beginning and end of the string.
+ * Converts all other non-alphanumeric characters or sequences of non-alphanumeric characters + * to a single hyphen ('-').
+ * + * @param str The text to convert. + * @return The converted-text. + * @see #toDelimitedCase(String, Character) + * @see StringUtils#lowerCase(String) + */ + public static String toKebabCase(String str) { + return StringUtils.lowerCase(toDelimitedCase(str, '-')); + } + + /** + * Uses {@code toDelimitedCase()} to convert a string to UpperCamelCase.
+ * Normalizes accented characters (removes accents).
+ * Capitalizes The first character of any alphanumeric sequence. + * Converts the rest of the characters in the sequence to lower case
+ * Strips all non-alphanumeric characters or sequences of non-alphanumeric characters + * from the beginning and end of the string.
+ * Deletes all other non-alphanumeric characters or sequences of non-alphanumeric characters.
+ * + * @param str The text to convert. + * @return The ConvertedText. + * @see #toDelimitedCase(String, Character) + * @see StringUtils#deleteWhitespace(String) + */ + public static String toPascalCase(String str) { + return StringUtils.deleteWhitespace(toDelimitedCase(str, ' ')); + } + + /** + * Uses {@code toDelimitedCase()} to convert a string to snake_case.
+ * Normalizes accented characters (removes accents).
+ * Converts all alphanumeric characters to lower case.
+ * Strips all non-alphanumeric characters or sequences of non-alphanumeric characters + * from the beginning and end of the string.
+ * Converts all other non-alphanumeric characters or sequences of non-alphanumeric characters + * to a single underscore ('_').
+ * + * @param str The text to convert. + * @return The converted_text. + * @see #toDelimitedCase(String, Character) + * @see StringUtils#lowerCase(String) + */ + public static String toSnakeCase(String str) { + return StringUtils.lowerCase(toDelimitedCase(str, '_')); } /** @@ -127,4 +320,3 @@ private static Set toDelimiterSet(final char[] delimiters) { public CaseUtils() { } } - diff --git a/src/test/java/org/apache/commons/text/CaseUtilsTest.java b/src/test/java/org/apache/commons/text/CaseUtilsTest.java index f7195e708c..ec8890b03f 100644 --- a/src/test/java/org/apache/commons/text/CaseUtilsTest.java +++ b/src/test/java/org/apache/commons/text/CaseUtilsTest.java @@ -43,34 +43,281 @@ public void testToCamelCase() { assertThat(CaseUtils.toCamelCase(null, false, null)).isNull(); assertThat(CaseUtils.toCamelCase("", true, null)).isEqualTo(""); assertThat(CaseUtils.toCamelCase(" ", false, null)).isEqualTo(""); - assertThat(CaseUtils.toCamelCase("a b c @def", false, null)).isEqualTo("aBC@def"); + assertThat(CaseUtils.toCamelCase("a b c @def", false, null)) + .isEqualTo("aBC@def"); assertThat(CaseUtils.toCamelCase("a b c @def", true)).isEqualTo("ABC@def"); - assertThat(CaseUtils.toCamelCase("a b c @def", true, '-')).isEqualTo("ABC@def"); - assertThat(CaseUtils.toCamelCase("a b c @def", true, '-')).isEqualTo("ABC@def"); + assertThat(CaseUtils.toCamelCase("a b c @def", true, '-')) + .isEqualTo("ABC@def"); + assertThat(CaseUtils.toCamelCase("a b c @def", true, '-')) + .isEqualTo("ABC@def"); final char[] chars = {'-', '+', ' ', '@'}; assertThat(CaseUtils.toCamelCase("-+@ ", true, chars)).isEqualTo(""); - assertThat(CaseUtils.toCamelCase(" to-CAMEL-cASE", false, chars)).isEqualTo("toCamelCase"); - assertThat(CaseUtils.toCamelCase("@@@@ to+CAMEL@cASE ", true, chars)).isEqualTo("ToCamelCase"); - assertThat(CaseUtils.toCamelCase("To+CA+ME L@cASE", true, chars)).isEqualTo("ToCaMeLCase"); - - assertThat(CaseUtils.toCamelCase("To.Camel.Case", false, '.')).isEqualTo("toCamelCase"); - assertThat(CaseUtils.toCamelCase("To.Camel-Case", false, '-', '.')).isEqualTo("toCamelCase"); - assertThat(CaseUtils.toCamelCase(" to @ Camel case", false, '-', '@')).isEqualTo("toCamelCase"); - assertThat(CaseUtils.toCamelCase(" @to @ Camel case", true, '-', '@')).isEqualTo("ToCamelCase"); - - assertThat(CaseUtils.toCamelCase("TO CAMEL CASE", true, null)).isEqualTo("ToCamelCase"); - assertThat(CaseUtils.toCamelCase("TO CAMEL CASE", false, null)).isEqualTo("toCamelCase"); - assertThat(CaseUtils.toCamelCase("TO CAMEL CASE", false, null)).isEqualTo("toCamelCase"); - assertThat(CaseUtils.toCamelCase("tocamelcase", false, null)).isEqualTo("tocamelcase"); - assertThat(CaseUtils.toCamelCase("tocamelcase", true, null)).isEqualTo("Tocamelcase"); - assertThat(CaseUtils.toCamelCase("Tocamelcase", false, null)).isEqualTo("tocamelcase"); + assertThat(CaseUtils.toCamelCase(" to-CAMEL-cASE", false, chars)) + .isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("@@@@ to+CAMEL@cASE ", true, chars)) + .isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toCamelCase("To+CA+ME L@cASE", true, chars)) + .isEqualTo("ToCaMeLCase"); + + assertThat(CaseUtils.toCamelCase("To.Camel.Case", false, '.')) + .isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("To.Camel-Case", false, '-', '.')) + .isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase(" to @ Camel case", false, '-', '@')) + .isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase(" @to @ Camel case", true, '-', '@')) + .isEqualTo("ToCamelCase"); + + assertThat(CaseUtils.toCamelCase("TO CAMEL CASE", true, null)) + .isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toCamelCase("TO CAMEL CASE", false, null)) + .isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("TO CAMEL CASE", false, null)) + .isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("tocamelcase", false, null)) + .isEqualTo("tocamelcase"); + assertThat(CaseUtils.toCamelCase("tocamelcase", true, null)) + .isEqualTo("Tocamelcase"); + assertThat(CaseUtils.toCamelCase("Tocamelcase", false, null)) + .isEqualTo("tocamelcase"); assertThat(CaseUtils.toCamelCase("tocamelcase", true)).isEqualTo("Tocamelcase"); assertThat(CaseUtils.toCamelCase("tocamelcase", false)).isEqualTo("tocamelcase"); - assertThat(CaseUtils.toCamelCase("\uD800\uDF00 \uD800\uDF02", true)).isEqualTo("\uD800\uDF00\uD800\uDF02"); - assertThat(CaseUtils.toCamelCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03", true, '\uD800', - '\uDF14')).isEqualTo("\uD800\uDF00\uD800\uDF01\uD800\uDF02\uD800\uDF03"); +// // These tests fail the new toCamelCase(String, Boolean, char[]) method because +// // surrogate pairs \uD800 to \uDFFF have been removed from the Unicode Character Set. +// assertThat(CaseUtils.toCamelCase("\uD800\uDF00 \uD800\uDF02", true)).isEqualTo("\uD800\uDF00\uD800\uDF02"); +// assertThat(CaseUtils.toCamelCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03", true, '\uD800', +// '\uDF14')).isEqualTo("\uD800\uDF00\uD800\uDF01\uD800\uDF02\uD800\uDF03"); + + /* **** NEW TESTS **** */ + + assertThat(CaseUtils.toCamelCase("The café\u2019s piñata gave me déjà vu.", + false, ' ', '.')).isEqualTo("theCafé\u2019sPiñataGaveMeDéjàVu"); + + assertThat(CaseUtils.toCamelCase(null)).isNull(); + assertThat(CaseUtils.toCamelCase("")).isEqualTo(""); + assertThat(CaseUtils.toCamelCase(" ")).isEqualTo(""); + assertThat(CaseUtils.toCamelCase("a b c @def")).isEqualTo("aBCDef"); + assertThat(CaseUtils.toCamelCase("a b c @def")).isEqualTo("aBCDef"); + + assertThat(CaseUtils.toCamelCase("-+@ ")).isEqualTo(""); + assertThat(CaseUtils.toCamelCase(" to-CAMEL-cASE")).isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("@@@@ to+CAMEL@cASE ")).isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("To+CA+ME L@cASE")).isEqualTo("toCaMeLCase"); + + assertThat(CaseUtils.toCamelCase("To.Camel.Case")).isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("To.Camel-Case")).isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase(" to @ Camel case")).isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase(" @to @ Camel case_")).isEqualTo("toCamelCase"); + + assertThat(CaseUtils.toCamelCase("TO CAMEL CASE")).isEqualTo("toCamelCase"); + assertThat(CaseUtils.toCamelCase("tocamelcase")).isEqualTo("tocamelcase"); + + assertThat(CaseUtils.toCamelCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03")) + .isEqualTo(""); + + assertThat(CaseUtils.toCamelCase("The café\u2019s piñata gave me déjà vu.")) + .isEqualTo("theCafesPinataGaveMeDejaVu"); + assertThat(CaseUtils.toCamelCase("\u1E70\u01EB \u010C\u0227\u1E41\u0113\u0142 \u010D\u1E01\u0219\u1E1B")) + .isEqualTo("toCamelCase"); + } + + @Test + public void testToDelimitedCase() { + assertThat(CaseUtils.toDelimitedCase(null, null)).isNull(); + assertThat(CaseUtils.toDelimitedCase(null, null, null)).isNull(); + assertThat(CaseUtils.toDelimitedCase("", ' ')).isEqualTo(""); + assertThat(CaseUtils.toDelimitedCase(" ", ' ')).isEqualTo(""); + assertThat(CaseUtils.toDelimitedCase("a b c @def", '_')).isEqualTo("A_B_C_Def"); + assertThat(CaseUtils.toDelimitedCase("a b c @def", '-')).isEqualTo("A-B-C-Def"); + assertThat(CaseUtils.toDelimitedCase("a b c @def", true, null)) + .isEqualTo("ABCDef"); + assertThat(CaseUtils.toDelimitedCase("a b c @def", false, null)) + .isEqualTo("aBCDef"); + assertThat(CaseUtils.toDelimitedCase("a b c @def", null, null)) + .isEqualTo("ABCDef"); + assertThat(CaseUtils.toDelimitedCase("a b c @def", null, '_')) + .isEqualTo("A_B_C_Def"); + + assertThat(CaseUtils.toDelimitedCase("-+@ ", '@')).isEqualTo(""); + + assertThat(CaseUtils.toDelimitedCase(" to-Delimited-cASE", '_')) + .isEqualTo("To_Delimited_Case"); + assertThat(CaseUtils.toDelimitedCase("@@@@ to+DELIMITED@cASE ", '_')) + .isEqualTo("To_Delimited_Case"); + assertThat(CaseUtils.toDelimitedCase("To+DELIM+IT ED@cASE", '+')) + .isEqualTo("To+Delim+It+Ed+Case"); + + assertThat(CaseUtils.toDelimitedCase(" to-Delimited-cASE", false, '_')) + .isEqualTo("to_Delimited_Case"); + assertThat(CaseUtils.toDelimitedCase("@@@@ to+DELIMITED@cASE ", false, '_')) + .isEqualTo("to_Delimited_Case"); + assertThat(CaseUtils.toDelimitedCase("To+DELIM+IT ED@cASE", false, '+')) + .isEqualTo("to+Delim+It+Ed+Case"); + + assertThat(CaseUtils.toDelimitedCase("To.Delimited.Case", '.')) + .isEqualTo("To.Delimited.Case"); + assertThat(CaseUtils.toDelimitedCase("To.Delimited-Case", '\u2250')) + .isEqualTo("To\u2250Delimited\u2250Case"); + assertThat(CaseUtils.toDelimitedCase(" to @ Delimited case", ' ')) + .isEqualTo("To Delimited Case"); + assertThat(CaseUtils.toDelimitedCase(" @to @ Delimited case_", '@')) + .isEqualTo("To@Delimited@Case"); + + assertThat(CaseUtils.toDelimitedCase("To.Delimited.Case", false, '.')) + .isEqualTo("to.Delimited.Case"); + assertThat(CaseUtils.toDelimitedCase("To.Delimited-Case", false, '\u2250')) + .isEqualTo("to\u2250Delimited\u2250Case"); + assertThat(CaseUtils.toDelimitedCase(" to @ Delimited case", false, ' ')) + .isEqualTo("to Delimited Case"); + assertThat(CaseUtils.toDelimitedCase(" @to @ Delimited case_", false, '@')) + .isEqualTo("to@Delimited@Case"); + + assertThat(CaseUtils.toDelimitedCase("TO DELIMITED CASE", '_')) + .isEqualTo("To_Delimited_Case"); + assertThat(CaseUtils.toDelimitedCase("todelimitedcase", '_')) + .isEqualTo("Todelimitedcase"); + + assertThat(CaseUtils.toDelimitedCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03", + '\uD800')).isEqualTo(""); + + assertThat(CaseUtils.toDelimitedCase("The café\u2019s piñata gave me déjà vu.", '_')) + .isEqualTo("The_Cafes_Pinata_Gave_Me_Deja_Vu"); + assertThat(CaseUtils.toDelimitedCase("The café\u2019s piñata gave me déjà vu.", + false, '_')).isEqualTo("the_Cafes_Pinata_Gave_Me_Deja_Vu"); + assertThat(CaseUtils.toDelimitedCase("\u1E12\u0205\u0142\u012B\u1E43\u01D0\u1E6B\u0119\u1E0B " + + "\u010D\u1E01\u0219\u1E1B", '_')) + .isEqualTo("Delimited_Case"); + + assertThat(CaseUtils.toDelimitedCase("Will 'O The Wisp", '-')).isEqualTo("Will-O-The-Wisp"); + + assertThat(CaseUtils.toDelimitedCase("\u2026with boughs of holly\u2026\n\u2019Tis the season\u2026", + '\u272F')) + .isEqualTo("With\u272FBoughs\u272FOf\u272FHolly\u272FTis\u272FThe\u272FSeason"); + assertThat(CaseUtils.toDelimitedCase("\u2026with boughs of holly\u2026\n\u2019Tis the season\u2026", + false,'\u272F')) + .isEqualTo("with\u272FBoughs\u272FOf\u272FHolly\u272FTis\u272FThe\u272FSeason"); + + assertThat(CaseUtils.toDelimitedCase("\"Officer O'Malley and Peart O'Niel " + + "walk into the Protestant's Bar.\"", '_')) + .isEqualTo("Officer_O_Malley_And_Peart_O_Niel_Walk_Into_The_Protestants_Bar"); + } + + @Test + public void testToKebabCase() { + assertThat(CaseUtils.toKebabCase(null)).isNull(); + assertThat(CaseUtils.toKebabCase("")).isEqualTo(""); + assertThat(CaseUtils.toKebabCase(" ")).isEqualTo(""); + assertThat(CaseUtils.toKebabCase("a b c @def")).isEqualTo("a-b-c-def"); + assertThat(CaseUtils.toKebabCase("a b c @def")).isEqualTo("a-b-c-def"); + + assertThat(CaseUtils.toKebabCase("-+@ ")).isEqualTo(""); + assertThat(CaseUtils.toKebabCase(" to-KEBAB-cASE")).isEqualTo("to-kebab-case"); + assertThat(CaseUtils.toKebabCase("@@@@ to+KEBAB@cASE ")).isEqualTo("to-kebab-case"); + assertThat(CaseUtils.toKebabCase("To+KE+BA B@cASE")).isEqualTo("to-ke-ba-b-case"); + + assertThat(CaseUtils.toKebabCase("To.Kebab.Case")).isEqualTo("to-kebab-case"); + assertThat(CaseUtils.toKebabCase("To.Kebab-Case")).isEqualTo("to-kebab-case"); + assertThat(CaseUtils.toKebabCase(" to @ Kebab case")).isEqualTo("to-kebab-case"); + assertThat(CaseUtils.toKebabCase(" @to @ Kebab case_")).isEqualTo("to-kebab-case"); + + assertThat(CaseUtils.toKebabCase("TO KEBAB CASE")).isEqualTo("to-kebab-case"); + assertThat(CaseUtils.toKebabCase("tokebabcase")).isEqualTo("tokebabcase"); + + assertThat(CaseUtils.toKebabCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03")) + .isEqualTo(""); + + assertThat(CaseUtils.toKebabCase("The café\u2019s piñata gave me déjà vu.")) + .isEqualTo("the-cafes-pinata-gave-me-deja-vu"); + assertThat(CaseUtils.toKebabCase("\u1E70\u01EB \u1E31\u0115\u1E07\u0227\u1E05 \u010D\u1E01\u0219\u1E1B")) + .isEqualTo("to-kebab-case"); + } + + @Test + public void testToPascalCase() { + assertThat(CaseUtils.toPascalCase(null)).isNull(); + assertThat(CaseUtils.toPascalCase("")).isEqualTo(""); + assertThat(CaseUtils.toPascalCase(" ")).isEqualTo(""); + assertThat(CaseUtils.toPascalCase("a b c @def")).isEqualTo("ABCDef"); + assertThat(CaseUtils.toPascalCase("a b c @def")).isEqualTo("ABCDef"); + + assertThat(CaseUtils.toPascalCase("-+@ ")).isEqualTo(""); + assertThat(CaseUtils.toPascalCase(" to-CAMEL-cASE")).isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toPascalCase("@@@@ to+CAMEL@cASE ")).isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toPascalCase("To+CA+ME L@cASE")).isEqualTo("ToCaMeLCase"); + + assertThat(CaseUtils.toPascalCase("To.Camel.Case")).isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toPascalCase("To.Camel-Case")).isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toPascalCase(" to @ Camel case")).isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toPascalCase(" @to @ Camel case_")).isEqualTo("ToCamelCase"); + + assertThat(CaseUtils.toPascalCase("TO CAMEL CASE")).isEqualTo("ToCamelCase"); + assertThat(CaseUtils.toPascalCase("tocamelcase")).isEqualTo("Tocamelcase"); + + assertThat(CaseUtils.toPascalCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03")) + .isEqualTo(""); + + assertThat(CaseUtils.toPascalCase("The café\u2019s piñata gave me déjà vu.")) + .isEqualTo("TheCafesPinataGaveMeDejaVu"); + assertThat(CaseUtils.toPascalCase("\u1E70\u01EB \u010C\u0227\u1E41\u0113\u0142 \u010D\u1E01\u0219\u1E1B")) + .isEqualTo("ToCamelCase"); } + + @Test + public void testToSnakeCase() { + assertThat(CaseUtils.toSnakeCase(null)).isNull(); + assertThat(CaseUtils.toSnakeCase("")).isEqualTo(""); + assertThat(CaseUtils.toSnakeCase(" ")).isEqualTo(""); + assertThat(CaseUtils.toSnakeCase("a b c @def")).isEqualTo("a_b_c_def"); + assertThat(CaseUtils.toSnakeCase("a b c @def")).isEqualTo("a_b_c_def"); + + assertThat(CaseUtils.toSnakeCase("-+@ ")).isEqualTo(""); + assertThat(CaseUtils.toSnakeCase(" to-SNAKE-cASE")).isEqualTo("to_snake_case"); + assertThat(CaseUtils.toSnakeCase("@@@@ to+SNAKE@cASE ")).isEqualTo("to_snake_case"); + assertThat(CaseUtils.toSnakeCase("To+SN+AK E@cASE")).isEqualTo("to_sn_ak_e_case"); + + assertThat(CaseUtils.toSnakeCase("To.Snake.Case")).isEqualTo("to_snake_case"); + assertThat(CaseUtils.toSnakeCase("To.Snake-Case")).isEqualTo("to_snake_case"); + assertThat(CaseUtils.toSnakeCase(" to @ Snake case")).isEqualTo("to_snake_case"); + assertThat(CaseUtils.toSnakeCase(" @to @ Snake case_")).isEqualTo("to_snake_case"); + + assertThat(CaseUtils.toSnakeCase("TO SNAKE CASE")).isEqualTo("to_snake_case"); + assertThat(CaseUtils.toSnakeCase("tosnakecase")).isEqualTo("tosnakecase"); + + assertThat(CaseUtils.toSnakeCase("\uD800\uDF00\uD800\uDF01\uD800\uDF14\uD800\uDF02\uD800\uDF03")) + .isEqualTo(""); + + assertThat(CaseUtils.toSnakeCase("The café\u2019s piñata gave me déjà vu.")) + .isEqualTo("the_cafes_pinata_gave_me_deja_vu"); + assertThat(CaseUtils.toSnakeCase("\u1E70\u01EB \u1E61\u1E47\u1EA3\u1E31\u1EB9 \u010D\u1E01\u0219\u1E1B")) + .isEqualTo("to_snake_case"); + } + + @Test + public void testSeparatedWordFormats() { + assertThat(CaseUtils.toCamelCase("Çàmè\u0142 çásé")).isEqualTo("camelCase"); + assertThat(CaseUtils.toCamelCase("Çàmè\u0142 çásé", false, ' ')) + .isEqualTo("çàmè\u0142Çásé"); + assertThat(CaseUtils.toDelimitedCase("Camel snake", false, '_')) + .isEqualTo("camel_Snake"); + assertThat(CaseUtils.toPascalCase("FLAT CASE").toLowerCase()).isEqualTo("flatcase"); + assertThat(CaseUtils.toKebabCase("Kebab Case")).isEqualTo("kebab-case"); + assertThat(CaseUtils.toPascalCase("pâsçã\u0142 çäsê")).isEqualTo("PascalCase"); + assertThat(CaseUtils.toCamelCase("pâsçã\u0142 çäsê", true, ' ')) + .isEqualTo("Pâsçã\u0142Çäsê"); + assertThat(CaseUtils.toPascalCase("screaming case").toUpperCase()).isEqualTo("SCREAMINGCASE"); + assertThat(CaseUtils.toDelimitedCase("screaming KEBAB-CASE", '-').toUpperCase()) + .isEqualTo("SCREAMING-KEBAB-CASE"); + assertThat(CaseUtils.toDelimitedCase("SCREAMING snake_case", '_').toUpperCase()) + .isEqualTo("SCREAMING_SNAKE_CASE"); + assertThat(CaseUtils.toDelimitedCase("Snake Case", '_').toLowerCase()) + .isEqualTo("snake_case"); + assertThat(CaseUtils.toDelimitedCase("title case", true, '_')) + .isEqualTo("Title_Case"); + assertThat(CaseUtils.toDelimitedCase("train case", true, '-')) + .isEqualTo("Train-Case"); + } + }