Added javadoc and significant test cases
diff --git a/src/main/java/org/apache/commons/text/CasedString.java b/src/main/java/org/apache/commons/text/CasedString.java
index 5d1153a..34418cf 100644
--- a/src/main/java/org/apache/commons/text/CasedString.java
+++ b/src/main/java/org/apache/commons/text/CasedString.java
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -37,46 +38,56 @@
* A method to join camel string fragments together.
*/
private static final Function<String[], String> CAMEL_JOINER = a -> {
- StringBuilder sb = new StringBuilder(a[0]);
+ StringBuilder sb = new StringBuilder(a[0].toLowerCase(Locale.ROOT));
for (int i = 1; i < a.length; i++) {
- sb.append(WordUtils.capitalize(a[i]));
+ sb.append(WordUtils.capitalize(a[i].toLowerCase(Locale.ROOT)));
}
return sb.toString();
};
/**
- * An enumeration of supported string cases.
+ * An enumeration of supported string cases. These cases tag strings as having a specific format.
*/
public enum StringCase {
/**
- * Camel case identifies strings like 'CamelCase'.
+ * Camel case tags strings like 'CamelCase' or 'camelCase'. This conversion forces the first character to
+ * lower case. If specific capitalization rules are required use {@code WordUtils.capitalize()} to set the first
+ * character of the string.
*/
- Camel(Character::isUpperCase, true, CAMEL_JOINER),
+ CAMEL(Character::isUpperCase, true, CAMEL_JOINER),
/**
- * Snake case identifies strings like 'Snake_Case'.
+ * 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 {@code String.upperCase}, {@code String.upperCase},
+ * {@code WordUtils.capitalize()}, or {@code WordUtils.uncapitalize()} as required.
*/
- Snake(c -> c == '_', false, a -> String.join("_", a)),
+ SNAKE(c -> c == '_', false, a -> String.join("_", a)),
/**
- * Kebab case identifies strings like 'kebab-case'.
+ * 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 {@code String.upperCase}, {@code String.upperCase},
+ * {@code WordUtils.capitalize()}, or {@code WordUtils.uncapitalize()} as required.
*/
- Kebab(c -> c == '-', false, a -> String.join("-", a)),
+ KEBAB(c -> c == '-', false, a -> String.join("-", a)),
/**
- * Phrase case identifies phrases of words like 'phrase case'.
+ * 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 {@code String.upperCase}, {@code String.upperCase},
+ * {@code WordUtils.capitalize()}, or {@code WordUtils.uncapitalize()} as required.
*/
- Phrase(c -> c == ' ', false, a -> String.join(" ", a)),
+ PHRASE(Character::isWhitespace, false, a -> String.join(" ", a)),
/**
- * Dot case identifies strings of words like 'dot.case'.
+ * 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 {@code String.upperCase}, {@code String.upperCase},
+ * {@code WordUtils.capitalize()}, or {@code WordUtils.uncapitalize()} as required.
*/
- Dot(c -> c == '.', false, a -> String.join(".", a));
+ DOT(c -> c == '.', false, a -> String.join(".", a));
/** test for split position character. */
private final Predicate<Character> splitter;
/** if {@code true} split position character will be preserved in following segment. */
private final boolean preserveSplit;
- /** a function to joing the segments into this case type. */
+ /** a function to joining the segments into this case type. */
private final Function<String[], String> joiner;
/**
@@ -90,6 +101,48 @@
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(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(String string) {
+ if (string == null) {
+ return new String[0];
+ }
+ if (string.isEmpty()) {
+ return new String[]{""};
+ }
+ List<String> lst = new ArrayList<>();
+ StringBuilder sb = new StringBuilder();
+ for (char c : string.toCharArray()) {
+ if (splitter.test(c)) {
+ if (sb.length() > 0) {
+ lst.add(sb.toString());
+ sb.setLength(0);
+ }
+ if (preserveSplit) {
+ sb.append(c);
+ }
+ } else {
+ sb.append(c);
+ }
+ }
+ if (sb.length() > 0) {
+ lst.add(sb.toString());
+ }
+ return lst.toArray(new String[0]);
+ }
}
/**
@@ -98,30 +151,17 @@
* @param string The string.
*/
public CasedString(StringCase stringCase, String string) {
- this.string = string;
+ this.string = string == null ? null : stringCase.assemble(stringCase.getSegments(string.trim()));
this.stringCase = stringCase;
}
- private String[] split() {
- List<String> lst = new ArrayList<>();
- StringBuilder sb = new StringBuilder();
- for (char c : string.toCharArray()) {
- if (stringCase.splitter.test(c)) {
- if (sb.length() > 0) {
- lst.add(sb.toString());
- sb.setLength(0);
- }
- if (stringCase.preserveSplit) {
- sb.append(c);
- }
- } else {
- sb.append(c);
- }
- }
- if (sb.length() > 0) {
- lst.add(sb.toString());
- }
- return lst.toArray(new String[0]);
+ /**
+ * 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);
}
/**
@@ -134,7 +174,7 @@
if (stringCase == this.stringCase) {
return string;
}
- return stringCase.joiner.apply(split());
+ return string == null ? null : stringCase.joiner.apply(getSegments());
}
/**
diff --git a/src/test/java/org/apache/commons/text/CasedStringTest.java b/src/test/java/org/apache/commons/text/CasedStringTest.java
index f0c0007..106a93a 100644
--- a/src/test/java/org/apache/commons/text/CasedStringTest.java
+++ b/src/test/java/org/apache/commons/text/CasedStringTest.java
@@ -18,48 +18,141 @@
*/
package org.apache.commons.text;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
+import org.apache.commons.text.CasedString.StringCase;
+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;
public class CasedStringTest {
- private static String expectedValue(CasedString.StringCase stringCase) {
+ private static String helloWorldValue(StringCase stringCase) {
switch (stringCase) {
- case Camel:
- return "HelloWorld";
- case Kebab:
- return "Hello-World";
- case Phrase:
- return "Hello World";
- case Snake:
- return "Hello_World";
- case Dot:
- return "Hello.World";
+ case CAMEL:
+ return "helloWorld";
+ case KEBAB:
+ return "hello-World";
+ case PHRASE:
+ return "hello World";
+ case SNAKE:
+ return "hello_World";
+ case DOT:
+ return "hello.World";
default:
- throw new RuntimeException("Unsupported StringCase: " + stringCase);
+ fail("Unsupported StringCase: " + stringCase);
}
+ return null; // keeps compiler happy
}
+ private static final CasedString CAMEL = new CasedString(StringCase.CAMEL, "aCamelString");
+ private static final CasedString PHRASE = new CasedString(StringCase.PHRASE, "A test PhrAse");
+ private static final CasedString KEBAB = new CasedString(StringCase.KEBAB, "A-kebAb-string");
+ private static final CasedString SNAKE = new CasedString(StringCase.SNAKE, "A_snaKE_string");
+ private static final CasedString DOT = new CasedString(StringCase.DOT, "A.dOt.string");
+ private static final CasedString ABCDEF = new CasedString(StringCase.PHRASE, "a b c @def");
+ /**
+ * tests the conversion from each Cased string type to every other type.
+ * @param underTest the CasedString being tested.
+ */
@ParameterizedTest
@MethodSource("conversionProvider")
- public void testConversions(CasedString underTest) {
- for (CasedString.StringCase stringCase : CasedString.StringCase.values()) {
- assertEquals(expectedValue(stringCase), underTest.toCase(stringCase), () -> "failed converting to " + stringCase);
+ public void testCrossProductConversions(CasedString underTest) {
+ for (StringCase stringCase : StringCase.values()) {
+ assertEquals(helloWorldValue(stringCase), underTest.toCase(stringCase), () -> "failed converting to " + stringCase);
+ }
+ }
+ /* generates the hello world Cased String for every StringCase */
+ private static Stream<Arguments> conversionProvider() {
+ List<Arguments> lst = new ArrayList<>();
+ for (StringCase stringCase : StringCase.values()) {
+ lst.add(Arguments.of(new CasedString(stringCase, helloWorldValue(stringCase))));
+ }
+ return lst.stream();
+ }
+
+ @Test
+ public void testNullConstructor() {
+ for (StringCase stringCase : StringCase.values()) {
+ CasedString underTest = new CasedString(stringCase, null);
+ assertThat(underTest.toString()).isNull();
+ assertThat(underTest.getSegments()).isEmpty();
+ // test a null underTest can convert to all others types.
+ for (CasedString.StringCase otherCase : StringCase.values()) {
+ assertThat(underTest.toCase(otherCase)).isNull();
+ }
}
}
- private static Stream<Arguments> conversionProvider() {
- List<Arguments> lst = new ArrayList<>();
- for (CasedString.StringCase stringCase : CasedString.StringCase.values()) {
- lst.add(Arguments.of(new CasedString(stringCase, expectedValue(stringCase))));
- }
- return lst.stream();
+ @Test
+ public void testToCamelCase() {
+ assertThat(new CasedString(StringCase.CAMEL, "").toString()).isEqualTo("");
+ assertThat(new CasedString(StringCase.CAMEL, " ").toString()).isEqualTo("");
+ assertThat(new CasedString(StringCase.CAMEL, "Tocamelcase").toString()).isEqualTo("tocamelcase");
+ assertThat(new CasedString(StringCase.PHRASE, "\uD800\uDF00 \uD800\uDF02").toCase(StringCase.CAMEL)).isEqualTo("\uD800\uDF00\uD800\uDF02");
+ assertThat(ABCDEF.toCase(StringCase.CAMEL)).isEqualTo("aBC@def");
+ assertThat(CAMEL.toCase(StringCase.CAMEL)).isEqualTo("aCamelString");
+ assertThat(PHRASE.toCase(StringCase.CAMEL)).isEqualTo("aTestPhrase");
+ assertThat(KEBAB.toCase(StringCase.CAMEL)).isEqualTo("aKebabString");
+ assertThat(SNAKE.toCase(StringCase.CAMEL)).isEqualTo("aSnakeString");
+ assertThat(DOT.toCase(StringCase.CAMEL)).isEqualTo("aDotString");
+ assertThat(new CasedString(StringCase.PHRASE, "TO CAMEL CASE").toCase(StringCase.CAMEL)).isEqualTo("toCamelCase");
+ assertThat(new CasedString(StringCase.KEBAB, " to-CAMEL-cASE").toCase(StringCase.CAMEL)).isEqualTo("toCamelCase");
+ assertThat(new CasedString(StringCase.DOT, "To.Camel.Case").toCase(StringCase.CAMEL)).isEqualTo("toCamelCase");
+ }
+
+ @Test
+ public void testToPhraseTest() {
+ assertThat(new CasedString(StringCase.PHRASE, "").toString()).isEqualTo("");
+ assertThat(new CasedString(StringCase.PHRASE, " ").toString()).isEqualTo("");
+ assertThat(ABCDEF.toCase(StringCase.PHRASE)).isEqualTo("a b c @def");
+ assertThat(CAMEL.toCase(StringCase.PHRASE)).isEqualTo("a Camel String");
+ assertThat(PHRASE.toCase(StringCase.PHRASE)).isEqualTo("A test PhrAse");
+ assertThat(KEBAB.toCase(StringCase.PHRASE)).isEqualTo("A kebAb string");
+ assertThat(SNAKE.toCase(StringCase.PHRASE)).isEqualTo("A snaKE string");
+ assertThat(DOT.toCase(StringCase.PHRASE)).isEqualTo("A dOt string");
+ }
+
+ @Test
+ public void testToKebabTest() {
+ assertThat(new CasedString(StringCase.KEBAB, "").toString()).isEqualTo("");
+ assertThat(new CasedString(StringCase.KEBAB, " ").toString()).isEqualTo("");
+ assertThat(ABCDEF.toCase(StringCase.KEBAB)).isEqualTo("a-b-c-@def");
+ assertThat(CAMEL.toCase(StringCase.KEBAB)).isEqualTo("a-Camel-String");
+ assertThat(PHRASE.toCase(StringCase.KEBAB)).isEqualTo("A-test-PhrAse");
+ assertThat(KEBAB.toCase(StringCase.KEBAB)).isEqualTo("A-kebAb-string");
+ assertThat(SNAKE.toCase(StringCase.KEBAB)).isEqualTo("A-snaKE-string");
+ assertThat(DOT.toCase(StringCase.KEBAB)).isEqualTo("A-dOt-string");
+ }
+
+ @Test
+ public void testToSnakeTest() {
+ assertThat(new CasedString(StringCase.SNAKE, "").toString()).isEqualTo("");
+ assertThat(new CasedString(StringCase.SNAKE, " ").toString()).isEqualTo("");
+ assertThat(ABCDEF.toCase(StringCase.SNAKE)).isEqualTo("a_b_c_@def");
+ assertThat(CAMEL.toCase(StringCase.SNAKE)).isEqualTo("a_Camel_String");
+ assertThat(PHRASE.toCase(StringCase.SNAKE)).isEqualTo("A_test_PhrAse");
+ assertThat(KEBAB.toCase(StringCase.SNAKE)).isEqualTo("A_kebAb_string");
+ assertThat(SNAKE.toCase(StringCase.SNAKE)).isEqualTo("A_snaKE_string");
+ assertThat(DOT.toCase(StringCase.SNAKE)).isEqualTo("A_dOt_string");
+ }
+
+ @Test
+ public void testToDotTest() {
+ assertThat(new CasedString(StringCase.DOT, "").toString()).isEqualTo("");
+ assertThat(new CasedString(StringCase.DOT, " ").toString()).isEqualTo("");
+ assertThat(ABCDEF.toCase(StringCase.DOT)).isEqualTo("a.b.c.@def");
+ assertThat(CAMEL.toCase(StringCase.DOT)).isEqualTo("a.Camel.String");
+ assertThat(PHRASE.toCase(StringCase.DOT)).isEqualTo("A.test.PhrAse");
+ assertThat(KEBAB.toCase(StringCase.DOT)).isEqualTo("A.kebAb.string");
+ assertThat(SNAKE.toCase(StringCase.DOT)).isEqualTo("A.snaKE.string");
+ assertThat(DOT.toCase(StringCase.DOT)).isEqualTo("A.dOt.string");
}
}