Return-Path: X-Original-To: apmail-tapestry-dev-archive@www.apache.org Delivered-To: apmail-tapestry-dev-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2318411427 for ; Sat, 17 May 2014 13:18:49 +0000 (UTC) Received: (qmail 76152 invoked by uid 500); 17 May 2014 12:45:01 -0000 Delivered-To: apmail-tapestry-dev-archive@tapestry.apache.org Received: (qmail 55061 invoked by uid 500); 17 May 2014 12:20:01 -0000 Mailing-List: contact commits-help@tapestry.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@tapestry.apache.org Delivered-To: mailing list commits@tapestry.apache.org Received: (qmail 49926 invoked by uid 99); 17 May 2014 12:14:36 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 17 May 2014 12:14:36 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 1757B92E089; Sat, 17 May 2014 12:14:36 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jkemnade@apache.org To: commits@tapestry.apache.org Date: Sat, 17 May 2014 12:14:36 -0000 Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: [1/2] git commit: TAP5-2266: extend support for HTML content inside parameter JavaDoc Repository: tapestry-5 Updated Branches: refs/heads/master c44abfd6a -> fd3c9e6b7 TAP5-2266: extend support for HTML content inside parameter JavaDoc Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/bb309644 Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/bb309644 Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/bb309644 Branch: refs/heads/master Commit: bb3096442155f68a69cee0b709717c09287bdea9 Parents: c44abfd Author: Jochen Kemnade Authored: Sat May 17 13:57:30 2014 +0200 Committer: Jochen Kemnade Committed: Sat May 17 14:05:30 2014 +0200 ---------------------------------------------------------------------- .../tapestry5/corelib/components/DevTool.java | 2 +- tapestry-javadoc/build.gradle | 3 + .../tapestry5/javadoc/ParameterDescription.java | 55 ++++++-- .../javadoc/ParameterDescriptionSpec.groovy | 125 +++++++++++++++++++ 4 files changed, 172 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/bb309644/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java ---------------------------------------------------------------------- diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java index 320e1cd..0954e66 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/DevTool.java @@ -58,7 +58,7 @@ public class DevTool /** * If true, then the DevTool modifies its markup so as to work within a Bootstrap 3 NavBar. This renders - * the component as an {@code
  • } (instead of a {@code
    }), and removes the "btn" CSS classes. + * the component as a {@code
  • } (instead of a {@code
    }), and removes the "btn" CSS classes. */ @Parameter private boolean navbar; http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/bb309644/tapestry-javadoc/build.gradle ---------------------------------------------------------------------- diff --git a/tapestry-javadoc/build.gradle b/tapestry-javadoc/build.gradle index 032114a..efc6f9d 100644 --- a/tapestry-javadoc/build.gradle +++ b/tapestry-javadoc/build.gradle @@ -4,6 +4,9 @@ dependencies { compile project(':tapestry-core') compile "commons-lang:commons-lang:2.6" compile files(getTools()) + + testCompile "org.spockframework:spock-core:${versions.spock}" + } /** Returns the tools.jar/classes.jar of the Java runtime. */ http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/bb309644/tapestry-javadoc/src/main/java/org/apache/tapestry5/javadoc/ParameterDescription.java ---------------------------------------------------------------------- diff --git a/tapestry-javadoc/src/main/java/org/apache/tapestry5/javadoc/ParameterDescription.java b/tapestry-javadoc/src/main/java/org/apache/tapestry5/javadoc/ParameterDescription.java index a60f8a7..ff8bc11 100644 --- a/tapestry-javadoc/src/main/java/org/apache/tapestry5/javadoc/ParameterDescription.java +++ b/tapestry-javadoc/src/main/java/org/apache/tapestry5/javadoc/ParameterDescription.java @@ -14,14 +14,18 @@ package org.apache.tapestry5.javadoc; -import com.sun.javadoc.FieldDoc; -import com.sun.javadoc.SeeTag; -import com.sun.javadoc.Tag; - import java.io.IOException; +import java.util.Locale; +import java.util.Set; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.StringEscapeUtils; +import org.apache.tapestry5.ioc.internal.util.CollectionFactory; + +import com.sun.javadoc.FieldDoc; +import com.sun.javadoc.SeeTag; +import com.sun.javadoc.Tag; public class ParameterDescription { @@ -45,10 +49,12 @@ public class ParameterDescription public final boolean deprecated; - private static final Pattern STRIPPER = Pattern.compile("(<.*?>|&.*?;)", Pattern.DOTALL); + private static final Pattern SPECIAL_CONTENT = Pattern.compile("(?:)|(?:&\\p{Alpha}+;)"); + private static final Set PASS_THROUGH_TAGS = CollectionFactory.newSet("b", "em", "i", "code", "strong"); - public ParameterDescription(FieldDoc fieldDoc, String name, String type, String defaultValue, String defaultPrefix, - boolean required, boolean allowNull, boolean cache, String since, boolean deprecated) + + public ParameterDescription(final FieldDoc fieldDoc, final String name, final String type, final String defaultValue, final String defaultPrefix, + final boolean required, final boolean allowNull, final boolean cache, final String since, final boolean deprecated) { this.field = fieldDoc; this.name = name; @@ -76,7 +82,7 @@ public class ParameterDescription { if (tag.name().equals("Text")) { - builder.append(tag.text()); + appendContentSafe(builder, tag.text()); continue; } @@ -87,17 +93,17 @@ public class ParameterDescription String label = seeTag.label(); if (label != null && !label.equals("")) { - builder.append(label); + builder.append(StringEscapeUtils.escapeHtml(label)); continue; } if (seeTag.referencedClassName() != null) - builder.append(seeTag.referencedClassName()); + builder.append(StringEscapeUtils.escapeHtml(seeTag.referencedClassName())); if (seeTag.referencedMemberName() != null) { builder.append("#"); - builder.append(seeTag.referencedMemberName()); + builder.append(StringEscapeUtils.escapeHtml(seeTag.referencedMemberName())); } } else if (tag.name().equals("@code")) @@ -114,6 +120,31 @@ public class ParameterDescription // Remove any simple open or close tags found in the text, as well as any XML entities. - return STRIPPER.matcher(text).replaceAll("").trim(); + return text.trim(); + } + + private static void appendContentSafe(final StringBuilder sb, final String string){ + Matcher m = SPECIAL_CONTENT.matcher(string); + int index = 0; + while (index < string.length()){ + boolean match = m.find(index); + if (match){ + if (index != m.start()){ + sb.append(StringEscapeUtils.escapeHtml(string.substring(index, m.start()))); + } + String tagName = m.group(1); + if (tagName!= null){ + if(PASS_THROUGH_TAGS.contains(tagName.toLowerCase(Locale.US))){ + sb.append(m.group()); + } + }else{ + sb.append(m.group()); + } + index = m.end(); + }else{ + sb.append(StringEscapeUtils.escapeHtml(string.substring(index))); + break; + } + } } } http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/bb309644/tapestry-javadoc/src/test/groovy/org/apache/tapestry5/javadoc/ParameterDescriptionSpec.groovy ---------------------------------------------------------------------- diff --git a/tapestry-javadoc/src/test/groovy/org/apache/tapestry5/javadoc/ParameterDescriptionSpec.groovy b/tapestry-javadoc/src/test/groovy/org/apache/tapestry5/javadoc/ParameterDescriptionSpec.groovy new file mode 100644 index 0000000..008d1b1 --- /dev/null +++ b/tapestry-javadoc/src/test/groovy/org/apache/tapestry5/javadoc/ParameterDescriptionSpec.groovy @@ -0,0 +1,125 @@ +package org.apache.tapestry5.javadoc + +import spock.lang.Specification + +import com.sun.javadoc.FieldDoc +import com.sun.tools.doclets.internal.toolkit.util.TextTag +import com.sun.tools.javadoc.TagImpl + + +class ParameterDescriptionSpec extends Specification { + + def "Parameter description without embedded tags is passed through"(){ + setup: + FieldDoc fieldDoc = Mock() + def inlineTags = [ + new TextTag(null, "Plain text") + ] + ParameterDescription parameterDescription = new ParameterDescription(fieldDoc, "parameter", "String", "value", "literal", false, true, false, "1", false) + when: + def extracted = parameterDescription.extractDescription() + then: + 1 * fieldDoc.inlineTags() >> inlineTags + extracted == "Plain text" + } + + def "Embedded code tags are turned into HTML elements"(){ + setup: + FieldDoc fieldDoc = Mock() + def inlineTags = [ + new TagImpl(null, "@code", "blah") + ] + ParameterDescription parameterDescription = new ParameterDescription(fieldDoc, "parameter", "String", "value", "literal", false, true, false, "1", false) + when: + def extracted = parameterDescription.extractDescription() + then: + 1 * fieldDoc.inlineTags() >> inlineTags + extracted == "blah" + } + + // TAP5-2266 + def "HTML in embedded code tags is escaped"(){ + setup: + FieldDoc fieldDoc = Mock() + def inlineTags = [ + new TextTag(null, "This renders the component as a "), + new TagImpl(null, "@code", "
  • "), + new TextTag(null, " (instead of a "), + new TagImpl(null, "@code", "
    "), + new TextTag(null, ")") + ] + ParameterDescription parameterDescription = new ParameterDescription(fieldDoc, "parameter", "String", "value", "literal", false, true, false, "1", false) + when: + def extracted = parameterDescription.extractDescription() + then: + 1 * fieldDoc.inlineTags() >> inlineTags + extracted == "This renders the component as a <li> (instead of a <div>)" + } + + + def "Characters with special meaning are escaped"(){ + setup: + FieldDoc fieldDoc = Mock() + def inlineTags = [ + new TextTag(null, "Javadoc with < character") + ] + ParameterDescription parameterDescription = new ParameterDescription(fieldDoc, "parameter", "String", "value", "literal", false, true, false, "1", false) + when: + def extracted = parameterDescription.extractDescription() + then: + 1 * fieldDoc.inlineTags() >> inlineTags + extracted == "Javadoc with < character" + } + + def "Entities in Javadoc are left alone"(){ + setup: + FieldDoc fieldDoc = Mock() + def inlineTags = [ + new TextTag(null, "Text & entity") + ] + ParameterDescription parameterDescription = new ParameterDescription(fieldDoc, "parameter", "String", "value", "literal", false, true, false, "1", false) + when: + def extracted = parameterDescription.extractDescription() + then: + 1 * fieldDoc.inlineTags() >> inlineTags + extracted == "Text & entity" + } + + def "Un-safe tags in Javadoc are removed"(){ + setup: + FieldDoc fieldDoc = Mock() + def inlineTags = [ + new TextTag(null, "We don't
    want new lines or table stuff") + ] + ParameterDescription parameterDescription = new ParameterDescription(fieldDoc, "parameter", "String", "value", "literal", false, true, false, "1", false) + when: + def extracted = parameterDescription.extractDescription() + then: + 1 * fieldDoc.inlineTags() >> inlineTags + extracted == "We don't want new lines or table stuff" + } + + def "#src in Javadoc becomes #target in HTML"(){ + setup: + FieldDoc fieldDoc = Mock() + def inlineTags = [ + new TextTag(null, src) + ] + ParameterDescription parameterDescription = new ParameterDescription(fieldDoc, "parameter", "String", "value", "literal", false, true, false, "1", false) + when: + def extracted = parameterDescription.extractDescription() + then: + 1 * fieldDoc.inlineTags() >> inlineTags + extracted == target + where: + src | target + "&" | "&" + "&" | "&" + "<" | "<" + "" | "" + + } + + + +}