This is an automated email from the ASF dual-hosted git repository. claude pushed a commit to branch TEXT-217_Add_SnakeCase_Parsing in repository https://gitbox.apache.org/repos/asf/commons-text.git
commit 7532dcb7bd0f8905391a491d428f195e8d7cbb0e Author: Claude Warren <cla...@xenei.com> AuthorDate: Sat May 25 11:42:22 2024 +0200 initial implementation --- .../java/org/apache/commons/text/CasedString.java | 143 +++++++++++++++++++++ .../org/apache/commons/text/CasedStringTest.java | 47 +++++++ 2 files changed, 190 insertions(+) diff --git a/src/main/java/org/apache/commons/text/CasedString.java b/src/main/java/org/apache/commons/text/CasedString.java new file mode 100644 index 00000000..a89cd862 --- /dev/null +++ b/src/main/java/org/apache/commons/text/CasedString.java @@ -0,0 +1,143 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Handles converting from one string case to another (e.g. camel case to snake case). + */ +public class CasedString { + private String string; + private StringCase stringCase; + + /** + * A method to join camel string fragments together. + */ + private final static Function<String[],String> camelJoiner = a -> { + StringBuilder sb = new StringBuilder(a[0]); + + for (int i=1;i<a.length;i++) { + sb.append(WordUtils.capitalize(a[i])); + } + return sb.toString(); + }; + + /** + * An enumeration of supported string cases. + */ + public enum StringCase { + /** + * Camel case identifies strings like 'CamelCase'. + */ + Camel(Character::isUpperCase, true, camelJoiner), + /** + * Snake case identifies strings like 'Snake_Case' + */ + Snake(c -> c =='_', false, a -> String.join("_", a)), + /** + * Kebab case identifies strings like 'kebab-case' + */ + Kebab(c -> c == '-', false, a -> String.join("-", a)), + + /** + * Phrase case identifies phrases of words like 'phrase case' + */ + Phrase(c -> c == ' ', false, a -> String.join(" ", a)), + + /** + * Dot case identifies strings of words like 'dot.case' + */ + Dot(c -> c == '.', false, a -> String.join(".", a)); + + private final Predicate<Character> splitter; + private final boolean preserveSplit; + private final Function<String[],String> 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<Character> splitter, final boolean preserveSplit, final Function<String[],String> joiner) { + this.splitter = splitter; + this.preserveSplit = preserveSplit; + this.joiner = joiner; + } + } + + /** + * 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(StringCase stringCase, String string) { + this.string = string; + 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]); + } + + /** + * 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 fomrat to convert to. + * @return the String current string represented in the new format. + */ + public String toCase(StringCase stringCase) { + if (stringCase == this.stringCase) { + return string; + } + return stringCase.joiner.apply(split()); + } + + /** + * Returns the string representation provided in the constructor. + * @return the string representation. + */ + @Override + public String toString() { + return string; + } +} diff --git a/src/test/java/org/apache/commons/text/CasedStringTest.java b/src/test/java/org/apache/commons/text/CasedStringTest.java new file mode 100644 index 00000000..14ccc781 --- /dev/null +++ b/src/test/java/org/apache/commons/text/CasedStringTest.java @@ -0,0 +1,47 @@ +package org.apache.commons.text; + +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.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CasedStringTest { + + private static String expectedValue(CasedString.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"; + default: + throw new RuntimeException("Unsupported StringCase: " + stringCase); + } + } + + @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); + } + } + + 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(); + } +}