Skip to content

Commit

Permalink
[html] Add visitor, add tests for xpath and java rules
Browse files Browse the repository at this point in the history
  • Loading branch information
adangel committed Apr 22, 2022
1 parent 62bedd3 commit 004a59a
Show file tree
Hide file tree
Showing 14 changed files with 350 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import net.sourceforge.pmd.lang.ast.AbstractNode;

class AbstractHtmlNode<T extends Node> extends AbstractNode implements HtmlNode {
abstract class AbstractHtmlNode<T extends Node> extends AbstractNode implements HtmlNode {

protected final T node;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ public final class HtmlCDataNode extends AbstractHtmlNode<CDataNode> {
public String getText() {
return node.text();
}

@Override
public Object acceptVisitor(HtmlVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ public final class HtmlComment extends AbstractHtmlNode<Comment> {
public String getData() {
return node.getData();
}

@Override
public Object acceptVisitor(HtmlVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ public class HtmlDocument extends HtmlElement implements RootNode {
HtmlDocument(Document document) {
super(document);
}

@Override
public Object acceptVisitor(HtmlVisitor visitor, Object data) {
return visitor.visit(this, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public final class HtmlDocumentType extends AbstractHtmlNode<DocumentType> {
super(node);
}

@Override
public Object acceptVisitor(HtmlVisitor visitor, Object data) {
return visitor.visit(this, data);
}

public String getName() {
return node.name();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ public class HtmlElement extends AbstractHtmlNode<Element> {
}
}

@Override
public Object acceptVisitor(HtmlVisitor visitor, Object data) {
return visitor.visit(this, data);
}

public List<Attribute> getAttributes() {
return attributes;
}

@Override
public Iterator<Attribute> getXPathAttributesIterator() {
Iterator<Attribute> defaultAttributes = super.getXPathAttributesIterator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ public interface HtmlNode extends Node {
@Override
Iterable<? extends HtmlNode> children();

Object acceptVisitor(HtmlVisitor visitor, Object data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public class HtmlTextNode extends AbstractHtmlNode<TextNode> {
super(node);
}

@Override
public Object acceptVisitor(HtmlVisitor visitor, Object data) {
return visitor.visit(this, data);
}

public String getNormalizedText() {
return node.text();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.html.ast;

public interface HtmlVisitor {

Object visit(HtmlNode node, Object data);

Object visit(HtmlCDataNode node, Object data);

Object visit(HtmlComment node, Object data);

Object visit(HtmlDocument node, Object data);

Object visit(HtmlDocumentType node, Object data);

Object visit(HtmlElement node, Object data);

Object visit(HtmlTextNode node, Object data);

Object visit(HtmlXmlDeclaration node, Object data);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.html.ast;

public class HtmlVisitorAdapter implements HtmlVisitor {

@Override
public Object visit(HtmlNode node, Object data) {
for (HtmlNode child : node.children()) {
child.acceptVisitor(this, data);
}
return null;
}

@Override
public Object visit(HtmlCDataNode node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlComment node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlDocument node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlDocumentType node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlElement node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlTextNode node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlXmlDeclaration node, Object data) {
return visit((HtmlNode) node, data);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public final class HtmlXmlDeclaration extends AbstractHtmlNode<XmlDeclaration> {
super(node);
}

@Override
public Object acceptVisitor(HtmlVisitor visitor, Object data) {
return visitor.visit(this, data);
}

public String getName() {
return node.name();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.html.rule;

import java.util.List;

import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.html.HtmlLanguageModule;
import net.sourceforge.pmd.lang.html.ast.HtmlCDataNode;
import net.sourceforge.pmd.lang.html.ast.HtmlComment;
import net.sourceforge.pmd.lang.html.ast.HtmlDocument;
import net.sourceforge.pmd.lang.html.ast.HtmlDocumentType;
import net.sourceforge.pmd.lang.html.ast.HtmlElement;
import net.sourceforge.pmd.lang.html.ast.HtmlNode;
import net.sourceforge.pmd.lang.html.ast.HtmlTextNode;
import net.sourceforge.pmd.lang.html.ast.HtmlVisitor;
import net.sourceforge.pmd.lang.html.ast.HtmlXmlDeclaration;
import net.sourceforge.pmd.lang.rule.AbstractRule;

public abstract class AbstractHtmlRule extends AbstractRule implements HtmlVisitor {

public AbstractHtmlRule() {
super.setLanguage(LanguageRegistry.getLanguage(HtmlLanguageModule.NAME));
}

@Override
public void apply(List<? extends Node> nodes, RuleContext ctx) {
for (Node node : nodes) {
if (node instanceof HtmlNode) {
((HtmlNode) node).acceptVisitor(this, ctx);
}
}
}

//
// The following APIs are identical to those in HtmlVisitorAdapter.
// Due to Java single inheritance, it is preferred to extend from the more
// complex Rule base class instead of from relatively simple Visitor.
//
// CPD-OFF

@Override
public Object visit(HtmlNode node, Object data) {
for (HtmlNode child : node.children()) {
child.acceptVisitor(this, data);
}
return null;
}

@Override
public Object visit(HtmlCDataNode node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlComment node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlDocument node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlDocumentType node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlElement node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlTextNode node, Object data) {
return visit((HtmlNode) node, data);
}

@Override
public Object visit(HtmlXmlDeclaration node, Object data) {
return visit((HtmlNode) node, data);
}

// CPD-ON
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/

package net.sourceforge.pmd.lang.html;

import java.io.StringReader;
import java.util.Arrays;

import org.junit.Assert;
import org.junit.Test;

import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
import net.sourceforge.pmd.lang.html.ast.HtmlElement;
import net.sourceforge.pmd.lang.html.rule.AbstractHtmlRule;

public class HtmlJavaRuleTest {
// from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter
private static final String LIGHTNING_WEB_COMPONENT = "<!-- helloExpressions.html -->\n"
+ "<template>\n"
+ " <p>Hello, { greeting}!</p>\n"
+ " <lightning-input label=\"Name\" value={ greeting} onchange=\"{ handleChange }\"></lightning-input>\n"
+ " <div class=\"slds-m-around_medium\">\n"
+ " <lightning-input name='firstName' label=\"First Name\" onchange={ handleChange }></lightning-input>\n"
+ " <lightning-input name='lastName' label=\"Last Name\" onchange={handleChange}></lightning-input>\n"
+ " <p class=\"slds-m-top_medium\">Uppercased Full Name: {uppercasedFullName}</p>\n"
+ " </div>\n"
+ "</template>";

@Test
public void findAllAttributesWithInvalidExpression() {
// "Don’t add spaces around the property, for example, { data } is not valid HTML."
Rule rule = new AbstractHtmlRule() {
@Override
public String getMessage() {
return "Invalid expression";
}

@Override
public Object visit(HtmlElement node, Object data) {
for (Attribute attribute : node.getAttributes()) {
if ("{".equals(attribute.getValue())) {
RuleContext ctx = (RuleContext) data;
ctx.addViolation(node);
}
}
return super.visit(node, data);
}
};
Report report = runRule(LIGHTNING_WEB_COMPONENT, rule);
Assert.assertEquals(2, report.getViolations().size());
Assert.assertEquals(4, report.getViolations().get(0).getBeginLine());
Assert.assertEquals(6, report.getViolations().get(1).getBeginLine());
}

private Report runRule(String html, Rule rule) {
LanguageVersion htmlLanguage = LanguageRegistry.findLanguageByTerseName(HtmlLanguageModule.TERSE_NAME).getDefaultVersion();
Parser parser = htmlLanguage.getLanguageVersionHandler().getParser(htmlLanguage.getLanguageVersionHandler().getDefaultParserOptions());

Node node = parser.parse("n/a", new StringReader(html));
RuleContext context = new RuleContext();
context.setLanguageVersion(htmlLanguage);
context.setCurrentRule(rule);
rule.apply(Arrays.asList(node), context);
return context.getReport();
}
}
Loading

0 comments on commit 004a59a

Please sign in to comment.