Repository: avaje/avaje-inject
Pull request #826 (2025-06-08 15:36:22)

fix: handle nested java types like Flow.Publisher in Util.shortName
This PR fixes a bug in Util.shortName(...) where nested types like java.util.concurrent.Flow.Publisher were resolved incorrectly as Publisher, which caused generated code to fail compilation when the short name was not imported correctly.


Patch:
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java
index fc6f47cb..1221ee56 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/Util.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/Util.java
@@ -141,26 +141,26 @@ final class Util {
     final int p = fullType.lastIndexOf('.');
     if (p == -1) {
       return fullType;
-    } else if (fullType.startsWith("java")) {
-      return fullType.substring(p + 1);
-    } else {
-      var result = "";
-      var foundClass = false;
-      for (final String part : fullType.split("\\.")) {
-        char firstChar = part.charAt(0);
-        if (foundClass
-          || Character.isUpperCase(firstChar)
-          || !Character.isAlphabetic(firstChar) && Character.isJavaIdentifierStart(firstChar)) {
-          foundClass = true;
-          result += (result.isEmpty() ? "" : ".") + part;
-        }
+    }
+
+    String[] parts = fullType.split("\\.");
+    StringBuilder result = new StringBuilder();
+    boolean foundClass = false;
+
+    for (String part : parts) {
+      char firstChar = part.charAt(0);
+      if (!foundClass && Character.isUpperCase(firstChar)) {
+        foundClass = true;
       }
-      // when in doubt, do the basic thing
-      if (result.isBlank()) {
-        return fullType.substring(p + 1);
+      if (foundClass) {
+        if (result.length() > 0) {
+          result.append(".");
+        }
+        result.append(part);
       }
-      return result;
     }
+
+    return result.length() > 0 ? result.toString() : fullType.substring(p + 1);
   }
 
   static String shortName(UType uType) {


Test patch:
diff --git a/inject-generator/src/test/java/io/avaje/inject/generator/UtilTest.java b/inject-generator/src/test/java/io/avaje/inject/generator/UtilTest.java
index 61cf8683..5f826b6c 100644
--- a/inject-generator/src/test/java/io/avaje/inject/generator/UtilTest.java
+++ b/inject-generator/src/test/java/io/avaje/inject/generator/UtilTest.java
@@ -144,4 +144,12 @@ class UtilTest {
     assertEquals("my.Foo", Util.sanitizeImports("@org.bar.annotationMcgee my.Foo"));
     assertEquals("java.util.String", Util.sanitizeImports("java.util.String>"));
   }
+
+  @Test
+  void testShortName_nestedTypes() {
+    assertEquals("Flow.Publisher", Util.shortName("java.util.concurrent.Flow.Publisher"));
+    assertEquals("Outer.Inner", Util.shortName("com.foo.Outer.Inner"));
+    assertEquals("Only", Util.shortName("a.b.c.Only"));
+    assertEquals("simple", Util.shortName("simple"));
+  }
 }
diff --git a/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/generic/GenericImpl.java b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/generic/GenericImpl.java
new file mode 100644
index 00000000..e4e2db50
--- /dev/null
+++ b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/generic/GenericImpl.java
@@ -0,0 +1,12 @@
+package io.avaje.inject.generator.models.valid.generic;
+
+import jakarta.inject.Singleton;
+import java.util.concurrent.Flow;
+import java.util.concurrent.SubmissionPublisher;
+
+@Singleton
+public class GenericImpl implements GenericInterfaceObject<Flow.Publisher<Object>> {
+    public Flow.Publisher<Object> get() {
+        return new SubmissionPublisher();
+    }
+}
diff --git a/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/generic/GenericInterfaceObject.java b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/generic/GenericInterfaceObject.java
new file mode 100644
index 00000000..523b1b8c
--- /dev/null
+++ b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/generic/GenericInterfaceObject.java
@@ -0,0 +1,5 @@
+package io.avaje.inject.generator.models.valid.generic;
+
+public interface GenericInterfaceObject<T> {
+    T get();
+}