diff --git a/src/main/java/org/apache/commons/lang3/StringUtils.java b/src/main/java/org/apache/commons/lang3/StringUtils.java index 70c26ee315b..482f46be1b8 100644 --- a/src/main/java/org/apache/commons/lang3/StringUtils.java +++ b/src/main/java/org/apache/commons/lang3/StringUtils.java @@ -9332,6 +9332,51 @@ public static String wrapIfMissing(final String str, final String wrapWith) { return builder.toString(); } + /** + * Converts a camelCase string to snake_case. + *
+ * StringUtils.toSnakeCase(null) = null
+ * StringUtils.toSnakeCase("") = ""
+ * StringUtils.toSnakeCase("simple") = "simple"
+ * StringUtils.toSnakeCase("camelCase") = "camel_case"
+ * StringUtils.toSnakeCase("CamelCase") = "camel_case"
+ * StringUtils.toSnakeCase("thisIsATest") = "this_is_a_test"
+ * StringUtils.toSnakeCase("JSONParser") = "json_parser"
+ * StringUtils.toSnakeCase("already_snake_case") = "already_snake_case"
+ * StringUtils.toSnakeCase("A") = "a"
+ * StringUtils.toSnakeCase("HelloWorld") = "hello_world"
+ * StringUtils.toSnakeCase("multipleUPPERCases") = "multiple_upper_cases"
+ *
+ * @param str the camelCase string to be converted, may be {@code null}
+ * @return the snake_case version of the input string
+ */
+ public static String toSnakeCase(String str) {
+ if (str == null || str.isEmpty()) {
+ return str;
+ }
+
+ StringBuilder result = new StringBuilder();
+ char prevChar = str.charAt(0);
+ result.append(Character.toLowerCase(prevChar)); // Initialize with the first character in lowercase
+
+ for (int i = 1; i < str.length(); i++) {
+ char currentChar = str.charAt(i);
+
+ if (Character.isUpperCase(currentChar)) {
+ // Add underscore if the previous character is not uppercase or if the next character is lowercase
+ if (!Character.isUpperCase(prevChar) || (i + 1 < str.length() && Character.isLowerCase(str.charAt(i + 1)))) {
+ result.append('_');
+ }
+ result.append(Character.toLowerCase(currentChar));
+ } else {
+ result.append(currentChar);
+ }
+
+ prevChar = currentChar;
+ }
+ return result.toString();
+ }
+
/**
* {@link StringUtils} instances should NOT be constructed in
* standard programming. Instead, the class should be used as
diff --git a/src/test/java/org/apache/commons/lang3/StringUtilsTest.java b/src/test/java/org/apache/commons/lang3/StringUtilsTest.java
index 8f15669b329..433a251468d 100644
--- a/src/test/java/org/apache/commons/lang3/StringUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/StringUtilsTest.java
@@ -3431,4 +3431,21 @@ public void testWrapIfMissing_StringString() {
assertSame("ab/ab", StringUtils.wrapIfMissing("ab/ab", "ab"));
assertSame("//x//", StringUtils.wrapIfMissing("//x//", "//"));
}
+
+ /**
+ * Tests {@code toSnakeCase}.
+ */
+ @Test
+ public void testToSnakeCase() {
+ assertEquals("camel_case", StringUtils.toSnakeCase("camelCase"));
+ assertEquals("json_parser", StringUtils.toSnakeCase("JSONParser"));
+ assertEquals("simple", StringUtils.toSnakeCase("simple"));
+ assertEquals("this_is_a_test_string", StringUtils.toSnakeCase("thisIsATestString"));
+ assertEquals("", StringUtils.toSnakeCase(""));
+ assertEquals("a", StringUtils.toSnakeCase("A"));
+ assertEquals("hello_world", StringUtils.toSnakeCase("HelloWorld"));
+ assertEquals("multiple_upper_cases", StringUtils.toSnakeCase("multipleUPPERCases"));
+ assertNull(StringUtils.toSnakeCase(null));
+ assertEquals("already_snake_case", StringUtils.toSnakeCase("already_snake_case"));
+ }
}