Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -122,40 +122,6 @@ public static List<ComponentDependency> toComponentDependencies(Collection<Artif
return componentDeps;
}

/**
* Returns a literal replacement <code>String</code> for the specified <code>String</code>. This method
* produces a <code>String</code> that will work as a literal replacement <code>s</code> in the
* <code>appendReplacement</code> method of the {@link Matcher} class. The <code>String</code> produced will
* match the sequence of characters in <code>s</code> treated as a literal sequence. Slashes ('\') and dollar
* signs ('$') will be given no special meaning. TODO: copied from Matcher class of Java 1.5, remove once target
* platform can be upgraded
*
* @see <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/Matcher.html">java.util.regex.Matcher</a>
* @param s The string to be literalized
* @return A literal string replacement
*/
private static String quoteReplacement(String s) {
if ((s.indexOf('\\') == -1) && (s.indexOf('$') == -1)) {
return s;
}

StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '\\') {
sb.append('\\');
sb.append('\\');
} else if (c == '$') {
sb.append('\\');
sb.append('$');
} else {
sb.append(c);
}
}

return sb.toString();
}

/**
* Decodes javadoc inline tags into equivalent HTML tags. For instance, the inline tag "{@code <A&B>}" should be
* rendered as "<code>&lt;A&amp;B&gt;</code>".
Expand All @@ -170,12 +136,20 @@ static String decodeJavadocTags(String description) {
return "";
}

StringBuffer decoded = new StringBuffer(description.length() + 1024);
StringBuilder decoded = new StringBuilder(description.length() + 1024);

Matcher matcher = Pattern.compile("\\{@(\\w+)\\s*").matcher(description);
int copiedUntil = 0;
int searchFrom = 0;
while (matcher.find(searchFrom)) {
int tagEnd = findInlineTagEnd(description, matcher.end());
if (tagEnd < 0) {
searchFrom = matcher.start() + 2;
continue;
}

Matcher matcher = Pattern.compile("\\{@(\\w+)\\s*([^\\}]*)\\}").matcher(description);
while (matcher.find()) {
String tag = matcher.group(1);
String text = matcher.group(2);
String text = description.substring(matcher.end(), tagEnd);
text = text.replace("&", "&amp;");
text = text.replace("<", "&lt;");
text = text.replace(">", "&gt;");
Expand Down Expand Up @@ -210,13 +184,31 @@ static String decodeJavadocTags(String description) {
text = "<code>" + text + "</code>";
}
}
matcher.appendReplacement(decoded, (text != null) ? quoteReplacement(text) : "");

decoded.append(description, copiedUntil, matcher.start());
decoded.append((text != null) ? text : "");
copiedUntil = tagEnd + 1;
searchFrom = copiedUntil;
}
matcher.appendTail(decoded);
decoded.append(description, copiedUntil, description.length());

return decoded.toString();
}

private static int findInlineTagEnd(String description, int contentStart) {
int braceDepth = 1;
for (int i = contentStart; i < description.length(); i++) {
char c = description.charAt(i);
// Backslashes are literal characters in Javadoc; only braces affect tag nesting.
if (c == '{') {
braceDepth++;
} else if (c == '}' && --braceDepth == 0) {
return i;
}
}
return -1;
}

/**
* Fixes some javadoc comment to become a valid XHTML snippet.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,44 @@ void excludeProvidedScopeFormComponentDependencies() {
assertEquals(a3.getVersion(), componentDependency2.getVersion());
assertEquals(a3.getType(), componentDependency2.getType());
}

@Test
void decodeJavadocCodeWithNestedClosingBrace() {
String description = "{@code <javaAgents>\n"
+ " <agent>\n"
+ " <agentPath>${project.basedir}/lib/agent.jar</agentPath>\n"
+ " </agent>\n"
+ "</javaAgents>\n} after";

assertEquals(
"<code>&lt;javaAgents&gt;\n"
+ " &lt;agent&gt;\n"
+ " &lt;agentPath&gt;${project.basedir}/lib/agent.jar&lt;/agentPath&gt;\n"
+ " &lt;/agent&gt;\n"
+ "&lt;/javaAgents&gt;\n</code> after",
GeneratorUtils.decodeJavadocTags(description));
}

@Test
void decodeJavadocCodeWithMultipleLevelsOfNestedBraces() {
String description = "before {@link java.lang.String label} {@code ${outer${inner}} and "
+ "\\{braces\\} and <x>} after {@link java.lang.Object second}";

assertEquals(
"before <code>java.lang.String label</code> "
+ "<code>${outer${inner}} and \\{braces\\} and &lt;x&gt;</code> after "
+ "<code>java.lang.Object second</code>",
GeneratorUtils.decodeJavadocTags(description));
}

@Test
void decodeJavadocCodeEndingWithBackslashes() {
assertEquals("before <code>C:\\</code> after", GeneratorUtils.decodeJavadocTags("before {@code C:\\} after"));
assertEquals("<code>C:\\\\</code>", GeneratorUtils.decodeJavadocTags("{@code C:\\\\}"));
}

@Test
void decodeJavadocLiteralEndingWithBackslash() {
assertEquals("before C:\\ after", GeneratorUtils.decodeJavadocTags("before {@literal C:\\} after"));
}
}