diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/EscapeUtils.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/EscapeUtils.java new file mode 100644 index 0000000000..5166750757 --- /dev/null +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/EscapeUtils.java @@ -0,0 +1,147 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import java.util.List; + +public final class EscapeUtils { + private EscapeUtils() { + // This is a utility class + } + + public static String escapeMarkdown(String unescaped) { + return unescaped.replace("\\", "\\\\") + .replace("*", "\\*") + .replace("_", "\\_") + .replace("~", "\\~") + .replace("[", "\\[") + .replace("]", "\\]") + .replace("|", "\\|"); + } + + private enum State { + S, LT, LT_H, LT_H_T, LT_H_T_T, LT_H_T_T_P, LT_H_T_T_P1, LT_H_T_T_P_S, LT_H_T_T_P_S1; + } + + public static String escapeSingleLine(String line) { + StringBuilder escaped = new StringBuilder(line.length() + 16); + State s = State.S; + boolean needsEscape = true; + for (int i = 0; i < line.length(); i++) { + char c = line.charAt(i); + if (c == '`') { + needsEscape = !needsEscape; + } + switch (s) { + case S: + if (c == '<') { + s = State.LT; + } else if (c == '>') { + if (needsEscape && i > 0) { + escaped.append(">"); + } else { + escaped.append(c); + } + } else if (c == '"') { + if (needsEscape) { + escaped.append("""); + } else { + escaped.append(c); + } + } else { + escaped.append(c); + } + break; + case LT: + if (c == 'h' || c == 'H') { + s = State.LT_H; + } else { + if (needsEscape) { + escaped.append("<").append(c); + } else { + escaped.append("<").append(c); + } + s = State.S; + } + break; + case LT_H: + if (c == 't' || c == 'T') { + s = State.LT_H_T; + } else { + escaped.append("<h").append(c); + s = State.S; + } + break; + case LT_H_T: + if (c == 't' || c == 'T') { + s = State.LT_H_T_T; + } else { + escaped.append("<ht").append(c); + s = State.S; + } + break; + case LT_H_T_T: + if (c == 'p' || c == 'P') { + s = State.LT_H_T_T_P; + } else { + escaped.append("<htt").append(c); + s = State.S; + } + break; + case LT_H_T_T_P: + if (c == 's' || c == 'S') { + s = State.LT_H_T_T_P_S; + } else if (c == ':') { + escaped.append("') { + s = State.S; + } + break; + case LT_H_T_T_P_S: + if (c == ':') { + escaped.append("') { + s = State.S; + } + break; + default: + escaped.append(c); + break; + } + } + return escaped.toString(); + } + + public static List escapeLines(List lines) { + boolean needsEscape = true; + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + if (line.startsWith("```")) { + needsEscape = !needsEscape; + } + if (needsEscape && !line.startsWith(" ")) { + line = escapeSingleLine(line); + } + lines.set(i, line); + } + return lines; + } +} diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index 19d22dbcad..16fe848df0 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -395,7 +395,7 @@ public class RuleDocGenerator { lines.add(""); } - lines.addAll(escapeLines(toLines(stripIndentation(rule.getDescription())))); + lines.addAll(EscapeUtils.escapeLines(toLines(stripIndentation(rule.getDescription())))); lines.add(""); if (rule instanceof XPathRule || rule instanceof RuleReference && ((RuleReference) rule).getRule() instanceof XPathRule) { @@ -468,11 +468,11 @@ public class RuleDocGenerator { + multiValuePropertyDescriptor.multiValueDelimiter() + "'."; } - lines.add("|" + escapeMarkdown(StringEscapeUtils.escapeHtml4(propertyDescriptor.name())) - + "|" + escapeMarkdown(StringEscapeUtils.escapeHtml4(defaultValue)) + "|" - + escapeMarkdown((isDeprecated ? DEPRECATION_LABEL_SMALL : "") + lines.add("|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(propertyDescriptor.name())) + + "|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(defaultValue)) + "|" + + EscapeUtils.escapeMarkdown((isDeprecated ? DEPRECATION_LABEL_SMALL : "") + StringEscapeUtils.escapeHtml4(description)) - + "|" + escapeMarkdown(StringEscapeUtils.escapeHtml4(multiValued)) + "|"); + + "|" + EscapeUtils.escapeMarkdown(StringEscapeUtils.escapeHtml4(multiValued)) + "|"); } lines.add(""); } @@ -615,127 +615,4 @@ public class RuleDocGenerator { return FilenameUtils.normalize(relativeSourceFilename, true); } - - private static String escapeMarkdown(String unescaped) { - return unescaped.replace("\\", "\\\\").replace("*", "\\*").replace("_", "\\_").replace("~", "\\~") - .replace("[", "\\[").replace("]", "\\]").replace("|", "\\|"); - } - - private enum State { - S, LT, LT_H, LT_H_T, LT_H_T_T, LT_H_T_T_P, LT_H_T_T_P1, LT_H_T_T_P_S, LT_H_T_T_P_S1; - } - - private static String escapeSingleLine(String line) { - StringBuilder escaped = new StringBuilder(line.length() + 16); - State s = State.S; - boolean needsEscape = true; - for (int i = 0; i < line.length(); i++) { - char c = line.charAt(i); - if (c == '`') { - needsEscape = !needsEscape; - } - switch (s) { - case S: - if (c == '<') { - s = State.LT; - } else if (c == '>') { - if (needsEscape) { - escaped.append(">"); - } else { - escaped.append(c); - } - } else { - escaped.append(c); - } - break; - case LT: - if (c == 'h' || c == 'H') { - s = State.LT_H; - } else { - if (needsEscape) { - escaped.append("<").append(c); - } else { - escaped.append("<").append(c); - } - s = State.S; - } - break; - case LT_H: - if (c == 't' || c == 'T') { - s = State.LT_H_T; - } else { - escaped.append("<h").append(c); - s = State.S; - } - break; - case LT_H_T: - if (c == 't' || c == 'T') { - s = State.LT_H_T_T; - } else { - escaped.append("<ht").append(c); - s = State.S; - } - break; - case LT_H_T_T: - if (c == 'p' || c == 'P') { - s = State.LT_H_T_T_P; - } else { - escaped.append("<htt").append(c); - s = State.S; - } - break; - case LT_H_T_T_P: - if (c == 's' || c == 'S') { - s = State.LT_H_T_T_P_S; - } else if (c == ':') { - escaped.append("') { - s = State.S; - } - break; - case LT_H_T_T_P_S: - if (c == ':') { - escaped.append("') { - s = State.S; - } - break; - default: - escaped.append(c); - break; - } - } - return escaped.toString(); - } - - private static List escapeLines(List lines) { - boolean needsEscape = true; - for (int i = 0; i < lines.size(); i++) { - String line = lines.get(i); - if (line.startsWith("```")) { - needsEscape = !needsEscape; - } - if (needsEscape && !line.startsWith(" ") && !line.startsWith(">")) { - line = escapeSingleLine(line); - } - lines.set(i, line); - } - return lines; - } } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/EscapeUtilsTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/EscapeUtilsTest.java new file mode 100644 index 0000000000..1010f375ad --- /dev/null +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/EscapeUtilsTest.java @@ -0,0 +1,56 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class EscapeUtilsTest { + + @Test + public void testEscapeMarkdown() { + assertEquals("This is a \\\\backslash", EscapeUtils.escapeMarkdown("This is a \\backslash")); + assertEquals("This \"\\*\" is not a emphasis", EscapeUtils.escapeMarkdown("This \"*\" is not a emphasis")); + assertEquals("This \"\\*\\*\" is not a strong style", EscapeUtils.escapeMarkdown("This \"**\" is not a strong style")); + assertEquals("This \"\\[foo\\]\" does not start a link", EscapeUtils.escapeMarkdown("This \"[foo]\" does not start a link")); + assertEquals("This \"\\~bar\\~\" is not a strike-through", EscapeUtils.escapeMarkdown("This \"~bar~\" is not a strike-through")); + assertEquals("That's \"\\|\" just a bar", EscapeUtils.escapeMarkdown("That's \"|\" just a bar")); + assertEquals("This \"\\_\" is just a underscore", EscapeUtils.escapeMarkdown("This \"_\" is just a underscore")); + } + + @Test + public void testEscapeHtmlWithinMarkdownSingleLine() { + assertEquals("a <script> tag outside of `