From 978529b355c0b7238193a1831fa49e324eb68d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=B6kdemir?= Date: Fri, 25 Sep 2026 21:04:16 +0300 Subject: [PATCH 1/2] fix: avoid reflective help formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Efe Gökdemir --- .../maven/plugins/help/DescribeMojo.java | 100 +++++++++++------- .../maven/plugins/help/DescribeMojoTest.java | 13 +++ 2 files changed, 77 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java b/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java index 17948d6c..dd924619 100644 --- a/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java +++ b/src/main/java/org/apache/maven/plugins/help/DescribeMojo.java @@ -22,8 +22,6 @@ import java.io.File; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -733,48 +731,78 @@ private boolean describeCommand(StringBuilder descriptionBuffer) throws MojoExec } /** - * Invoke the following private method - * HelpMojo#toLines(String, int, int, int) + * Splits and wraps display text without depending on the generated help mojo implementation. * - * @param text The text to split into lines, must not be null. - * @param indent The base indentation level of each line, must not be negative. - * @param indentSize The size of each indentation, must not be negative. - * @param lineLength The length of the line, must not be negative. - * @return The sequence of display lines, never null. - * @throws MojoFailureException if any can not invoke the method - * @throws MojoExecutionException if no line was found for text + * @param text the text to split + * @param indent the base indentation level of each line + * @param indentSize the size of each indentation + * @param lineLength the maximum line length + * @return the display lines + * @throws MojoFailureException if the requested indentation cannot be represented safely */ private static List toLines(String text, int indent, int indentSize, int lineLength) - throws MojoFailureException, MojoExecutionException { - try { - Method m = - HelpMojo.class.getDeclaredMethod("toLines", String.class, Integer.TYPE, Integer.TYPE, Integer.TYPE); - m.setAccessible(true); - @SuppressWarnings("unchecked") - List output = (List) m.invoke(HelpMojo.class, text, indent, indentSize, lineLength); - - if (output == null) { - throw new MojoExecutionException("No output was specified."); + throws MojoFailureException { + List lines = new ArrayList<>(); + String ind = repeat("\t", indent); + + for (String plainLine : text.split("(\r\n)|(\r)|(\n)")) { + addLines(lines, ind + plainLine, indentSize, lineLength); + } + return lines; + } + + private static void addLines(List lines, String line, int indentSize, int lineLength) + throws MojoFailureException { + int lineIndent = getIndentLevel(line); + StringBuilder buf = new StringBuilder(256); + + for (String token : line.split(" +")) { + if (buf.length() > 0) { + if (buf.length() + token.length() >= lineLength) { + lines.add(buf.toString()); + buf.setLength(0); + buf.append(repeat(" ", Math.multiplyExact(lineIndent, indentSize))); + } else { + buf.append(' '); + } } + for (int j = 0; j < token.length(); j++) { + char c = token.charAt(j); + if (c == '\t') { + buf.append(repeat(" ", indentSize - buf.length() % indentSize)); + } else if (c == '\u00A0') { + buf.append(' '); + } else { + buf.append(c); + } + } + } + lines.add(buf.toString()); + } - return output; - } catch (SecurityException e) { - throw new MojoFailureException("SecurityException: " + e.getMessage()); - } catch (IllegalArgumentException e) { - throw new MojoFailureException("IllegalArgumentException: " + e.getMessage()); - } catch (NoSuchMethodException e) { - throw new MojoFailureException("NoSuchMethodException: " + e.getMessage()); - } catch (IllegalAccessException e) { - throw new MojoFailureException("IllegalAccessException: " + e.getMessage()); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - - if (cause instanceof NegativeArraySizeException) { - throw new MojoFailureException("NegativeArraySizeException: " + cause.getMessage()); + private static int getIndentLevel(String line) { + int level = 0; + for (int i = 0; i < line.length() && line.charAt(i) == '\t'; i++) { + level++; + } + for (int i = level + 1; i <= level + 4 && i < line.length(); i++) { + if (line.charAt(i) == '\t') { + level++; + break; } + } + return level; + } - throw new MojoFailureException("InvocationTargetException: " + e.getMessage()); + private static String repeat(String str, int repeat) throws MojoFailureException { + if (repeat < 0 || ((long) repeat * str.length()) >= Integer.MAX_VALUE) { + throw new MojoFailureException("Requested indentation is too large"); + } + StringBuilder result = new StringBuilder((int) ((long) repeat * str.length())); + for (int i = 0; i < repeat; i++) { + result.append(str); } + return result.toString(); } /** diff --git a/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java b/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java index 7083f9c4..2eed98ff 100644 --- a/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java @@ -36,6 +36,7 @@ import org.apache.maven.model.Plugin; import org.apache.maven.plugin.MavenPluginManager; import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.Parameter; import org.apache.maven.plugin.descriptor.PluginDescriptor; @@ -50,6 +51,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -76,6 +78,17 @@ void testGetExpressionsRoot() toLines.invoke(null, "", 2, 2, 80); } + @Test + void testToLinesRejectsOverflowingIndentation() throws Exception { + Method toLines = DescribeMojo.class.getDeclaredMethod("toLines", String.class, int.class, int.class, int.class); + toLines.setAccessible(true); + + InvocationTargetException exception = assertThrows( + InvocationTargetException.class, () -> toLines.invoke(null, "text", Integer.MAX_VALUE, 2, 80)); + + assertTrue(exception.getCause() instanceof MojoFailureException); + } + @Test void testValidExpression() throws Exception { StringBuilder sb = new StringBuilder(); From 94e3b26bb2c72ff9b2f145378775261c501a7a82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Efe=20G=C3=B6kdemir?= Date: Tue, 13 Oct 2026 03:17:01 +0300 Subject: [PATCH 2/2] style: order DescribeMojoTest assertions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Efe Gökdemir --- .../java/org/apache/maven/plugins/help/DescribeMojoTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java b/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java index 2eed98ff..090d79ac 100644 --- a/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java +++ b/src/test/java/org/apache/maven/plugins/help/DescribeMojoTest.java @@ -51,8 +51,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any;