Merge pull request #4804 from adangel:issue-4309-xpath-cleanups

[core] XPath cleanups #4804
This commit is contained in:
Andreas Dangel
2024-02-09 16:21:24 +01:00
87 changed files with 918 additions and 655 deletions

View File

@ -165,7 +165,6 @@ with a backslash when needed.
```xml
<rule name="MyXpathRule" ...>
<properties>
<property name="version" value="2.0" />
<property name="intProp" type="List[Integer]" value="1,2,5" description="An IntegerMultiProperty." />
<property name="reportedIdentifiers" type="List[String]" value="foo,bar"
description="A StringMultiProperty." />

View File

@ -104,7 +104,7 @@ instead of mentioning the `ref` attribute, it mentions the `class` attribute,
with the implementation class of your rule.
* **For Java rules:** this is the concrete class extending AbstractRule (transitively)
* **For XPath rules:** this is `net.sourceforge.pmd.lang.rule.XPathRule`.
* **For XPath rules:** this is `net.sourceforge.pmd.lang.rule.xpath.XPathRule`.
* **For XPath rules analyzing XML-based languages:** this is `net.sourceforge.pmd.lang.xml.rule.DomXPathRule`.
See [XPath rules in XML](pmd_languages_xml.html#xpath-rules-in-xml) for more info.
@ -128,7 +128,7 @@ Example for XPath rule:
<rule name="MyXPathRule"
language="java"
message="Violation!"
class="net.sourceforge.pmd.lang.rule.XPathRule">
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
<description>
Description
</description>

View File

@ -27,7 +27,8 @@ with opt-in support for XPath 2.0.
See [the Saxonica documentation](https://www.saxonica.com/html/documentation/expressions/xpath31new.html)
for an introduction to new features in XPath 3.1.
The property `version` of {% jdoc core::lang.rule.XPathRule %} is deprecated and will be removed.
The property `version` of {% jdoc core::lang.rule.XPathRule %} is deprecated and
has been removed with PMD 7.
## DOM representation of ASTs

View File

@ -127,7 +127,7 @@ copy-paste into your ruleset XML. The resulting element looks like so:
<rule name="DontCallBossShort"
language="java"
message="Boss wants to talk to you."
class="net.sourceforge.pmd.lang.rule.XPathRule">
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
<description>
TODO
</description>

View File

@ -109,9 +109,11 @@ XPath 2.0 is available in PMD 6 already and can be used right away. PMD 7 will u
won't support XPath 1.0 anymore. The difference between XPath 2.0 and XPath 3.1 is not big, so your XPath 2.0
can be expected to work in PMD 7 without any further changes. So the migration path is to simply migrate to XPath 2.0.
After you have migrated your XPath rules to XPath 2.0, remove the "version" property, since that will be removed
with PMD 7. PMD 7 by default uses XPath 3.1.
See below [XPath](#xpath-migrating-from-10-to-20) for details.
After you have migrated your XPath rules to XPath 2.0, remove the "version" property, since that has been removed
with PMD 7. PMD 7 by default uses XPath 3.1. See below [XPath](#xpath-migrating-from-10-to-20) for details.
Then change the `class` attribute of your rule to `net.sourceforge.pmd.lang.rule.xpath.XPathRule` - because the
class {%jdoc core::lang.rule.xpath.XPathRule %} has been moved into subpackage {% jdoc_package core::lang.rule.xpath %}.
There are some general changes for AST nodes regarding the `@Image` attribute.
See below [General AST Changes to avoid @Image](#general-ast-changes-to-avoid-image).

View File

@ -175,6 +175,7 @@ The rules have been moved into categories with PMD 6.
* [#3903](https://github.com/pmd/pmd/issues/3903): \[core] Consolidate `n.s.pmd.reporting` package
* [#3917](https://github.com/pmd/pmd/issues/3917): \[core] Consolidate `n.s.pmd.lang.rule` package
* [#4065](https://github.com/pmd/pmd/issues/4065): \[core] Rename TokenMgrError to LexException, Tokenizer to CpdLexer
* [#4309](https://github.com/pmd/pmd/issues/4309): \[core] Cleanups in XPath area
* [#4312](https://github.com/pmd/pmd/issues/4312): \[core] Remove unnecessary property `color` and system property `pmd.color` in `TextColorRenderer`
* [#4313](https://github.com/pmd/pmd/issues/4313): \[core] Remove support for &lt;lang&gt;-&lt;ruleset&gt; hyphen notation for ruleset references
* [#4314](https://github.com/pmd/pmd/issues/4314): \[core] Remove ruleset compatibility filter (RuleSetFactoryCompatibility) and CLI option `--no-ruleset-compatibility`
@ -235,6 +236,11 @@ The rules have been moved into categories with PMD 6.
See [General AST Changes to avoid @Image]({{ baseurl }}pmd_userdocs_migrating_to_pmd7.html#general-ast-changes-to-avoid-image)
in the migration guide for details.
**XPath Rules**
* The property `version` was already deprecated and has finally been removed. Please don't define the version
property anymore in your custom XPath rules. By default, the latest XPath version will be used, which
is XPath 3.1.
**Moved classes/consolidated packages**
* pmd-core
@ -252,6 +258,7 @@ in the migration guide for details.
* {%jdoc core::reporting.RuleViolation %}
* {%jdoc core::reporting.ViolationSuppressor %}
* {%jdoc core::reporting.ParametricRuleViolation %} (moved from `net.sourcceforge.pmd.lang.rule`)
* {%jdoc core::lang.rule.xpath.XPathRule %} has been moved into subpackage {% jdoc_package core::lang.rule.xpath %}.
**Internalized classes**
@ -746,6 +753,7 @@ See also [Detailed Release Notes for PMD 7]({{ baseurl }}pmd_release_notes_pmd7.
* [#4204](https://github.com/pmd/pmd/issues/4204): \[core] Provide a CpdAnalysis class as a programmatic entry point into CPD
* [#4301](https://github.com/pmd/pmd/issues/4301): \[core] Remove deprecated property concrete classes
* [#4302](https://github.com/pmd/pmd/issues/4302): \[core] Migrate Property Framework API to Java 8
* [#4309](https://github.com/pmd/pmd/issues/4309): \[core] Cleanups in XPath area
* [#4312](https://github.com/pmd/pmd/issues/4312): \[core] Remove unnecessary property `color` and system property `pmd.color` in `TextColorRenderer`
* [#4313](https://github.com/pmd/pmd/issues/4313): \[core] Remove support for &lt;lang&gt;-&lt;ruleset&gt; hyphen notation for ruleset references
* [#4314](https://github.com/pmd/pmd/issues/4314): \[core] Remove ruleset compatibility filter (RuleSetFactoryCompatibility) and CLI option `--no-ruleset-compatibility`

View File

@ -106,7 +106,7 @@ private class TestRunAs {
since="6.13.0"
language="apex"
message="Apex test methods should have @isTest annotation."
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#apexunittestmethodshouldhaveistestannotation">
<description>
Apex test methods should have `@isTest` annotation instead of the `testMethod` keyword,
@ -116,7 +116,6 @@ annotation for test classes and methods.
</description>
<priority>3</priority>
<properties>
<property name="version" value="2.0"/>
<property name="xpath">
<value>
<![CDATA[
@ -231,7 +230,7 @@ trigger Accounts on Account (before insert, before update, before delete, after
since="6.18.0"
language="apex"
message="Calls to System.debug should specify a logging level."
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#debugsshoulduselogginglevel">
<description>
The first parameter of System.debug, when using the signature with two parameters, is a LoggingLevel enum.

View File

@ -37,7 +37,7 @@ public class fooClass { } // This will be reported unless you change the regex
language="apex"
since="5.6.0"
message="Avoid using 'if...else' statements without curly braces"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#ifelsestmtsmustusebraces">
<description>
Avoid using if..else statements without using surrounding braces. If the code formatting
@ -74,7 +74,7 @@ else
language="apex"
since="5.6.0"
message="Avoid using if statements without curly braces"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#ifstmtsmustusebraces">
<description>
Avoid using if statements without using braces to surround the code block. If the code
@ -156,7 +156,7 @@ public class Foo {
language="apex"
since="5.6.0"
message="Avoid using 'for' statements without curly braces"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#forloopsmustusebraces">
<description>
Avoid using 'for' statements without using surrounding braces. If the code formatting or
@ -270,7 +270,7 @@ public class Foo {
language="apex"
since="6.7.0"
message="Use one statement for each line, it enhances code readability."
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#onedeclarationperline">
<description>
Apex allows the use of several variables declaration of the same type on one line. However, it
@ -338,7 +338,7 @@ public class Foo {
language="apex"
since="5.6.0"
message="Avoid using 'while' statements without curly braces"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#whileloopsmustusebraces">
<description>
Avoid using 'while' statements without using braces to surround the code block. If the code

View File

@ -54,7 +54,7 @@ public class Foo {
language="apex"
since="6.0.0"
message="Avoid directly accessing Trigger.old and Trigger.new"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#avoiddirectaccesstriggermap">
<description>
Avoid directly accessing Trigger.old and Trigger.new as it can lead to a bug. Triggers should be bulkified and iterate through the map to handle the actions for each item separately.
@ -138,7 +138,7 @@ public without sharing class Foo {
language="apex"
since="6.0.0"
message="Avoid empty catch blocks"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptycatchblock">
<description>
Empty Catch Block finds instances where an exception is caught, but nothing is done.
@ -177,7 +177,7 @@ public void doSomething() {
language="apex"
since="6.0.0"
message="Avoid empty 'if' statements"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptyifstmt">
<description>
Empty If Statement finds instances where a condition is checked but nothing is done about it.
@ -210,7 +210,7 @@ public class Foo {
language="apex"
since="6.0.0"
message="Avoid empty block statements."
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptystatementblock">
<description>
Empty block statements serve no purpose and should be removed.
@ -247,7 +247,7 @@ public class Foo {
language="apex"
since="6.0.0"
message="Avoid empty try or finally blocks"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptytryorfinallyblock">
<description>
Avoid empty try or finally blocks - what's the point?
@ -291,7 +291,7 @@ public class Foo {
language="apex"
since="6.0.0"
message="Avoid empty 'while' statements"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptywhilestmt">
<description>
Empty While Statement finds all instances where a while statement does nothing.
@ -445,7 +445,7 @@ public class Foo { // perfect, both methods provided
language="apex"
since="6.22.0"
message="Test methods must be in test classes"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#testmethodsmustbeintestclasses">
<description>
Test methods marked as a testMethod or annotated with @IsTest,

View File

@ -13,7 +13,7 @@ Rules that flag suboptimal code.
language="apex"
since="6.36.0"
message="Avoid debug statements since they impact on performance"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#avoiddebugstatements">
<description>
Debug statements contribute to longer transactions and consume Apex CPU time even when debug logs are not being captured.
@ -24,7 +24,6 @@ For other valid use cases that the statement is in fact valid make use of the `@
</description>
<priority>3</priority>
<properties>
<property name="version" value="2.0"/>
<property name="xpath">
<value>
<![CDATA[
@ -141,7 +140,7 @@ public class Something {
language="apex"
since="6.40.0"
message="DescribeSObjectResult could be being loaded eagerly with all child relationships."
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#eagerlyloadeddescribesobjectresult">
<description>
This rule finds `DescribeSObjectResult`s which could have been loaded eagerly via `SObjectType.getDescribe()`.
@ -172,7 +171,6 @@ Properties:
<priority>3</priority>
<properties>
<property name="noDefault" type="Boolean" value="false" description="Do not allow SObjectDescribeOptions.DEFAULT option to ensure consistent results no matter where getDescribe is called"/>
<property name="version" value="2.0"/>
<property name="xpath">
<value>
<![CDATA[

View File

@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.lang.apex.ast.ApexParserTestBase;
import net.sourceforge.pmd.lang.document.FileId;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.XPathRule;
import net.sourceforge.pmd.reporting.Report;
/**

View File

@ -7,7 +7,7 @@
</description>
<rule name="ReportAllRootNodes" language="dummy" since="1.0" message="Violation from ReportAllRootNodes"
class="net.sourceforge.pmd.lang.rule.XPathRule"
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/rules/test/TestRuleset3.xml#Ruleset3Rule1">
<description>Just for test</description>
<priority>3</priority>

View File

@ -2,7 +2,7 @@
<description/>
<rule name="ExceptionThrowingRule"
language="java"
class="net.sourceforge.pmd.lang.rule.XPathRule">
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
<description>Use this rule to produce a processing error.</description>
<priority>3</priority>
<properties>

View File

@ -0,0 +1,8 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule;
public class XPathRule extends net.sourceforge.pmd.lang.rule.xpath.XPathRule {
}

View File

@ -0,0 +1,172 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
// This class has been taken from 7.0.0-SNAPSHOT
// Changes: not final anymore to allow a subclass in the old package
package net.sourceforge.pmd.lang.rule.xpath;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ContextedRuntimeException;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.Rule;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.LanguageProcessor;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.AbstractRule;
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
import net.sourceforge.pmd.lang.rule.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.lang.rule.xpath.internal.SaxonXPathRuleQuery;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertyFactory;
/**
* Rule that tries to match an XPath expression against a DOM view of an AST.
*/
public /*final*/ class XPathRule extends AbstractRule {
private static final Logger LOG = LoggerFactory.getLogger(XPathRule.class);
/**
* @deprecated Use {@link #XPathRule(XPathVersion, String)}
*/
@Deprecated
public static final PropertyDescriptor<String> XPATH_DESCRIPTOR =
PropertyFactory.stringProperty("xpath")
.desc("XPath expression")
.defaultValue("")
.build();
/**
* This is initialized only once when calling {@link #apply(Node, RuleContext)} or {@link #getTargetSelector()}.
*/
private SaxonXPathRuleQuery xpathRuleQuery;
// this is shared with rules forked by deepCopy, used by the XPathRuleQuery
private DeprecatedAttrLogger attrLogger = DeprecatedAttrLogger.create(this);
/**
* @deprecated This is now only used by the ruleset loader. When
* we have syntactic sugar for XPath rules in the XML, we won't
* need this anymore.
*/
@Deprecated
public XPathRule() {
definePropertyDescriptor(XPATH_DESCRIPTOR);
}
/**
* Make a new XPath rule with the given version + expression
*
* @param version Version of the XPath language
* @param expression XPath expression
*
* @throws NullPointerException If any of the arguments is null
*/
public XPathRule(XPathVersion version, String expression) {
this();
Objects.requireNonNull(version, "XPath version is null");
Objects.requireNonNull(expression, "XPath expression is null");
setProperty(XPathRule.XPATH_DESCRIPTOR, expression);
}
@Override
public Rule deepCopy() {
XPathRule rule = (XPathRule) super.deepCopy();
rule.attrLogger = this.attrLogger;
return rule;
}
/**
* Returns the XPath expression that implements this rule.
*/
public String getXPathExpression() {
return getProperty(XPATH_DESCRIPTOR);
}
@Override
public void apply(Node target, RuleContext ctx) {
SaxonXPathRuleQuery query = getQueryMaybeInitialize();
List<Node> nodesWithViolation;
try {
nodesWithViolation = query.evaluate(target);
} catch (PmdXPathException e) {
throw addExceptionContext(e);
}
for (Node nodeWithViolation : nodesWithViolation) {
addViolation(ctx, nodeWithViolation, nodeWithViolation.getImage());
}
}
private ContextedRuntimeException addExceptionContext(PmdXPathException e) {
return e.addRuleName(getName());
}
@Override
public void initialize(LanguageProcessor languageProcessor) {
String xpath = getXPathExpression();
XPathVersion version = XPathVersion.DEFAULT;
try {
xpathRuleQuery = new SaxonXPathRuleQuery(xpath,
version,
getPropertiesByPropertyDescriptor(),
languageProcessor.services().getXPathHandler(),
attrLogger);
} catch (PmdXPathException e) {
throw addExceptionContext(e);
}
}
private SaxonXPathRuleQuery getQueryMaybeInitialize() throws PmdXPathException {
if (xpathRuleQuery == null) {
throw new IllegalStateException("Not initialized");
}
return xpathRuleQuery;
}
@Override
protected @NonNull RuleTargetSelector buildTargetSelector() {
List<String> visits = getQueryMaybeInitialize().getRuleChainVisits();
logXPathRuleChainUsage(!visits.isEmpty());
return visits.isEmpty() ? RuleTargetSelector.forRootOnly()
: RuleTargetSelector.forXPathNames(visits);
}
private void logXPathRuleChainUsage(boolean usesRuleChain) {
LOG.debug("{} rule chain for XPath rule: {} ({})",
usesRuleChain ? "Using" : "no",
getName(),
getRuleSetName());
}
@Override
public String dysfunctionReason() {
if (StringUtils.isBlank(getXPathExpression())) {
return "Missing XPath expression";
}
return null;
}
}

View File

@ -27,6 +27,7 @@ import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.document.FileId;
import net.sourceforge.pmd.lang.document.TextFile;
import net.sourceforge.pmd.lang.rule.internal.RuleSetReference;
import net.sourceforge.pmd.lang.rule.xpath.XPathRule;
/**
* This class represents a collection of rules along with some optional filter

View File

@ -2,12 +2,9 @@
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule;
package net.sourceforge.pmd.lang.rule.xpath;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
@ -16,11 +13,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
import net.sourceforge.pmd.lang.LanguageProcessor;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
import net.sourceforge.pmd.lang.rule.AbstractRule;
import net.sourceforge.pmd.lang.rule.Rule;
import net.sourceforge.pmd.lang.rule.RuleTargetSelector;
import net.sourceforge.pmd.lang.rule.xpath.internal.DeprecatedAttrLogger;
import net.sourceforge.pmd.lang.rule.xpath.internal.SaxonXPathRuleQuery;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@ -35,8 +32,6 @@ public final class XPathRule extends AbstractRule {
private static final Logger LOG = LoggerFactory.getLogger(XPathRule.class);
// TODO move to XPath subpackage
/**
* @deprecated Use {@link #XPathRule(XPathVersion, String)}
*/
@ -47,17 +42,6 @@ public final class XPathRule extends AbstractRule {
.defaultValue("")
.build();
/**
* @deprecated Use {@link #XPathRule(XPathVersion, String)}
*/
@Deprecated
@DeprecatedUntil700
public static final PropertyDescriptor<XPathVersion> VERSION_DESCRIPTOR =
PropertyFactory.enumProperty("version", getXPathVersions())
.desc("XPath specification version")
.defaultValue(XPathVersion.DEFAULT)
.build();
/**
* This is initialized only once when calling {@link #apply(Node, RuleContext)} or {@link #getTargetSelector()}.
*/
@ -76,7 +60,6 @@ public final class XPathRule extends AbstractRule {
@Deprecated
public XPathRule() {
definePropertyDescriptor(XPATH_DESCRIPTOR);
definePropertyDescriptor(VERSION_DESCRIPTOR);
}
/**
@ -94,7 +77,6 @@ public final class XPathRule extends AbstractRule {
Objects.requireNonNull(expression, "XPath expression is null");
setProperty(XPathRule.XPATH_DESCRIPTOR, expression);
setProperty(XPathRule.VERSION_DESCRIPTOR, XPathVersion.ofId(version.getXmlName()));
}
@ -105,14 +87,6 @@ public final class XPathRule extends AbstractRule {
return rule;
}
/**
* Returns the version for this rule. Returns null if this is not
* set or invalid.
*/
public XPathVersion getVersion() {
return getProperty(VERSION_DESCRIPTOR);
}
/**
* Returns the XPath expression that implements this rule.
*/
@ -144,11 +118,7 @@ public final class XPathRule extends AbstractRule {
@Override
public void initialize(LanguageProcessor languageProcessor) {
String xpath = getXPathExpression();
XPathVersion version = getVersion();
if (version == null) {
throw new IllegalStateException("Invalid XPath version, should have been caught by Rule::dysfunctionReason");
}
XPathVersion version = XPathVersion.DEFAULT;
try {
xpathRuleQuery = new SaxonXPathRuleQuery(xpath,
@ -182,9 +152,8 @@ public final class XPathRule extends AbstractRule {
private void logXPathRuleChainUsage(boolean usesRuleChain) {
LOG.debug("{} rule chain for XPath {} rule: {} ({})",
LOG.debug("{} rule chain for XPath rule: {} ({})",
usesRuleChain ? "Using" : "no",
getProperty(XPathRule.VERSION_DESCRIPTOR),
getName(),
getRuleSetName());
}
@ -192,19 +161,9 @@ public final class XPathRule extends AbstractRule {
@Override
public String dysfunctionReason() {
if (getVersion() == null) {
return "Invalid XPath version '" + getProperty(VERSION_DESCRIPTOR) + "'";
} else if (StringUtils.isBlank(getXPathExpression())) {
if (StringUtils.isBlank(getXPathExpression())) {
return "Missing XPath expression";
}
return null;
}
private static Map<String, XPathVersion> getXPathVersions() {
Map<String, XPathVersion> tmp = new HashMap<>();
for (XPathVersion v : XPathVersion.values()) {
tmp.put(v.getXmlName(), v);
}
return Collections.unmodifiableMap(tmp);
}
}

View File

@ -1,40 +0,0 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule.xpath.impl;
import net.sourceforge.pmd.lang.Language;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.StructuredQName;
/**
* Base impl for an XPath function definition.
* This uses Saxon API.
*
* @since 7.0.0
*/
public abstract class AbstractXPathFunctionDef extends ExtensionFunctionDefinition {
private static final String PMD_URI_PREFIX = "http://pmd.sourceforge.net/";
private final StructuredQName qname;
private AbstractXPathFunctionDef(String localName, String namespacePrefix, String uri) {
this.qname = new StructuredQName(namespacePrefix, uri, localName);
}
protected AbstractXPathFunctionDef(String localName) {
this(localName, "pmd", PMD_URI_PREFIX + "pmd-core");
}
protected AbstractXPathFunctionDef(String localName, Language language) {
this(localName, "pmd-" + language.getId(), PMD_URI_PREFIX + "pmd-" + language.getId());
}
@Override
public final StructuredQName getFunctionQName() {
return qname;
}
}

View File

@ -0,0 +1,126 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule.xpath.impl;
import javax.xml.namespace.QName;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.ast.Node;
/**
* Base impl for an XPath function definition.
*
* @since 7.0.0
*/
public abstract class XPathFunctionDefinition {
private static final String PMD_URI_PREFIX = "http://pmd.sourceforge.net/";
private final QName qname;
private XPathFunctionDefinition(String localName, String namespacePrefix, String uri) {
this.qname = new QName(uri, localName, namespacePrefix);
}
protected XPathFunctionDefinition(String localName) {
this(localName, "pmd", PMD_URI_PREFIX + "pmd-core");
}
protected XPathFunctionDefinition(String localName, Language language) {
this(localName, "pmd-" + language.getId(), PMD_URI_PREFIX + "pmd-" + language.getId());
}
public final QName getQName() {
return qname;
}
/**
* Defines the types of the function arguments. By default, an empty array is returned, indicating
* that the function takes no arguments.
*/
public Type[] getArgumentTypes() {
return new Type[0];
}
/**
* Defines the return type of the function.
*/
public abstract Type getResultType();
/**
* If the function depends on the context item, then
* this method should return {@code true}.
*
* <p>Note: Only if this is true, the contextNode parameter will be present in the
* {@link FunctionCall#call(Node, Object[])} method.
*/
public boolean dependsOnContext() {
return false;
}
/**
* Create a call on this function. This method is called, when a function call
* is found in the XPath expression.
*/
public abstract FunctionCall makeCallExpression();
/**
* Supported types of a custom XPath function. These can be used as {@link #getResultType() result types}
* or {@link #getArgumentTypes() argument types}.
*/
public enum Type {
/** Represents {@link String}. */
SINGLE_STRING,
/** Represents {@link Boolean}. */
SINGLE_BOOLEAN,
/** Represents {@link Integer}. */
SINGLE_INTEGER,
/** Represents any node. Usually used as an argument type. */
SINGLE_ELEMENT,
/** Represents a {@link java.util.List} of {@link String}, potentially empty. */
STRING_SEQUENCE,
/** Represents a {@link java.util.Optional} {@link String}. */
OPTIONAL_STRING,
/** Represents a {@link java.util.Optional} {@link Double}. */
OPTIONAL_DECIMAL,
}
/**
* Provides the actual implementation of a custom XPath function.
*/
public interface FunctionCall {
/**
* This method is called at runtime to evaluate the XPath function expression.
*
* @param contextNode the context node or {@code null}, if this function doesn't depend on the context.
* See {@link XPathFunctionDefinition#dependsOnContext()}.
* @param arguments The arguments converted as the corresponding java types.
* See {@link XPathFunctionDefinition#getArgumentTypes()}.
* @return The result of the function. This should be the corresponding java type of
* {@link XPathFunctionDefinition#getResultType()}.
* @throws XPathFunctionException when any problem during evaluation occurs, like invalid arguments.
*/
Object call(@Nullable Node contextNode, Object[] arguments) throws XPathFunctionException;
/**
* This is called once before the function is evaluated. It can be used to optimize the
* implementation by doing expensive operations only once and cache the result.
* This is useful, if the argument of the function is of type {@link String} and is provided
* as a String literal in the XPath expression.
*
* <p>This is an optional step. The default implementation does nothing.
*
* @param arguments The arguments converted as the corresponding java types.
* See {@link XPathFunctionDefinition#getArgumentTypes()}.
* Note: This array might contain {@code null} elements, if the values are
* not known yet because they are dynamic. Only literal values are available.
* @throws XPathFunctionException when any problem during initialization occurs, like invalid arguments.
*/
default void staticInit(Object[] arguments) throws XPathFunctionException {
// default implementation does nothing
}
}
}

View File

@ -0,0 +1,19 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule.xpath.impl;
/**
* Indicates a problem during the execution of a custom
* XPath function.
*/
public class XPathFunctionException extends Exception {
public XPathFunctionException(String message) {
super(message);
}
public XPathFunctionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -11,8 +11,6 @@ import java.util.Set;
import net.sourceforge.pmd.lang.rule.xpath.internal.DefaultXPathFunctions;
import net.sourceforge.pmd.util.CollectionUtil;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
/**
* Interface for performing Language specific XPath handling, such as
@ -24,7 +22,7 @@ public interface XPathHandler {
* Returns the set of extension functions for this language module.
* These are the additional functions available in XPath queries.
*/
Set<ExtensionFunctionDefinition> getRegisteredExtensionFunctions();
Set<XPathFunctionDefinition> getRegisteredExtensionFunctions();
static XPathHandler noFunctionDefinitions() {
@ -34,8 +32,8 @@ public interface XPathHandler {
/**
* Returns a default XPath handler.
*/
static XPathHandler getHandlerForFunctionDefs(ExtensionFunctionDefinition first, ExtensionFunctionDefinition... defs) {
Set<ExtensionFunctionDefinition> set = new HashSet<>(CollectionUtil.setOf(first, defs));
static XPathHandler getHandlerForFunctionDefs(XPathFunctionDefinition first, XPathFunctionDefinition... defs) {
Set<XPathFunctionDefinition> set = new HashSet<>(CollectionUtil.setOf(first, defs));
set.addAll(DefaultXPathFunctions.getDefaultFunctions());
return () -> Collections.unmodifiableSet(set);

View File

@ -7,23 +7,15 @@ package net.sourceforge.pmd.lang.rule.xpath.internal;
import java.util.function.ToIntFunction;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.pattern.NodeKindTest;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.Int64Value;
import net.sf.saxon.value.SequenceType;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionException;
/**
* A function that returns the current file name.
*
* @author Clément Fournier
*/
public final class CoordinateXPathFunction extends AbstractXPathFunctionDef {
public final class CoordinateXPathFunction extends XPathFunctionDefinition {
public static final CoordinateXPathFunction START_LINE =
new CoordinateXPathFunction("startLine", Node::getBeginLine);
@ -34,9 +26,7 @@ public final class CoordinateXPathFunction extends AbstractXPathFunctionDef {
public static final CoordinateXPathFunction END_COLUMN =
new CoordinateXPathFunction("endColumn", Node::getEndColumn);
private static final SequenceType[] A_SINGLE_ELEMENT = {
NodeKindTest.makeNodeKindTest(Type.ELEMENT).one(),
};
private static final Type[] A_SINGLE_ELEMENT = { Type.SINGLE_ELEMENT };
public static final String PMD_NODE_USER_DATA = "pmd.node";
private final ToIntFunction<Node> getter;
@ -46,32 +36,26 @@ public final class CoordinateXPathFunction extends AbstractXPathFunctionDef {
}
@Override
public SequenceType[] getArgumentTypes() {
public Type[] getArgumentTypes() {
return A_SINGLE_ELEMENT;
}
@Override
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
return SequenceType.SINGLE_INTEGER;
public Type getResultType() {
return Type.SINGLE_INTEGER;
}
@Override
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
@Override
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
Node node = XPathElementToNodeHelper.itemToNode(arguments[0]);
if (node == null) {
throw new XPathException(
"Cannot call function '" + getFunctionQName().getLocalPart()
+ "' on argument " + arguments[0]
);
}
return Int64Value.makeIntegerValue(getter.applyAsInt(node));
public FunctionCall makeCallExpression() {
return (contextNode, arguments) -> {
Node node = XPathElementToNodeHelper.itemToNode(arguments[0]);
if (node == null) {
throw new XPathFunctionException(
"Cannot call function '" + getQName().getLocalPart()
+ "' on argument " + arguments[0]
);
}
return getter.applyAsInt(node);
};
}
}

View File

@ -7,16 +7,15 @@ package net.sourceforge.pmd.lang.rule.xpath.internal;
import java.util.Set;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
import net.sourceforge.pmd.util.CollectionUtil;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
/**
* Default XPath functions provided by pmd-core.
*/
public final class DefaultXPathFunctions {
private static final Set<ExtensionFunctionDefinition> DEFAULTS =
private static final Set<XPathFunctionDefinition> DEFAULTS =
CollectionUtil.immutableSetOf(
FileNameXPathFunction.INSTANCE,
CoordinateXPathFunction.START_LINE,
@ -29,7 +28,7 @@ public final class DefaultXPathFunctions {
// utility class
}
public static Set<ExtensionFunctionDefinition> getDefaultFunctions() {
public static Set<XPathFunctionDefinition> getDefaultFunctions() {
return DEFAULTS;
}
}

View File

@ -11,8 +11,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.lang.rule.Rule;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.Attribute;
import net.sourceforge.pmd.lang.rule.xpath.XPathRule;
/**
* Records usages of deprecated attributes in XPath rules. This needs

View File

@ -6,23 +6,16 @@ package net.sourceforge.pmd.lang.rule.xpath.internal;
import java.util.Objects;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.RootNode;
import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionException;
/**
* A function that returns the current file name.
*
* @author Clément Fournier
*/
public final class FileNameXPathFunction extends AbstractXPathFunctionDef {
public final class FileNameXPathFunction extends XPathFunctionDefinition {
public static final FileNameXPathFunction INSTANCE = new FileNameXPathFunction();
@ -31,36 +24,31 @@ public final class FileNameXPathFunction extends AbstractXPathFunctionDef {
}
@Override
public SequenceType[] getArgumentTypes() {
return new SequenceType[0];
public Type getResultType() {
return Type.SINGLE_STRING;
}
@Override
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
return SequenceType.STRING_SEQUENCE;
public boolean dependsOnContext() {
return true;
}
@Override
public ExtensionFunctionCall makeCallExpression() {
return new ExtensionFunctionCall() {
@Override
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
Node node = XPathElementToNodeHelper.itemToNode(context.getContextItem());
if (node == null) {
throw new XPathException(
"Cannot call function '" + getFunctionQName().getLocalPart()
+ "' with context item " + context.getContextItem()
);
}
RootNode root = node.getRoot();
Objects.requireNonNull(root, "No root node in tree?");
String fileName = root.getTextDocument().getFileId().getFileName();
Objects.requireNonNull(fileName, "File name was not set");
return new StringValue(fileName);
public FunctionCall makeCallExpression() {
return (node, arguments) -> {
if (node == null) {
throw new XPathFunctionException(
"Cannot call function '" + getQName().getLocalPart()
+ "' without context item"
);
}
RootNode root = node.getRoot();
Objects.requireNonNull(root, "No root node in tree?");
String fileName = root.getTextDocument().getFileId().getFileName();
Objects.requireNonNull(fileName, "File name was not set");
return fileName;
};
}
}

View File

@ -0,0 +1,172 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.rule.xpath.internal;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.xml.namespace.QName;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionException;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.StaticContext;
import net.sf.saxon.expr.StringLiteral;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.EmptyAtomicSequence;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.pattern.NodeKindTest;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.BigDecimalValue;
import net.sf.saxon.value.BooleanValue;
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.Int64Value;
import net.sf.saxon.value.SequenceExtent;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;
/**
* Converts PMD's {@link XPathFunctionDefinition} into Saxon's {@link ExtensionFunctionDefinition}.
*/
public class SaxonExtensionFunctionDefinitionAdapter extends ExtensionFunctionDefinition {
private static final SequenceType SINGLE_ELEMENT_SEQUENCE_TYPE = NodeKindTest.ELEMENT.one();
private final XPathFunctionDefinition definition;
public SaxonExtensionFunctionDefinitionAdapter(XPathFunctionDefinition definition) {
this.definition = definition;
}
private SequenceType convertToSequenceType(XPathFunctionDefinition.Type type) {
switch (type) {
case SINGLE_STRING: return SequenceType.SINGLE_STRING;
case SINGLE_BOOLEAN: return SequenceType.SINGLE_BOOLEAN;
case SINGLE_ELEMENT: return SINGLE_ELEMENT_SEQUENCE_TYPE;
case SINGLE_INTEGER: return SequenceType.SINGLE_INTEGER;
case STRING_SEQUENCE: return SequenceType.STRING_SEQUENCE;
case OPTIONAL_STRING: return SequenceType.OPTIONAL_STRING;
case OPTIONAL_DECIMAL: return SequenceType.OPTIONAL_DECIMAL;
default:
throw new UnsupportedOperationException("Type " + type + " is not supported");
}
}
private SequenceType[] convertToSequenceTypes(XPathFunctionDefinition.Type[] types) {
SequenceType[] result = new SequenceType[types.length];
for (int i = 0; i < types.length; i++) {
result[i] = convertToSequenceType(types[i]);
}
return result;
}
@Override
public StructuredQName getFunctionQName() {
QName qName = definition.getQName();
return new StructuredQName(qName.getPrefix(), qName.getNamespaceURI(), qName.getLocalPart());
}
@Override
public SequenceType[] getArgumentTypes() {
return convertToSequenceTypes(definition.getArgumentTypes());
}
@Override
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
return convertToSequenceType(definition.getResultType());
}
@Override
public boolean dependsOnFocus() {
return definition.dependsOnContext();
}
@Override
public ExtensionFunctionCall makeCallExpression() {
XPathFunctionDefinition.FunctionCall call = definition.makeCallExpression();
return new ExtensionFunctionCall() {
@Override
public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException {
Object[] convertedArguments = new Object[definition.getArgumentTypes().length];
for (int i = 0; i < convertedArguments.length; i++) {
if (arguments[i] instanceof StringLiteral) {
convertedArguments[i] = ((StringLiteral) arguments[i]).getStringValue();
}
}
try {
call.staticInit(convertedArguments);
} catch (XPathFunctionException e) {
XPathException xPathException = new XPathException(e);
xPathException.setIsStaticError(true);
throw xPathException;
}
return null;
}
@Override
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
Node contextNode = null;
if (definition.dependsOnContext()) {
contextNode = XPathElementToNodeHelper.itemToNode(context.getContextItem());
}
Object[] convertedArguments = new Object[definition.getArgumentTypes().length];
for (int i = 0; i < convertedArguments.length; i++) {
switch (definition.getArgumentTypes()[i]) {
case SINGLE_STRING:
convertedArguments[i] = arguments[i].head().getStringValue();
break;
case SINGLE_ELEMENT:
convertedArguments[i] = arguments[i].head();
break;
default:
throw new UnsupportedOperationException("Don't know how to convert argument type " + definition.getArgumentTypes()[i]);
}
}
Object result = null;
try {
result = call.call(contextNode, convertedArguments);
} catch (XPathFunctionException e) {
throw new XPathException(e);
}
Sequence convertedResult = null;
switch (definition.getResultType()) {
case SINGLE_BOOLEAN:
convertedResult = BooleanValue.get((Boolean) result);
break;
case SINGLE_INTEGER:
convertedResult = Int64Value.makeIntegerValue((Integer) result);
break;
case SINGLE_STRING:
convertedResult = new StringValue((String) result);
break;
case OPTIONAL_STRING:
convertedResult = result instanceof Optional && ((Optional<String>) result).isPresent()
? new StringValue(((Optional<String>) result).get())
: EmptyAtomicSequence.INSTANCE;
break;
case STRING_SEQUENCE:
convertedResult = result instanceof List
? new SequenceExtent(((List<String>) result).stream().map(StringValue::new).collect(Collectors.toList()))
: EmptySequence.getInstance();
break;
case OPTIONAL_DECIMAL:
convertedResult = result instanceof Optional && ((Optional<Double>) result).isPresent()
? new BigDecimalValue(((Optional<Double>) result).get())
: EmptySequence.getInstance();
break;
default:
throw new UnsupportedOperationException("Don't know how to convert result type " + definition.getResultType());
}
return convertedResult;
}
};
}
}

View File

@ -17,10 +17,10 @@ import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.RootNode;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException;
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.util.DataMap;
@ -194,12 +194,13 @@ public class SaxonXPathRuleQuery {
for (final PropertyDescriptor<?> propertyDescriptor : properties.keySet()) {
final String name = propertyDescriptor.name();
if (!"xpath".equals(name) && !XPathRule.VERSION_DESCRIPTOR.name().equals(name)) {
if (!"xpath".equals(name)) {
staticCtx.declareProperty(propertyDescriptor);
}
}
for (ExtensionFunctionDefinition fun : xPathHandler.getRegisteredExtensionFunctions()) {
for (XPathFunctionDefinition xpathFun : xPathHandler.getRegisteredExtensionFunctions()) {
ExtensionFunctionDefinition fun = new SaxonExtensionFunctionDefinitionAdapter(xpathFun);
StructuredQName qname = fun.getFunctionQName();
staticCtx.declareNamespace(qname.getPrefix(), qname.getURI());
this.configuration.registerExtensionFunction(fun);

View File

@ -241,7 +241,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase {
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<ruleset name=\"test\">\n"
+ " <description>ruleset desc</description>\n"
+ " <rule deprecated=\"true\" ref=\"NewName\" name=\"OldName\"/>"
+ " <rule name=\"NewName\" message=\"m\" class=\"net.sourceforge.pmd.lang.rule.XPathRule\" language=\"dummy\">"
+ " <rule name=\"NewName\" message=\"m\" class=\"net.sourceforge.pmd.lang.rule.xpath.XPathRule\" language=\"dummy\">"
+ " <description>d</description>\n" + " <priority>2</priority>\n" + " </rule>"
+ "</ruleset>");
assertEquals(1, rs.getRules().size());
@ -269,7 +269,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase {
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<ruleset name=\"test\">\n"
+ " <description>ruleset desc</description>\n"
+ " <rule deprecated=\"true\" ref=\"NewName\" name=\"OldName\"/>"
+ " <rule name=\"NewName\" message=\"m\" class=\"net.sourceforge.pmd.lang.rule.XPathRule\" language=\"dummy\">"
+ " <rule name=\"NewName\" message=\"m\" class=\"net.sourceforge.pmd.lang.rule.xpath.XPathRule\" language=\"dummy\">"
+ " <description>d</description>\n"
+ " <priority>2</priority>\n"
+ " </rule>"

View File

@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.lang.rule.RuleSet.RuleSetBuilder;
import net.sourceforge.pmd.lang.rule.internal.RuleSetReference;
import net.sourceforge.pmd.lang.rule.xpath.XPathRule;
import net.sourceforge.pmd.util.internal.xml.SchemaConstants;
/**

View File

@ -19,6 +19,7 @@ import net.sourceforge.pmd.lang.ast.DummyNode;
import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode;
import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute;
import net.sourceforge.pmd.lang.document.TextRegion;
import net.sourceforge.pmd.lang.rule.xpath.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
import net.sourceforge.pmd.reporting.Report;

Some files were not shown because too many files have changed in this diff Show More