diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/CommentNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/CommentNode.java new file mode 100644 index 0000000000..7d700481ed --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/CommentNode.java @@ -0,0 +1,17 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.rule.xpath; + +import net.sourceforge.pmd.lang.ast.Node; + +public interface CommentNode extends Node { + + String getData(); + + @Override + default String getXPathNodeName() { + return "#comment"; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/TextNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/TextNode.java new file mode 100644 index 0000000000..00d2b5e6c1 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/TextNode.java @@ -0,0 +1,16 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.rule.xpath; + +import net.sourceforge.pmd.lang.ast.Node; + +public interface TextNode extends Node { + String getText(); + + @Override + default String getXPathNodeName() { + return "#text"; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java index c97f6c754d..230f96e32e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/AstElementNode.java @@ -11,6 +11,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.Predicate; +import java.util.stream.Collectors; import org.apache.commons.lang3.mutable.MutableInt; import org.checkerframework.checker.nullness.qual.Nullable; @@ -18,6 +19,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.rule.xpath.Attribute; +import net.sourceforge.pmd.lang.rule.xpath.CommentNode; +import net.sourceforge.pmd.lang.rule.xpath.TextNode; import net.sourceforge.pmd.util.CollectionUtil; import net.sf.saxon.Configuration; @@ -67,13 +70,10 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin } private static int determineType(Node node) { - // As of PMD 6.48.0, only the experimental HTML module uses this naming - // convention to identify non-element nodes. - // TODO PMD 7: maybe generalize this to other languages - String name = node.getXPathNodeName(); - if ("#text".equals(name)) { + // As of PMD 7, only the HTML module uses these interfaces + if (node instanceof TextNode) { return Type.TEXT; - } else if ("#comment".equals(name)) { + } else if (node instanceof CommentNode) { return Type.COMMENT; } return Type.ELEMENT; @@ -209,8 +209,12 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin @Override public CharSequence getStringValueCS() { - if (getNodeKind() == Type.TEXT || getNodeKind() == Type.COMMENT) { - return getUnderlyingNode().getImage(); + Node node = getUnderlyingNode(); + if (node instanceof TextNode) { + return ((TextNode) node).getText(); + } + if (node instanceof CommentNode) { + return ((CommentNode) node).getData(); } // https://www.w3.org/TR/xpath-datamodel-31/#ElementNode @@ -220,9 +224,11 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin // descendants, the zero-length string. // Since we represent all our Nodes as elements, there are no - // text nodes - // TODO: for some languages like html we have text nodes - return ""; + // text nodes usually, except for HTML module - there we have + // potentially text nodes + return node.descendants(TextNode.class).toStream() + .map(TextNode::getText) + .collect(Collectors.joining("")); } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index b0c76988ae..88661dceb7 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -98,7 +98,14 @@ public class DummyLanguageModule extends SimpleLanguageModuleBase implements Cpd for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c == '(') { - DummyNode node = new DummyNode(); + DummyNode node; + if (text.startsWith("#text", i + 1)) { + node = new DummyNode.DummyTextNode(); + } else if (text.startsWith("#comment", i + 1)) { + node = new DummyNode.DummyCommentNode(); + } else { + node = new DummyNode(); + } node.setParent(top); top.addChild(node, top.getNumChildren()); // setup coordinates, temporary (will be completed when node closes) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java index fd2bc56496..a67ac6f386 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/DummyNode.java @@ -20,6 +20,8 @@ import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.lang.rule.xpath.Attribute; +import net.sourceforge.pmd.lang.rule.xpath.CommentNode; +import net.sourceforge.pmd.lang.rule.xpath.TextNode; public class DummyNode extends AbstractNode { @@ -171,4 +173,28 @@ public class DummyNode extends AbstractNode { super("dummyNodeB"); } } + + public static class DummyTextNode extends DummyNode implements TextNode { + @Override + public String getText() { + return getImage(); + } + + @Override + public String getXPathNodeName() { + return TextNode.super.getXPathNodeName(); + } + } + + public static class DummyCommentNode extends DummyNode implements CommentNode { + @Override + public String getData() { + return getImage(); + } + + @Override + public String getXPathNodeName() { + return CommentNode.super.getXPathNodeName(); + } + } } diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlComment.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlComment.java index ae78e81606..5647b5ec5f 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlComment.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlComment.java @@ -7,12 +7,15 @@ package net.sourceforge.pmd.lang.html.ast; import org.jsoup.nodes.Comment; -public final class ASTHtmlComment extends AbstractHtmlNode { +import net.sourceforge.pmd.lang.rule.xpath.CommentNode; + +public final class ASTHtmlComment extends AbstractHtmlNode implements CommentNode { ASTHtmlComment(Comment node) { super(node); } + @Override public String getData() { return node.getData(); } @@ -21,4 +24,9 @@ public final class ASTHtmlComment extends AbstractHtmlNode { protected R acceptHtmlVisitor(HtmlVisitor visitor, P data) { return visitor.visit(this, data); } + + @Override + public String getXPathNodeName() { + return CommentNode.super.getXPathNodeName(); + } } diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlTextNode.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlTextNode.java index 1fb41518d4..a9d2327a97 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlTextNode.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/ASTHtmlTextNode.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.html.ast; import org.jsoup.nodes.TextNode; -public class ASTHtmlTextNode extends AbstractHtmlNode { +public class ASTHtmlTextNode extends AbstractHtmlNode implements net.sourceforge.pmd.lang.rule.xpath.TextNode { ASTHtmlTextNode(TextNode node) { super(node); @@ -18,11 +18,17 @@ public class ASTHtmlTextNode extends AbstractHtmlNode { return visitor.visit(this, data); } - public String getNormalizedText() { + public String getWholeText() { + return node.getWholeText(); + } + + @Override + public String getText() { return node.text(); } - public String getText() { - return node.getWholeText(); + @Override + public String getXPathNodeName() { + return net.sourceforge.pmd.lang.rule.xpath.TextNode.super.getXPathNodeName(); } } diff --git a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/AbstractHtmlNode.java b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/AbstractHtmlNode.java index bd45efd915..70f7c0396b 100644 --- a/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/AbstractHtmlNode.java +++ b/pmd-html/src/main/java/net/sourceforge/pmd/lang/html/ast/AbstractHtmlNode.java @@ -27,6 +27,7 @@ abstract class AbstractHtmlNode extends AbstractNode