diff --git a/code-generator/pom.xml b/code-generator/pom.xml
index f317bb0..ad623f1 100644
--- a/code-generator/pom.xml
+++ b/code-generator/pom.xml
@@ -46,11 +46,6 @@
guava
-
- org.jibx
- jibx-tools
-
-
org.jboss.forge.roaster
roaster-api
@@ -60,6 +55,12 @@
roaster-jdt
runtime
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
diff --git a/code-generator/src/main/java/io/streamnative/lightproto/generator/Util.java b/code-generator/src/main/java/io/streamnative/lightproto/generator/Util.java
index d5151ce..1c407cf 100644
--- a/code-generator/src/main/java/io/streamnative/lightproto/generator/Util.java
+++ b/code-generator/src/main/java/io/streamnative/lightproto/generator/Util.java
@@ -17,9 +17,6 @@
import java.io.PrintWriter;
-import org.jibx.schema.codegen.extend.DefaultNameConverter;
-import org.jibx.schema.codegen.extend.NameConverter;
-
import static com.google.common.base.CaseFormat.LOWER_CAMEL;
import static com.google.common.base.CaseFormat.LOWER_UNDERSCORE;
@@ -66,14 +63,37 @@ public static String upperCase(String... parts) {
return sb.toString().toUpperCase();
}
- private static final NameConverter nameTools = new DefaultNameConverter();
-
- public static String plural(String s) {
- return nameTools.pluralize(s);
+ // pluralize/singular rules vendored from JiBX NameUtilities
+ // (Copyright (c) 2008-2010, Dennis M. Sosnoski, BSD 3-clause license).
+ public static String plural(String name) {
+ if (name.endsWith("List") || (name.endsWith("s") && !name.endsWith("ss"))) {
+ return name;
+ }
+ if (name.endsWith("y") && !name.endsWith("ay") && !name.endsWith("ey") && !name.endsWith("iy")
+ && !name.endsWith("oy") && !name.endsWith("uy")) {
+ if (name.equalsIgnoreCase("any")) {
+ return name;
+ }
+ return name.substring(0, name.length() - 1) + "ies";
+ } else if (name.endsWith("ss")) {
+ return name + "es";
+ } else {
+ return name + 's';
+ }
}
- public static String singular(String s) {
- return nameTools.depluralize(s);
+ public static String singular(String name) {
+ if (name.endsWith("ies")) {
+ return name.substring(0, name.length() - 3) + 'y';
+ } else if (name.endsWith("sses")) {
+ return name.substring(0, name.length() - 2);
+ } else if (name.endsWith("s") && !name.endsWith("ss")) {
+ return name.substring(0, name.length() - 1);
+ } else if (name.endsWith("List")) {
+ return name.substring(0, name.length() - 4);
+ } else {
+ return name;
+ }
}
public static void writeJavadoc(PrintWriter w, String doc, String indent) {
diff --git a/code-generator/src/test/java/io/streamnative/lightproto/generator/UtilTest.java b/code-generator/src/test/java/io/streamnative/lightproto/generator/UtilTest.java
new file mode 100644
index 0000000..f63fbaa
--- /dev/null
+++ b/code-generator/src/test/java/io/streamnative/lightproto/generator/UtilTest.java
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2026 StreamNative
+ *
+ * Licensed 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 io.streamnative.lightproto.generator;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+/**
+ * Locks the pluralize/singular behavior inherited from JiBX NameUtilities.
+ * These functions feed into generated public API names (e.g. {@code addItem},
+ * {@code getItemsCount}), so changing their output is an ABI break.
+ */
+class UtilTest {
+
+ @ParameterizedTest
+ @CsvSource({
+ // default case: append 's'
+ "item, items",
+ "book, books",
+ "Message, Messages",
+ // ends with 'ss': append 'es'
+ "address, addresses",
+ "class, classes",
+ // ends with consonant+'y': replace with 'ies'
+ "city, cities",
+ "category, categories",
+ "company, companies",
+ // ends with vowel+'y': default rule, append 's'
+ "day, days",
+ "key, keys",
+ "boy, boys",
+ "guy, guys",
+ "way, ways",
+ // 'any' is a hardcoded exception (case-insensitive match), but
+ // only reached when endsWith("y") matches, which is case-sensitive.
+ // So "ANY" slips through to the default rule.
+ "any, any",
+ "Any, Any",
+ "ANY, ANYs",
+ // already plural ('s' but not 'ss'): unchanged
+ "items, items",
+ "books, books",
+ // ends with 'List': treated as already plural
+ "itemList, itemList",
+ "userList, userList",
+ })
+ void pluralizes(String input, String expected) {
+ assertEquals(expected, Util.plural(input));
+ }
+
+ @ParameterizedTest
+ @CsvSource({
+ // ends with 'ies': replace with 'y'
+ "cities, city",
+ "categories, category",
+ "companies, company",
+ // ends with 'sses': strip 'es'
+ "addresses, address",
+ "classes, class",
+ // ends with 's' (not 'ss'): strip 's'
+ "items, item",
+ "books, book",
+ "days, day",
+ // ends with 'List': strip suffix
+ "itemList, item",
+ "userList, user",
+ // ends with 'ss': unchanged (not a plural)
+ "address, address",
+ "class, class",
+ // no recognized plural marker: unchanged
+ "item, item",
+ "any, any",
+ })
+ void singularizes(String input, String expected) {
+ assertEquals(expected, Util.singular(input));
+ }
+}
diff --git a/pom.xml b/pom.xml
index bee3e9f..ce61a10 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,7 +68,6 @@
5.7.0
32.0.0-jre
4.1.131.Final
- 1.3.3
2.22.2.Final
1.68.0
@@ -177,12 +176,6 @@
${protobuf.version}
-
- org.jibx
- jibx-tools
- ${jibx.version}
-
-
org.openjdk.jmh
jmh-core