elharo commented on code in PR #450:
URL: https://github.com/apache/commons-text/pull/450#discussion_r1376568237


##########
src/main/java/org/apache/commons/text/cases/CasesUtils.java:
##########
@@ -0,0 +1,44 @@
+/*
+ * 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.commons.text.cases;
+
+/**
+ * Util methods for the Cases API.
+ */
+public class CasesUtils {

Review Comment:
   CasesUitls --> Cases
   
   "Utild" doesn't add much iof anytihng



##########
src/main/java/org/apache/commons/text/cases/CharacterDelimitedCase.java:
##########
@@ -0,0 +1,151 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.CharUtils;
+
+/**
+ * DelimitedCase is a case in which the true alphabetic case of the characters 
is ignored by default
+ * and tokens themselves are determined by the presence of a delimiter between 
each token.

Review Comment:
   delete "themselves"
   
   This is a good description though. It does make me wonder if "Cases" isn't 
the right language here since as you point out, it's not always about case. Is 
there a more generic term that can be used for both case and other-than-case 
delimited strings?



##########
src/main/java/org/apache/commons/text/cases/CharacterDelimitedCase.java:
##########
@@ -0,0 +1,151 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.CharUtils;
+
+/**
+ * DelimitedCase is a case in which the true alphabetic case of the characters 
is ignored by default
+ * and tokens themselves are determined by the presence of a delimiter between 
each token.
+ */
+public class CharacterDelimitedCase implements Case {
+
+    /** Delimiters to be used when parsing. */
+    private List<Integer> parseDelimiters;
+
+    /** Delimiter to be used when formatting. */
+    private String formatDelimiter;
+
+    /**
+     * Constructs a new Delimited Case with null delimiters.
+     */
+    CharacterDelimitedCase() {
+        this(null, null);
+    }
+
+    /**
+     * Constructs a new Delimited Case.
+     *
+     * @param delimiter the character to use as both the parse and format 
delimiter
+     */
+    CharacterDelimitedCase(char delimiter) {
+        this(new char[] { delimiter }, CharUtils.toString(delimiter));
+    }
+
+    /**
+     * Constructs a new delimited case.
+     *
+     * @param parseDelimiters the array of delimiters to use when parsing
+     * @param formatDelimiter the delimiter to use when formatting
+     */
+    CharacterDelimitedCase(char[] parseDelimiters, String formatDelimiter) {
+        super();
+        this.parseDelimiters = generateDelimiterList(parseDelimiters);
+        this.formatDelimiter = formatDelimiter;
+    }
+
+    /**
+     * Formats tokens into Delimited Case.
+     * <p>
+     * Tokens are iterated on and appended to an output stream, with an 
instance of a

Review Comment:
   "iterated on" sounds off; maybe just drop it
   
   delete instance of. The delimiter might not even be an object. I guess it is 
a string but then it might not be a single character



##########
src/test/java/org/apache/commons/text/cases/CasesTest.java:
##########
@@ -0,0 +1,268 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class CasesTest {
+
+    @Test
+    public void testCasesUtils() {
+        Assertions.assertEquals("testUtils", CasesUtils.convert("test-utils", 
CasesUtils.KEBAB, CasesUtils.CAMEL));
+        Assertions.assertEquals("Test_Utils", CasesUtils.convert("TestUtils", 
CasesUtils.PASCAL, CasesUtils.SNAKE));
+    }
+
+    @Test
+    public void testCharacterDelimitedCase() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
KebabCase.INSTANCE.format(Arrays.asList("a", "-")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
SnakeCase.INSTANCE.format(Arrays.asList("a", "_")));
+        CharacterDelimitedCase nullDelimiters = new CharacterDelimitedCase();
+        assertFormat(nullDelimiters, "abc", Arrays.asList("a", "b", "c"));
+        assertParse(nullDelimiters, "abc", Arrays.asList("abc"));
+    }
+
+    @Test
+    public void testKebabCase() {
+        assertFormatAndParse(KebabCase.INSTANCE, "", Arrays.asList());
+        assertParse(KebabCase.INSTANCE, null, Arrays.asList());
+        assertFormatAndParse(KebabCase.INSTANCE, "my-Tokens-123-a1", 
Arrays.asList("my", "Tokens", "123", "a1"));
+        assertFormatAndParse(KebabCase.INSTANCE, "blank--token", 
Arrays.asList("blank", "", "token"));
+    }
+
+    @Test
+    public void testUtf32() {
+        assertFormatAndParse(KebabCase.INSTANCE, 
"\uD800\uDF00-\uD800\uDF01\uD800\uDF14-\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("\uD800\uDF00", "\uD800\uDF01\uD800\uDF14", 
"\uD800\uDF02\uD800\uDF03"));
+        assertFormatAndParse(SnakeCase.INSTANCE, 
"\uD800\uDF00_\uD800\uDF01\uD800\uDF14_\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("\uD800\uDF00", "\uD800\uDF01\uD800\uDF14", 
"\uD800\uDF02\uD800\uDF03"));
+
+        assertFormatAndParse(PascalCase.INSTANCE, 
"A\uD800\uDF00B\uD800\uDF01\uD800\uDF14C\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("A\uD800\uDF00", "B\uD800\uDF01\uD800\uDF14", 
"C\uD800\uDF02\uD800\uDF03"));
+        assertFormatAndParse(CamelCase.INSTANCE, 
"a\uD800\uDF00B\uD800\uDF01\uD800\uDF14C\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("a\uD800\uDF00", "B\uD800\uDF01\uD800\uDF14", 
"C\uD800\uDF02\uD800\uDF03"));
+
+    }
+
+    @Test
+    public void testSnakeCase() {
+        assertFormatAndParse(SnakeCase.INSTANCE, "", Arrays.asList());
+        assertParse(SnakeCase.INSTANCE, null, Arrays.asList());
+        assertFormatAndParse(SnakeCase.INSTANCE, "my_Tokens_123_a1", 
Arrays.asList("my", "Tokens", "123", "a1"));
+        assertFormatAndParse(SnakeCase.INSTANCE, "blank__token", 
Arrays.asList("blank", "", "token"));
+    }
+
+    @Test
+    public void testPascalCase() {
+
+        assertFormatAndParse(PascalCase.INSTANCE, "MyVarName", 
Arrays.asList("My", "Var", "Name"));
+        assertFormatAndParse(PascalCase.INSTANCE, "MyTokensA1D", 
Arrays.asList("My", "Tokens", "A1", "D"));
+        assertFormatAndParse(PascalCase.INSTANCE, "", Arrays.asList());
+        assertParse(PascalCase.INSTANCE, "lowerFirst", Arrays.asList("lower", 
"First"));
+        assertFormat(PascalCase.INSTANCE, "LowerFirst", Arrays.asList("lower", 
"First"));
+
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("1")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("a1", "2c")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("1a")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("")));
+    }
+
+    @Test
+    public void testNumberLetters() {
+
+        // roman numerals - have an upper/lower case but are numbers
+
+        assertFormatAndParse(PascalCase.INSTANCE, "A\u2170\u2160c", 
Arrays.asList("A\u2170", "\u2160c"));
+
+        assertFormat(PascalCase.INSTANCE, "A\u2170Bc", 
Arrays.asList("a\u2160", "bc"));
+        assertParse(PascalCase.INSTANCE, "A\u2170Bc", Arrays.asList("A\u2170", 
"Bc"));
+        assertFormat(PascalCase.INSTANCE, "A\u2170", Arrays.asList("a\u2170"));
+        assertParse(PascalCase.INSTANCE, "A\u2170Bc", Arrays.asList("A\u2170", 
"Bc"));
+
+        assertFormat(CamelCase.INSTANCE, "a\u2170Bc", Arrays.asList("a\u2160", 
"bc"));
+        assertParse(CamelCase.INSTANCE, "\u2160Bc", Arrays.asList("\u2160", 
"Bc"));
+
+    }
+
+    @Test
+    public void testCamelCase() {
+
+        assertFormatAndParse(CamelCase.INSTANCE, "", Arrays.asList());
+        assertFormatAndParse(CamelCase.INSTANCE, "myTokensAbc123", 
Arrays.asList("my", "Tokens", "Abc123"));
+        assertFormatAndParse(CamelCase.INSTANCE, "specChar-Token+", 
Arrays.asList("spec", "Char-", "Token+"));
+
+        assertParse(CamelCase.INSTANCE, "MyTokens", Arrays.asList("My", 
"Tokens"));
+        assertFormat(CamelCase.INSTANCE, "myTokens", Arrays.asList("My", 
"Tokens"));
+
+        // empty token not supported
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
CamelCase.INSTANCE.format(Arrays.asList("a", "b", "")));
+        // must begin with character that can be uppercased
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
CamelCase.INSTANCE.format(Arrays.asList("a", "1b")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
CamelCase.INSTANCE.format(Arrays.asList("1a")));
+    }
+
+    @Test
+    public void testConversionsDelimited() {
+
+        List<String> tokens = Arrays.asList("My", "var", "NAME", "mIXED", 
"a1", "12", "");
+
+        String kebabString = "My-var-NAME-mIXED-a1-12-";
+        assertFormatAndParse(KebabCase.INSTANCE, kebabString, tokens);
+
+        String snakeString = "My_var_NAME_mIXED_a1_12_";
+        assertFormatAndParse(SnakeCase.INSTANCE, snakeString, tokens);
+    }
+
+    @Test
+    public void testConversions() {
+
+        List<String> tokens = Arrays.asList("My", "var", "NAME", "mIXED", 
"a1", "c|=+");

Review Comment:
   These shiould really be separate test methods for easier debugging



##########
src/main/java/org/apache/commons/text/cases/CharacterDelimitedCase.java:
##########
@@ -0,0 +1,151 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.CharUtils;
+
+/**
+ * DelimitedCase is a case in which the true alphabetic case of the characters 
is ignored by default
+ * and tokens themselves are determined by the presence of a delimiter between 
each token.
+ */
+public class CharacterDelimitedCase implements Case {
+
+    /** Delimiters to be used when parsing. */
+    private List<Integer> parseDelimiters;
+
+    /** Delimiter to be used when formatting. */
+    private String formatDelimiter;
+
+    /**
+     * Constructs a new Delimited Case with null delimiters.
+     */
+    CharacterDelimitedCase() {
+        this(null, null);
+    }
+
+    /**
+     * Constructs a new Delimited Case.
+     *
+     * @param delimiter the character to use as both the parse and format 
delimiter
+     */
+    CharacterDelimitedCase(char delimiter) {
+        this(new char[] { delimiter }, CharUtils.toString(delimiter));
+    }
+
+    /**
+     * Constructs a new delimited case.
+     *
+     * @param parseDelimiters the array of delimiters to use when parsing
+     * @param formatDelimiter the delimiter to use when formatting
+     */
+    CharacterDelimitedCase(char[] parseDelimiters, String formatDelimiter) {
+        super();
+        this.parseDelimiters = generateDelimiterList(parseDelimiters);
+        this.formatDelimiter = formatDelimiter;
+    }
+
+    /**
+     * Formats tokens into Delimited Case.
+     * <p>
+     * Tokens are iterated on and appended to an output stream, with an 
instance of a
+     * delimiter character between them. This method validates that the 
delimiter character is not
+     * part of the token. If it is found within the token an exception is 
thrown.<br>
+     * No other restrictions are placed on the contents of the tokens.
+     * Note: This Case does support empty tokens.<br>
+     * </p>
+     *
+     * @param tokens the tokens to be formatted into a delimited string
+     * @return the delimited string
+     * @throws IllegalArgumentException if any tokens contain the delimiter 
character
+     */
+    @Override
+    public String format(Iterable<String> tokens) {
+        StringBuilder formattedString = new StringBuilder();
+        int i = 0;
+        for (String token : tokens) {
+            if (formatDelimiter != null) {
+              int delimiterFoundIndex = token.indexOf(formatDelimiter);
+              if (delimiterFoundIndex > -1) {
+                  throw new IllegalArgumentException("Token " + i + " contains 
delimiter character '" + formatDelimiter + "' at index " + delimiterFoundIndex);
+              }
+              if (i > 0) {
+                  formattedString.append(formatDelimiter);
+              }
+            }
+            i++;
+            formattedString.append(token);
+        }
+        return formattedString.toString();
+    }
+
+    /**
+     * Parses delimited string into tokens.
+     * <p>
+     * Input string is parsed one character at a time until a delimiter 
character is reached.

Review Comment:
   delimiter chaarcter or string?



##########
src/main/java/org/apache/commons/text/cases/CharacterDelimitedCase.java:
##########
@@ -0,0 +1,151 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.CharUtils;
+
+/**
+ * DelimitedCase is a case in which the true alphabetic case of the characters 
is ignored by default

Review Comment:
   comma after default



##########
src/main/java/org/apache/commons/text/cases/Case.java:
##########
@@ -0,0 +1,52 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.List;
+
+/**
+ * Formats and parses tokens to/from a String. In most implementations tokens 
returned
+ * by the parse method abide by any restrictions present in the format method. 
That is, calling
+ * format() with the results of a call to parse() on the same Case instance 
should return a
+ * matching String.
+ *
+ * @since 1.11
+ */
+public interface Case {
+
+    /**
+     * Formats a set of tokens into a string. The tokens do not necessarily 
have to meet the syntax
+     * requirements of the Case. The documentation for each implementation 
should specify what input
+     * is supported.
+     *
+     * @param tokens string tokens to be formatted by this Case
+     * @return the formatted string
+     * @throws IllegalArgumentException if tokens cannot be formatted
+     */
+    String format(Iterable<String> tokens);
+
+    /**
+     * Parses a string into a series of tokens. The string must abide by 
certain restrictions,
+     * dependent on each Case implementation.
+     *
+     * @param string the string to be parsed by this Case into a list of tokens
+     * @return the list of parsed tokens
+     * @throws IllegalArgumentException if the string cannot be parsed

Review Comment:
   cannot be parsed  --> cannot be parsed in the specific format
   
   (or something likle that; I don't have the exact words yet but  I'd like to 
get the notion of the grammar in there somehow)



##########
src/main/java/org/apache/commons/text/cases/CharacterDelimitedCase.java:
##########
@@ -0,0 +1,151 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang3.CharUtils;
+
+/**
+ * DelimitedCase is a case in which the true alphabetic case of the characters 
is ignored by default
+ * and tokens themselves are determined by the presence of a delimiter between 
each token.
+ */
+public class CharacterDelimitedCase implements Case {
+
+    /** Delimiters to be used when parsing. */
+    private List<Integer> parseDelimiters;
+
+    /** Delimiter to be used when formatting. */
+    private String formatDelimiter;
+
+    /**
+     * Constructs a new Delimited Case with null delimiters.
+     */
+    CharacterDelimitedCase() {
+        this(null, null);
+    }
+
+    /**
+     * Constructs a new Delimited Case.
+     *
+     * @param delimiter the character to use as both the parse and format 
delimiter
+     */
+    CharacterDelimitedCase(char delimiter) {
+        this(new char[] { delimiter }, CharUtils.toString(delimiter));
+    }
+
+    /**
+     * Constructs a new delimited case.
+     *
+     * @param parseDelimiters the array of delimiters to use when parsing
+     * @param formatDelimiter the delimiter to use when formatting
+     */
+    CharacterDelimitedCase(char[] parseDelimiters, String formatDelimiter) {
+        super();
+        this.parseDelimiters = generateDelimiterList(parseDelimiters);
+        this.formatDelimiter = formatDelimiter;
+    }
+
+    /**
+     * Formats tokens into Delimited Case.
+     * <p>
+     * Tokens are iterated on and appended to an output stream, with an 
instance of a
+     * delimiter character between them. This method validates that the 
delimiter character is not
+     * part of the token. If it is found within the token an exception is 
thrown.<br>
+     * No other restrictions are placed on the contents of the tokens.
+     * Note: This Case does support empty tokens.<br>
+     * </p>
+     *
+     * @param tokens the tokens to be formatted into a delimited string
+     * @return the delimited string
+     * @throws IllegalArgumentException if any tokens contain the delimiter 
character
+     */
+    @Override
+    public String format(Iterable<String> tokens) {
+        StringBuilder formattedString = new StringBuilder();
+        int i = 0;
+        for (String token : tokens) {
+            if (formatDelimiter != null) {
+              int delimiterFoundIndex = token.indexOf(formatDelimiter);
+              if (delimiterFoundIndex > -1) {
+                  throw new IllegalArgumentException("Token " + i + " contains 
delimiter character '" + formatDelimiter + "' at index " + delimiterFoundIndex);
+              }
+              if (i > 0) {
+                  formattedString.append(formatDelimiter);
+              }
+            }
+            i++;
+            formattedString.append(token);
+        }
+        return formattedString.toString();
+    }
+
+    /**
+     * Parses delimited string into tokens.
+     * <p>
+     * Input string is parsed one character at a time until a delimiter 
character is reached.
+     * When a delimiter character is reached a new token begins. The delimiter 
character is
+     * considered reserved, and is omitted from the returned parsed tokens.<br>
+     * No other restrictions are placed on the contents of the input string. 
<br>
+     * </p>
+     *
+     * @param string the delimited string to be parsed
+     * @return the list of tokens found in the string
+     */
+    @Override
+    public List<String> parse(String string) {
+        List<String> tokens = new ArrayList<>();
+        if (string == null || string.isEmpty()) {
+            return tokens;
+        }
+        int strLen = string.length();
+        int[] tokenCodePoints = new int[strLen];
+        int tokenCodePointsOffset = 0;
+        for (int i = 0; i < string.length();) {
+            final int codePoint = string.codePointAt(i);
+            if (parseDelimiters.contains(codePoint)) {
+                tokens.add(new String(tokenCodePoints, 0, 
tokenCodePointsOffset));
+                tokenCodePoints = new int[strLen];
+                tokenCodePointsOffset = 0;
+                i++;
+            } else {
+                tokenCodePoints[tokenCodePointsOffset++] = codePoint;
+                i += Character.charCount(codePoint);
+            }
+        }
+        tokens.add(new String(tokenCodePoints, 0, tokenCodePointsOffset));
+        return tokens;
+    }
+
+    /**
+     * Converts an array of delimiters to a hash set of code points.

Review Comment:
   has set or list?



##########
src/test/java/org/apache/commons/text/cases/CasesTest.java:
##########
@@ -0,0 +1,268 @@
+/*
+ * 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.commons.text.cases;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class CasesTest {
+
+    @Test
+    public void testCasesUtils() {
+        Assertions.assertEquals("testUtils", CasesUtils.convert("test-utils", 
CasesUtils.KEBAB, CasesUtils.CAMEL));
+        Assertions.assertEquals("Test_Utils", CasesUtils.convert("TestUtils", 
CasesUtils.PASCAL, CasesUtils.SNAKE));
+    }
+
+    @Test
+    public void testCharacterDelimitedCase() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
KebabCase.INSTANCE.format(Arrays.asList("a", "-")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
SnakeCase.INSTANCE.format(Arrays.asList("a", "_")));
+        CharacterDelimitedCase nullDelimiters = new CharacterDelimitedCase();
+        assertFormat(nullDelimiters, "abc", Arrays.asList("a", "b", "c"));
+        assertParse(nullDelimiters, "abc", Arrays.asList("abc"));
+    }
+
+    @Test
+    public void testKebabCase() {
+        assertFormatAndParse(KebabCase.INSTANCE, "", Arrays.asList());
+        assertParse(KebabCase.INSTANCE, null, Arrays.asList());
+        assertFormatAndParse(KebabCase.INSTANCE, "my-Tokens-123-a1", 
Arrays.asList("my", "Tokens", "123", "a1"));
+        assertFormatAndParse(KebabCase.INSTANCE, "blank--token", 
Arrays.asList("blank", "", "token"));
+    }
+
+    @Test
+    public void testUtf32() {
+        assertFormatAndParse(KebabCase.INSTANCE, 
"\uD800\uDF00-\uD800\uDF01\uD800\uDF14-\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("\uD800\uDF00", "\uD800\uDF01\uD800\uDF14", 
"\uD800\uDF02\uD800\uDF03"));
+        assertFormatAndParse(SnakeCase.INSTANCE, 
"\uD800\uDF00_\uD800\uDF01\uD800\uDF14_\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("\uD800\uDF00", "\uD800\uDF01\uD800\uDF14", 
"\uD800\uDF02\uD800\uDF03"));
+
+        assertFormatAndParse(PascalCase.INSTANCE, 
"A\uD800\uDF00B\uD800\uDF01\uD800\uDF14C\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("A\uD800\uDF00", "B\uD800\uDF01\uD800\uDF14", 
"C\uD800\uDF02\uD800\uDF03"));
+        assertFormatAndParse(CamelCase.INSTANCE, 
"a\uD800\uDF00B\uD800\uDF01\uD800\uDF14C\uD800\uDF02\uD800\uDF03",
+                Arrays.asList("a\uD800\uDF00", "B\uD800\uDF01\uD800\uDF14", 
"C\uD800\uDF02\uD800\uDF03"));
+
+    }
+
+    @Test
+    public void testSnakeCase() {
+        assertFormatAndParse(SnakeCase.INSTANCE, "", Arrays.asList());
+        assertParse(SnakeCase.INSTANCE, null, Arrays.asList());
+        assertFormatAndParse(SnakeCase.INSTANCE, "my_Tokens_123_a1", 
Arrays.asList("my", "Tokens", "123", "a1"));
+        assertFormatAndParse(SnakeCase.INSTANCE, "blank__token", 
Arrays.asList("blank", "", "token"));
+    }
+
+    @Test
+    public void testPascalCase() {
+
+        assertFormatAndParse(PascalCase.INSTANCE, "MyVarName", 
Arrays.asList("My", "Var", "Name"));
+        assertFormatAndParse(PascalCase.INSTANCE, "MyTokensA1D", 
Arrays.asList("My", "Tokens", "A1", "D"));
+        assertFormatAndParse(PascalCase.INSTANCE, "", Arrays.asList());
+        assertParse(PascalCase.INSTANCE, "lowerFirst", Arrays.asList("lower", 
"First"));
+        assertFormat(PascalCase.INSTANCE, "LowerFirst", Arrays.asList("lower", 
"First"));
+
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("1")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("a1", "2c")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("1a")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
PascalCase.INSTANCE.format(Arrays.asList("")));
+    }
+
+    @Test
+    public void testNumberLetters() {
+
+        // roman numerals - have an upper/lower case but are numbers
+
+        assertFormatAndParse(PascalCase.INSTANCE, "A\u2170\u2160c", 
Arrays.asList("A\u2170", "\u2160c"));
+
+        assertFormat(PascalCase.INSTANCE, "A\u2170Bc", 
Arrays.asList("a\u2160", "bc"));
+        assertParse(PascalCase.INSTANCE, "A\u2170Bc", Arrays.asList("A\u2170", 
"Bc"));
+        assertFormat(PascalCase.INSTANCE, "A\u2170", Arrays.asList("a\u2170"));
+        assertParse(PascalCase.INSTANCE, "A\u2170Bc", Arrays.asList("A\u2170", 
"Bc"));
+
+        assertFormat(CamelCase.INSTANCE, "a\u2170Bc", Arrays.asList("a\u2160", 
"bc"));
+        assertParse(CamelCase.INSTANCE, "\u2160Bc", Arrays.asList("\u2160", 
"Bc"));
+
+    }
+
+    @Test
+    public void testCamelCase() {
+
+        assertFormatAndParse(CamelCase.INSTANCE, "", Arrays.asList());
+        assertFormatAndParse(CamelCase.INSTANCE, "myTokensAbc123", 
Arrays.asList("my", "Tokens", "Abc123"));
+        assertFormatAndParse(CamelCase.INSTANCE, "specChar-Token+", 
Arrays.asList("spec", "Char-", "Token+"));
+
+        assertParse(CamelCase.INSTANCE, "MyTokens", Arrays.asList("My", 
"Tokens"));
+        assertFormat(CamelCase.INSTANCE, "myTokens", Arrays.asList("My", 
"Tokens"));
+
+        // empty token not supported
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
CamelCase.INSTANCE.format(Arrays.asList("a", "b", "")));
+        // must begin with character that can be uppercased
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
CamelCase.INSTANCE.format(Arrays.asList("a", "1b")));
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
CamelCase.INSTANCE.format(Arrays.asList("1a")));
+    }
+
+    @Test
+    public void testConversionsDelimited() {
+
+        List<String> tokens = Arrays.asList("My", "var", "NAME", "mIXED", 
"a1", "12", "");
+
+        String kebabString = "My-var-NAME-mIXED-a1-12-";

Review Comment:
   
   
   These should be separate test methods for easier debugging
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to