Merge pull request #4804 from adangel:issue-4309-xpath-cleanups
[core] XPath cleanups #4804
This commit is contained in:
@ -165,7 +165,6 @@ with a backslash when needed.
|
|||||||
```xml
|
```xml
|
||||||
<rule name="MyXpathRule" ...>
|
<rule name="MyXpathRule" ...>
|
||||||
<properties>
|
<properties>
|
||||||
<property name="version" value="2.0" />
|
|
||||||
<property name="intProp" type="List[Integer]" value="1,2,5" description="An IntegerMultiProperty." />
|
<property name="intProp" type="List[Integer]" value="1,2,5" description="An IntegerMultiProperty." />
|
||||||
<property name="reportedIdentifiers" type="List[String]" value="foo,bar"
|
<property name="reportedIdentifiers" type="List[String]" value="foo,bar"
|
||||||
description="A StringMultiProperty." />
|
description="A StringMultiProperty." />
|
||||||
|
@ -104,7 +104,7 @@ instead of mentioning the `ref` attribute, it mentions the `class` attribute,
|
|||||||
with the implementation class of your rule.
|
with the implementation class of your rule.
|
||||||
|
|
||||||
* **For Java rules:** this is the concrete class extending AbstractRule (transitively)
|
* **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`.
|
* **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.
|
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"
|
<rule name="MyXPathRule"
|
||||||
language="java"
|
language="java"
|
||||||
message="Violation!"
|
message="Violation!"
|
||||||
class="net.sourceforge.pmd.lang.rule.XPathRule">
|
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
|
||||||
<description>
|
<description>
|
||||||
Description
|
Description
|
||||||
</description>
|
</description>
|
||||||
|
@ -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)
|
See [the Saxonica documentation](https://www.saxonica.com/html/documentation/expressions/xpath31new.html)
|
||||||
for an introduction to new features in XPath 3.1.
|
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
|
## DOM representation of ASTs
|
||||||
|
@ -127,7 +127,7 @@ copy-paste into your ruleset XML. The resulting element looks like so:
|
|||||||
<rule name="DontCallBossShort"
|
<rule name="DontCallBossShort"
|
||||||
language="java"
|
language="java"
|
||||||
message="Boss wants to talk to you."
|
message="Boss wants to talk to you."
|
||||||
class="net.sourceforge.pmd.lang.rule.XPathRule">
|
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
|
||||||
<description>
|
<description>
|
||||||
TODO
|
TODO
|
||||||
</description>
|
</description>
|
||||||
|
@ -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
|
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.
|
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
|
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.
|
with PMD 7. PMD 7 by default uses XPath 3.1. See below [XPath](#xpath-migrating-from-10-to-20) for details.
|
||||||
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.
|
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).
|
See below [General AST Changes to avoid @Image](#general-ast-changes-to-avoid-image).
|
||||||
|
@ -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
|
* [#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
|
* [#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
|
* [#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`
|
* [#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 <lang>-<ruleset> hyphen notation for ruleset references
|
* [#4313](https://github.com/pmd/pmd/issues/4313): \[core] Remove support for <lang>-<ruleset> 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`
|
* [#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)
|
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.
|
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**
|
**Moved classes/consolidated packages**
|
||||||
|
|
||||||
* pmd-core
|
* pmd-core
|
||||||
@ -252,6 +258,7 @@ in the migration guide for details.
|
|||||||
* {%jdoc core::reporting.RuleViolation %}
|
* {%jdoc core::reporting.RuleViolation %}
|
||||||
* {%jdoc core::reporting.ViolationSuppressor %}
|
* {%jdoc core::reporting.ViolationSuppressor %}
|
||||||
* {%jdoc core::reporting.ParametricRuleViolation %} (moved from `net.sourcceforge.pmd.lang.rule`)
|
* {%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**
|
**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
|
* [#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
|
* [#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
|
* [#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`
|
* [#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 <lang>-<ruleset> hyphen notation for ruleset references
|
* [#4313](https://github.com/pmd/pmd/issues/4313): \[core] Remove support for <lang>-<ruleset> 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`
|
* [#4314](https://github.com/pmd/pmd/issues/4314): \[core] Remove ruleset compatibility filter (RuleSetFactoryCompatibility) and CLI option `--no-ruleset-compatibility`
|
||||||
|
@ -106,7 +106,7 @@ private class TestRunAs {
|
|||||||
since="6.13.0"
|
since="6.13.0"
|
||||||
language="apex"
|
language="apex"
|
||||||
message="Apex test methods should have @isTest annotation."
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#apexunittestmethodshouldhaveistestannotation">
|
||||||
<description>
|
<description>
|
||||||
Apex test methods should have `@isTest` annotation instead of the `testMethod` keyword,
|
Apex test methods should have `@isTest` annotation instead of the `testMethod` keyword,
|
||||||
@ -116,7 +116,6 @@ annotation for test classes and methods.
|
|||||||
</description>
|
</description>
|
||||||
<priority>3</priority>
|
<priority>3</priority>
|
||||||
<properties>
|
<properties>
|
||||||
<property name="version" value="2.0"/>
|
|
||||||
<property name="xpath">
|
<property name="xpath">
|
||||||
<value>
|
<value>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
@ -231,7 +230,7 @@ trigger Accounts on Account (before insert, before update, before delete, after
|
|||||||
since="6.18.0"
|
since="6.18.0"
|
||||||
language="apex"
|
language="apex"
|
||||||
message="Calls to System.debug should specify a logging level."
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_bestpractices.html#debugsshoulduselogginglevel">
|
||||||
<description>
|
<description>
|
||||||
The first parameter of System.debug, when using the signature with two parameters, is a LoggingLevel enum.
|
The first parameter of System.debug, when using the signature with two parameters, is a LoggingLevel enum.
|
||||||
|
@ -37,7 +37,7 @@ public class fooClass { } // This will be reported unless you change the regex
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="5.6.0"
|
since="5.6.0"
|
||||||
message="Avoid using 'if...else' statements without curly braces"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#ifelsestmtsmustusebraces">
|
||||||
<description>
|
<description>
|
||||||
Avoid using if..else statements without using surrounding braces. If the code formatting
|
Avoid using if..else statements without using surrounding braces. If the code formatting
|
||||||
@ -74,7 +74,7 @@ else
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="5.6.0"
|
since="5.6.0"
|
||||||
message="Avoid using if statements without curly braces"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#ifstmtsmustusebraces">
|
||||||
<description>
|
<description>
|
||||||
Avoid using if statements without using braces to surround the code block. If the code
|
Avoid using if statements without using braces to surround the code block. If the code
|
||||||
@ -156,7 +156,7 @@ public class Foo {
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="5.6.0"
|
since="5.6.0"
|
||||||
message="Avoid using 'for' statements without curly braces"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#forloopsmustusebraces">
|
||||||
<description>
|
<description>
|
||||||
Avoid using 'for' statements without using surrounding braces. If the code formatting or
|
Avoid using 'for' statements without using surrounding braces. If the code formatting or
|
||||||
@ -270,7 +270,7 @@ public class Foo {
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="6.7.0"
|
since="6.7.0"
|
||||||
message="Use one statement for each line, it enhances code readability."
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#onedeclarationperline">
|
||||||
<description>
|
<description>
|
||||||
Apex allows the use of several variables declaration of the same type on one line. However, it
|
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"
|
language="apex"
|
||||||
since="5.6.0"
|
since="5.6.0"
|
||||||
message="Avoid using 'while' statements without curly braces"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_codestyle.html#whileloopsmustusebraces">
|
||||||
<description>
|
<description>
|
||||||
Avoid using 'while' statements without using braces to surround the code block. If the code
|
Avoid using 'while' statements without using braces to surround the code block. If the code
|
||||||
|
@ -54,7 +54,7 @@ public class Foo {
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="6.0.0"
|
since="6.0.0"
|
||||||
message="Avoid directly accessing Trigger.old and Trigger.new"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#avoiddirectaccesstriggermap">
|
||||||
<description>
|
<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.
|
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"
|
language="apex"
|
||||||
since="6.0.0"
|
since="6.0.0"
|
||||||
message="Avoid empty catch blocks"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptycatchblock">
|
||||||
<description>
|
<description>
|
||||||
Empty Catch Block finds instances where an exception is caught, but nothing is done.
|
Empty Catch Block finds instances where an exception is caught, but nothing is done.
|
||||||
@ -177,7 +177,7 @@ public void doSomething() {
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="6.0.0"
|
since="6.0.0"
|
||||||
message="Avoid empty 'if' statements"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptyifstmt">
|
||||||
<description>
|
<description>
|
||||||
Empty If Statement finds instances where a condition is checked but nothing is done about it.
|
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"
|
language="apex"
|
||||||
since="6.0.0"
|
since="6.0.0"
|
||||||
message="Avoid empty block statements."
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptystatementblock">
|
||||||
<description>
|
<description>
|
||||||
Empty block statements serve no purpose and should be removed.
|
Empty block statements serve no purpose and should be removed.
|
||||||
@ -247,7 +247,7 @@ public class Foo {
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="6.0.0"
|
since="6.0.0"
|
||||||
message="Avoid empty try or finally blocks"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptytryorfinallyblock">
|
||||||
<description>
|
<description>
|
||||||
Avoid empty try or finally blocks - what's the point?
|
Avoid empty try or finally blocks - what's the point?
|
||||||
@ -291,7 +291,7 @@ public class Foo {
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="6.0.0"
|
since="6.0.0"
|
||||||
message="Avoid empty 'while' statements"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#emptywhilestmt">
|
||||||
<description>
|
<description>
|
||||||
Empty While Statement finds all instances where a while statement does nothing.
|
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"
|
language="apex"
|
||||||
since="6.22.0"
|
since="6.22.0"
|
||||||
message="Test methods must be in test classes"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#testmethodsmustbeintestclasses">
|
||||||
<description>
|
<description>
|
||||||
Test methods marked as a testMethod or annotated with @IsTest,
|
Test methods marked as a testMethod or annotated with @IsTest,
|
||||||
|
@ -13,7 +13,7 @@ Rules that flag suboptimal code.
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="6.36.0"
|
since="6.36.0"
|
||||||
message="Avoid debug statements since they impact on performance"
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#avoiddebugstatements">
|
||||||
<description>
|
<description>
|
||||||
Debug statements contribute to longer transactions and consume Apex CPU time even when debug logs are not being captured.
|
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>
|
</description>
|
||||||
<priority>3</priority>
|
<priority>3</priority>
|
||||||
<properties>
|
<properties>
|
||||||
<property name="version" value="2.0"/>
|
|
||||||
<property name="xpath">
|
<property name="xpath">
|
||||||
<value>
|
<value>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
@ -141,7 +140,7 @@ public class Something {
|
|||||||
language="apex"
|
language="apex"
|
||||||
since="6.40.0"
|
since="6.40.0"
|
||||||
message="DescribeSObjectResult could be being loaded eagerly with all child relationships."
|
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">
|
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_performance.html#eagerlyloadeddescribesobjectresult">
|
||||||
<description>
|
<description>
|
||||||
This rule finds `DescribeSObjectResult`s which could have been loaded eagerly via `SObjectType.getDescribe()`.
|
This rule finds `DescribeSObjectResult`s which could have been loaded eagerly via `SObjectType.getDescribe()`.
|
||||||
@ -172,7 +171,6 @@ Properties:
|
|||||||
<priority>3</priority>
|
<priority>3</priority>
|
||||||
<properties>
|
<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="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">
|
<property name="xpath">
|
||||||
<value>
|
<value>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import net.sourceforge.pmd.lang.apex.ast.ApexParserTestBase;
|
import net.sourceforge.pmd.lang.apex.ast.ApexParserTestBase;
|
||||||
import net.sourceforge.pmd.lang.document.FileId;
|
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;
|
import net.sourceforge.pmd.reporting.Report;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</description>
|
</description>
|
||||||
|
|
||||||
<rule name="ReportAllRootNodes" language="dummy" since="1.0" message="Violation from ReportAllRootNodes"
|
<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">
|
externalInfoUrl="${pmd.website.baseurl}/rules/test/TestRuleset3.xml#Ruleset3Rule1">
|
||||||
<description>Just for test</description>
|
<description>Just for test</description>
|
||||||
<priority>3</priority>
|
<priority>3</priority>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<description/>
|
<description/>
|
||||||
<rule name="ExceptionThrowingRule"
|
<rule name="ExceptionThrowingRule"
|
||||||
language="java"
|
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>
|
<description>Use this rule to produce a processing error.</description>
|
||||||
<priority>3</priority>
|
<priority>3</priority>
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -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 {
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import net.sourceforge.pmd.lang.LanguageVersion;
|
|||||||
import net.sourceforge.pmd.lang.document.FileId;
|
import net.sourceforge.pmd.lang.document.FileId;
|
||||||
import net.sourceforge.pmd.lang.document.TextFile;
|
import net.sourceforge.pmd.lang.document.TextFile;
|
||||||
import net.sourceforge.pmd.lang.rule.internal.RuleSetReference;
|
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
|
* This class represents a collection of rules along with some optional filter
|
||||||
|
@ -2,12 +2,9 @@
|
|||||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
* 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.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -16,11 +13,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
|
|
||||||
import net.sourceforge.pmd.lang.LanguageProcessor;
|
import net.sourceforge.pmd.lang.LanguageProcessor;
|
||||||
import net.sourceforge.pmd.lang.ast.Node;
|
import net.sourceforge.pmd.lang.ast.Node;
|
||||||
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException;
|
import net.sourceforge.pmd.lang.rule.AbstractRule;
|
||||||
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
|
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.DeprecatedAttrLogger;
|
||||||
import net.sourceforge.pmd.lang.rule.xpath.internal.SaxonXPathRuleQuery;
|
import net.sourceforge.pmd.lang.rule.xpath.internal.SaxonXPathRuleQuery;
|
||||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
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);
|
private static final Logger LOG = LoggerFactory.getLogger(XPathRule.class);
|
||||||
|
|
||||||
// TODO move to XPath subpackage
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #XPathRule(XPathVersion, String)}
|
* @deprecated Use {@link #XPathRule(XPathVersion, String)}
|
||||||
*/
|
*/
|
||||||
@ -47,17 +42,6 @@ public final class XPathRule extends AbstractRule {
|
|||||||
.defaultValue("")
|
.defaultValue("")
|
||||||
.build();
|
.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()}.
|
* 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
|
@Deprecated
|
||||||
public XPathRule() {
|
public XPathRule() {
|
||||||
definePropertyDescriptor(XPATH_DESCRIPTOR);
|
definePropertyDescriptor(XPATH_DESCRIPTOR);
|
||||||
definePropertyDescriptor(VERSION_DESCRIPTOR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,7 +77,6 @@ public final class XPathRule extends AbstractRule {
|
|||||||
Objects.requireNonNull(expression, "XPath expression is null");
|
Objects.requireNonNull(expression, "XPath expression is null");
|
||||||
|
|
||||||
setProperty(XPathRule.XPATH_DESCRIPTOR, expression);
|
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;
|
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.
|
* Returns the XPath expression that implements this rule.
|
||||||
*/
|
*/
|
||||||
@ -144,11 +118,7 @@ public final class XPathRule extends AbstractRule {
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(LanguageProcessor languageProcessor) {
|
public void initialize(LanguageProcessor languageProcessor) {
|
||||||
String xpath = getXPathExpression();
|
String xpath = getXPathExpression();
|
||||||
XPathVersion version = getVersion();
|
XPathVersion version = XPathVersion.DEFAULT;
|
||||||
|
|
||||||
if (version == null) {
|
|
||||||
throw new IllegalStateException("Invalid XPath version, should have been caught by Rule::dysfunctionReason");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
xpathRuleQuery = new SaxonXPathRuleQuery(xpath,
|
xpathRuleQuery = new SaxonXPathRuleQuery(xpath,
|
||||||
@ -182,9 +152,8 @@ public final class XPathRule extends AbstractRule {
|
|||||||
|
|
||||||
|
|
||||||
private void logXPathRuleChainUsage(boolean usesRuleChain) {
|
private void logXPathRuleChainUsage(boolean usesRuleChain) {
|
||||||
LOG.debug("{} rule chain for XPath {} rule: {} ({})",
|
LOG.debug("{} rule chain for XPath rule: {} ({})",
|
||||||
usesRuleChain ? "Using" : "no",
|
usesRuleChain ? "Using" : "no",
|
||||||
getProperty(XPathRule.VERSION_DESCRIPTOR),
|
|
||||||
getName(),
|
getName(),
|
||||||
getRuleSetName());
|
getRuleSetName());
|
||||||
}
|
}
|
||||||
@ -192,19 +161,9 @@ public final class XPathRule extends AbstractRule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String dysfunctionReason() {
|
public String dysfunctionReason() {
|
||||||
if (getVersion() == null) {
|
if (StringUtils.isBlank(getXPathExpression())) {
|
||||||
return "Invalid XPath version '" + getProperty(VERSION_DESCRIPTOR) + "'";
|
|
||||||
} else if (StringUtils.isBlank(getXPathExpression())) {
|
|
||||||
return "Missing XPath expression";
|
return "Missing XPath expression";
|
||||||
}
|
}
|
||||||
return null;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -11,8 +11,6 @@ import java.util.Set;
|
|||||||
import net.sourceforge.pmd.lang.rule.xpath.internal.DefaultXPathFunctions;
|
import net.sourceforge.pmd.lang.rule.xpath.internal.DefaultXPathFunctions;
|
||||||
import net.sourceforge.pmd.util.CollectionUtil;
|
import net.sourceforge.pmd.util.CollectionUtil;
|
||||||
|
|
||||||
import net.sf.saxon.lib.ExtensionFunctionDefinition;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for performing Language specific XPath handling, such as
|
* 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.
|
* Returns the set of extension functions for this language module.
|
||||||
* These are the additional functions available in XPath queries.
|
* These are the additional functions available in XPath queries.
|
||||||
*/
|
*/
|
||||||
Set<ExtensionFunctionDefinition> getRegisteredExtensionFunctions();
|
Set<XPathFunctionDefinition> getRegisteredExtensionFunctions();
|
||||||
|
|
||||||
|
|
||||||
static XPathHandler noFunctionDefinitions() {
|
static XPathHandler noFunctionDefinitions() {
|
||||||
@ -34,8 +32,8 @@ public interface XPathHandler {
|
|||||||
/**
|
/**
|
||||||
* Returns a default XPath handler.
|
* Returns a default XPath handler.
|
||||||
*/
|
*/
|
||||||
static XPathHandler getHandlerForFunctionDefs(ExtensionFunctionDefinition first, ExtensionFunctionDefinition... defs) {
|
static XPathHandler getHandlerForFunctionDefs(XPathFunctionDefinition first, XPathFunctionDefinition... defs) {
|
||||||
Set<ExtensionFunctionDefinition> set = new HashSet<>(CollectionUtil.setOf(first, defs));
|
Set<XPathFunctionDefinition> set = new HashSet<>(CollectionUtil.setOf(first, defs));
|
||||||
set.addAll(DefaultXPathFunctions.getDefaultFunctions());
|
set.addAll(DefaultXPathFunctions.getDefaultFunctions());
|
||||||
|
|
||||||
return () -> Collections.unmodifiableSet(set);
|
return () -> Collections.unmodifiableSet(set);
|
||||||
|
@ -7,23 +7,15 @@ package net.sourceforge.pmd.lang.rule.xpath.internal;
|
|||||||
import java.util.function.ToIntFunction;
|
import java.util.function.ToIntFunction;
|
||||||
|
|
||||||
import net.sourceforge.pmd.lang.ast.Node;
|
import net.sourceforge.pmd.lang.ast.Node;
|
||||||
import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef;
|
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
|
||||||
|
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionException;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that returns the current file name.
|
* A function that returns the current file name.
|
||||||
*
|
*
|
||||||
* @author Clément Fournier
|
* @author Clément Fournier
|
||||||
*/
|
*/
|
||||||
public final class CoordinateXPathFunction extends AbstractXPathFunctionDef {
|
public final class CoordinateXPathFunction extends XPathFunctionDefinition {
|
||||||
|
|
||||||
public static final CoordinateXPathFunction START_LINE =
|
public static final CoordinateXPathFunction START_LINE =
|
||||||
new CoordinateXPathFunction("startLine", Node::getBeginLine);
|
new CoordinateXPathFunction("startLine", Node::getBeginLine);
|
||||||
@ -34,9 +26,7 @@ public final class CoordinateXPathFunction extends AbstractXPathFunctionDef {
|
|||||||
public static final CoordinateXPathFunction END_COLUMN =
|
public static final CoordinateXPathFunction END_COLUMN =
|
||||||
new CoordinateXPathFunction("endColumn", Node::getEndColumn);
|
new CoordinateXPathFunction("endColumn", Node::getEndColumn);
|
||||||
|
|
||||||
private static final SequenceType[] A_SINGLE_ELEMENT = {
|
private static final Type[] A_SINGLE_ELEMENT = { Type.SINGLE_ELEMENT };
|
||||||
NodeKindTest.makeNodeKindTest(Type.ELEMENT).one(),
|
|
||||||
};
|
|
||||||
public static final String PMD_NODE_USER_DATA = "pmd.node";
|
public static final String PMD_NODE_USER_DATA = "pmd.node";
|
||||||
private final ToIntFunction<Node> getter;
|
private final ToIntFunction<Node> getter;
|
||||||
|
|
||||||
@ -46,32 +36,26 @@ public final class CoordinateXPathFunction extends AbstractXPathFunctionDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SequenceType[] getArgumentTypes() {
|
public Type[] getArgumentTypes() {
|
||||||
return A_SINGLE_ELEMENT;
|
return A_SINGLE_ELEMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
|
public Type getResultType() {
|
||||||
return SequenceType.SINGLE_INTEGER;
|
return Type.SINGLE_INTEGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtensionFunctionCall makeCallExpression() {
|
public FunctionCall makeCallExpression() {
|
||||||
return new ExtensionFunctionCall() {
|
return (contextNode, arguments) -> {
|
||||||
|
Node node = XPathElementToNodeHelper.itemToNode(arguments[0]);
|
||||||
@Override
|
if (node == null) {
|
||||||
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
|
throw new XPathFunctionException(
|
||||||
Node node = XPathElementToNodeHelper.itemToNode(arguments[0]);
|
"Cannot call function '" + getQName().getLocalPart()
|
||||||
if (node == null) {
|
+ "' on argument " + arguments[0]
|
||||||
throw new XPathException(
|
);
|
||||||
"Cannot call function '" + getFunctionQName().getLocalPart()
|
|
||||||
+ "' on argument " + arguments[0]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Int64Value.makeIntegerValue(getter.applyAsInt(node));
|
|
||||||
}
|
}
|
||||||
|
return getter.applyAsInt(node);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,15 @@ package net.sourceforge.pmd.lang.rule.xpath.internal;
|
|||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
|
||||||
import net.sourceforge.pmd.util.CollectionUtil;
|
import net.sourceforge.pmd.util.CollectionUtil;
|
||||||
|
|
||||||
import net.sf.saxon.lib.ExtensionFunctionDefinition;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default XPath functions provided by pmd-core.
|
* Default XPath functions provided by pmd-core.
|
||||||
*/
|
*/
|
||||||
public final class DefaultXPathFunctions {
|
public final class DefaultXPathFunctions {
|
||||||
|
|
||||||
private static final Set<ExtensionFunctionDefinition> DEFAULTS =
|
private static final Set<XPathFunctionDefinition> DEFAULTS =
|
||||||
CollectionUtil.immutableSetOf(
|
CollectionUtil.immutableSetOf(
|
||||||
FileNameXPathFunction.INSTANCE,
|
FileNameXPathFunction.INSTANCE,
|
||||||
CoordinateXPathFunction.START_LINE,
|
CoordinateXPathFunction.START_LINE,
|
||||||
@ -29,7 +28,7 @@ public final class DefaultXPathFunctions {
|
|||||||
// utility class
|
// utility class
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<ExtensionFunctionDefinition> getDefaultFunctions() {
|
public static Set<XPathFunctionDefinition> getDefaultFunctions() {
|
||||||
return DEFAULTS;
|
return DEFAULTS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,8 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import net.sourceforge.pmd.lang.rule.Rule;
|
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.Attribute;
|
||||||
|
import net.sourceforge.pmd.lang.rule.xpath.XPathRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records usages of deprecated attributes in XPath rules. This needs
|
* Records usages of deprecated attributes in XPath rules. This needs
|
||||||
|
@ -6,23 +6,16 @@ package net.sourceforge.pmd.lang.rule.xpath.internal;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import net.sourceforge.pmd.lang.ast.Node;
|
|
||||||
import net.sourceforge.pmd.lang.ast.RootNode;
|
import net.sourceforge.pmd.lang.ast.RootNode;
|
||||||
import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef;
|
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionDefinition;
|
||||||
|
import net.sourceforge.pmd.lang.rule.xpath.impl.XPathFunctionException;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function that returns the current file name.
|
* A function that returns the current file name.
|
||||||
*
|
*
|
||||||
* @author Clément Fournier
|
* @author Clément Fournier
|
||||||
*/
|
*/
|
||||||
public final class FileNameXPathFunction extends AbstractXPathFunctionDef {
|
public final class FileNameXPathFunction extends XPathFunctionDefinition {
|
||||||
|
|
||||||
public static final FileNameXPathFunction INSTANCE = new FileNameXPathFunction();
|
public static final FileNameXPathFunction INSTANCE = new FileNameXPathFunction();
|
||||||
|
|
||||||
@ -31,36 +24,31 @@ public final class FileNameXPathFunction extends AbstractXPathFunctionDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SequenceType[] getArgumentTypes() {
|
public Type getResultType() {
|
||||||
return new SequenceType[0];
|
return Type.SINGLE_STRING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
|
public boolean dependsOnContext() {
|
||||||
return SequenceType.STRING_SEQUENCE;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ExtensionFunctionCall makeCallExpression() {
|
public FunctionCall makeCallExpression() {
|
||||||
return new ExtensionFunctionCall() {
|
return (node, arguments) -> {
|
||||||
|
if (node == null) {
|
||||||
@Override
|
throw new XPathFunctionException(
|
||||||
public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
|
"Cannot call function '" + getQName().getLocalPart()
|
||||||
Node node = XPathElementToNodeHelper.itemToNode(context.getContextItem());
|
+ "' without context item"
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -17,10 +17,10 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import net.sourceforge.pmd.lang.ast.Node;
|
import net.sourceforge.pmd.lang.ast.Node;
|
||||||
import net.sourceforge.pmd.lang.ast.RootNode;
|
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;
|
||||||
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase;
|
import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase;
|
||||||
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
|
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.lang.rule.xpath.impl.XPathHandler;
|
||||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||||
import net.sourceforge.pmd.util.DataMap;
|
import net.sourceforge.pmd.util.DataMap;
|
||||||
@ -194,12 +194,13 @@ public class SaxonXPathRuleQuery {
|
|||||||
|
|
||||||
for (final PropertyDescriptor<?> propertyDescriptor : properties.keySet()) {
|
for (final PropertyDescriptor<?> propertyDescriptor : properties.keySet()) {
|
||||||
final String name = propertyDescriptor.name();
|
final String name = propertyDescriptor.name();
|
||||||
if (!"xpath".equals(name) && !XPathRule.VERSION_DESCRIPTOR.name().equals(name)) {
|
if (!"xpath".equals(name)) {
|
||||||
staticCtx.declareProperty(propertyDescriptor);
|
staticCtx.declareProperty(propertyDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ExtensionFunctionDefinition fun : xPathHandler.getRegisteredExtensionFunctions()) {
|
for (XPathFunctionDefinition xpathFun : xPathHandler.getRegisteredExtensionFunctions()) {
|
||||||
|
ExtensionFunctionDefinition fun = new SaxonExtensionFunctionDefinitionAdapter(xpathFun);
|
||||||
StructuredQName qname = fun.getFunctionQName();
|
StructuredQName qname = fun.getFunctionQName();
|
||||||
staticCtx.declareNamespace(qname.getPrefix(), qname.getURI());
|
staticCtx.declareNamespace(qname.getPrefix(), qname.getURI());
|
||||||
this.configuration.registerExtensionFunction(fun);
|
this.configuration.registerExtensionFunction(fun);
|
||||||
|
@ -241,7 +241,7 @@ class RuleSetFactoryTest extends RulesetFactoryTestBase {
|
|||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<ruleset name=\"test\">\n"
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<ruleset name=\"test\">\n"
|
||||||
+ " <description>ruleset desc</description>\n"
|
+ " <description>ruleset desc</description>\n"
|
||||||
+ " <rule deprecated=\"true\" ref=\"NewName\" name=\"OldName\"/>"
|
+ " <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>"
|
+ " <description>d</description>\n" + " <priority>2</priority>\n" + " </rule>"
|
||||||
+ "</ruleset>");
|
+ "</ruleset>");
|
||||||
assertEquals(1, rs.getRules().size());
|
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"
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<ruleset name=\"test\">\n"
|
||||||
+ " <description>ruleset desc</description>\n"
|
+ " <description>ruleset desc</description>\n"
|
||||||
+ " <rule deprecated=\"true\" ref=\"NewName\" name=\"OldName\"/>"
|
+ " <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"
|
+ " <description>d</description>\n"
|
||||||
+ " <priority>2</priority>\n"
|
+ " <priority>2</priority>\n"
|
||||||
+ " </rule>"
|
+ " </rule>"
|
||||||
|
@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import net.sourceforge.pmd.lang.rule.RuleSet.RuleSetBuilder;
|
import net.sourceforge.pmd.lang.rule.RuleSet.RuleSetBuilder;
|
||||||
import net.sourceforge.pmd.lang.rule.internal.RuleSetReference;
|
import net.sourceforge.pmd.lang.rule.internal.RuleSetReference;
|
||||||
|
import net.sourceforge.pmd.lang.rule.xpath.XPathRule;
|
||||||
import net.sourceforge.pmd.util.internal.xml.SchemaConstants;
|
import net.sourceforge.pmd.util.internal.xml.SchemaConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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.DummyNode.DummyRootNode;
|
||||||
import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute;
|
import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute;
|
||||||
import net.sourceforge.pmd.lang.document.TextRegion;
|
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.lang.rule.xpath.XPathVersion;
|
||||||
import net.sourceforge.pmd.reporting.Report;
|
import net.sourceforge.pmd.reporting.Report;
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user