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..090d79ac 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;
@@ -51,6 +52,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
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;
@@ -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();