Issue #4421: Support suppression-xpath element in SuppressionLoader
diff --git a/config/ant-phase-verify.xml b/config/ant-phase-verify.xml
index b9a2331..feba93f 100644
--- a/config/ant-phase-verify.xml
+++ b/config/ant-phase-verify.xml
@@ -38,6 +38,8 @@
       <property key="checkstyle.importcontroltest.file" file="config/import-control-test.xml"/>
       <property key="checkstyle.suppressions.file"
                 file="config/suppressions.xml"/>
+      <property key="checkstyle.suppressions-xpath.file"
+                file="config/suppressions-xpath.xml"/>
     </checkstyle>
 
     <tstamp>
diff --git a/config/checkstyle_checks.xml b/config/checkstyle_checks.xml
index c94e8f5..8e158f4 100644
--- a/config/checkstyle_checks.xml
+++ b/config/checkstyle_checks.xml
@@ -312,6 +312,9 @@
       <property name="offCommentFormat" value="CSOFF\: .*"/>
       <property name="onCommentFormat" value="CSON\: .*"/>
     </module>
+    <module name="SuppressionXpathFilter">
+      <property name="file" value="${checkstyle.suppressions-xpath.file}"/>
+    </module>
     <module name="SuppressWithNearbyCommentFilter">
       <property name="commentFormat" value="-@cs\[(\w{8,}(\|\w{8,})*)\] \w[\(\)\-\.\'\`\,\:\;\w ]{10,}"/>
       <property name="checkFormat" value="$1"/>
diff --git a/config/suppressions-xpath.xml b/config/suppressions-xpath.xml
new file mode 100644
index 0000000..65fba6d
--- /dev/null
+++ b/config/suppressions-xpath.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE suppressions PUBLIC
+    "-//Puppy Crawl//DTD Suppressions Xpath Experimental 1.2//EN"
+    "http://checkstyle.sourceforge.net/dtds/suppressions_1_2_xpath_experimental.dtd">
+
+<suppressions>
+    <!-- Tone down the checking for test code -->
+    <suppress-xpath checks="NPathComplexity"
+                    query="/CLASS_DEF[@text='XdocsPagesTest']
+                    //METHOD_DEF[@text='validateCheckSection']"/>
+
+    <suppress-xpath checks="CyclomaticComplexity"
+                    query="/CLASS_DEF[@text='XdocsPagesTest']//METHOD_DEF"/>
+
+    <!-- Suppressions from PMD configuration-->
+    <!-- JavadocMethodCheck, JavadocStyleCheck, JavadocUtils.getJavadocTags() - deprecated -->
+    <suppress-xpath checks="CyclomaticComplexity"
+                    query="/CLASS_DEF[@text='JavadocMethodCheck' or @text='JavadocStyleCheck'
+                    or @text='CustomImportOrderCheck']//METHOD_DEF"/>
+</suppressions>
diff --git a/config/suppressions.xml b/config/suppressions.xml
index c589879..aeb9c33 100644
--- a/config/suppressions.xml
+++ b/config/suppressions.xml
@@ -24,8 +24,6 @@
               files="AbstractCheckTest.java|AbstractClassNameCheckTest.java|AbstractTypeAwareCheckTest.java|AbstractJavadocCheckTest.java|AbstractViolationReporterTest.java|AbstractFileSetCheckTest.java|AbstractNodeTest.java"/>
 
     <!-- Tone down the checking for test code -->
-    <suppress checks="CyclomaticComplexity" files="[\\/]XdocsPagesTest\.java"/>
-    <suppress checks="NPathComplexity" files="[\\/]XdocsPagesTest\.java"/>
     <suppress checks="IllegalCatch" files="[\\/]internal[\\/].*[\\/]\w+Util\.java"/>
     <suppress checks="EmptyBlock" files=".*[\\/]src[\\/]test[\\/]"/>
     <suppress message="Missing a Javadoc comment|Missing package-info.java file|Expected @throws tag for|missing an @author tag" files=".*[\\/]src[\\/](test|it)[\\/]"/>
@@ -85,12 +83,6 @@
     <!-- Should be fixed after moving https://github.com/sevntu-checkstyle/sevntu.checkstyle/blob/master/sevntu-checks/src/main/java/com/github/sevntu/checkstyle/checks/coding/ReturnCountExtendedCheck.java into the main repo, to allow skip guard sentences(or by topLinesToIgnoreCount) -->
     <suppress checks="ReturnCount" files="(ConfigurationLoader|LambdaHandler)\.java"/>
 
-    <!-- Suppressions from PMD configuration-->
-    <!-- JavadocMethodCheck, JavadocStyleCheck, JavadocUtils.getJavadocTags() - deprecated -->
-    <suppress checks="CyclomaticComplexity" files="JavadocMethodCheck\.java"/>
-    <suppress checks="CyclomaticComplexity" files="JavadocStyleCheck\.java"/>
-    <suppress checks="CyclomaticComplexity" files="CustomImportOrderCheck\.java"/>
-
     <!-- HandlerFactory crosses allowed limit for executable statements -->
     <suppress checks="ExecutableStatementCount" files="HandlerFactory\.java"/>
 </suppressions>
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalkerAuditEvent.java b/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalkerAuditEvent.java
index 629c680..a881efb 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalkerAuditEvent.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/TreeWalkerAuditEvent.java
@@ -105,6 +105,14 @@
     }
 
     /**
+     * Gets the column char index associated with the message.
+     * @return the column char index associated with the message
+     */
+    public int getColumnCharIndex() {
+        return localizedMessage.getColumnCharIndex();
+    }
+
+    /**
      * Returns id of module.
      * @return the identifier of the module that generated the event. Can return
      *         null.
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/filters/XpathFilter.java b/src/main/java/com/puppycrawl/tools/checkstyle/filters/XpathFilter.java
index bd62cb6..6003404 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/filters/XpathFilter.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/filters/XpathFilter.java
@@ -161,7 +161,7 @@
                 final AbstractNode abstractNode = (AbstractNode) item;
                 isMatching = abstractNode.getTokenType() == event.getTokenType()
                         && abstractNode.getLineNumber() == event.getLine()
-                        && abstractNode.getColumnNumber() == event.getColumn();
+                        && abstractNode.getColumnNumber() == event.getColumnCharIndex();
                 if (isMatching) {
                     break;
                 }
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/internal/AllChecksTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/internal/AllChecksTest.java
index 6f8f1dd..f821554 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/internal/AllChecksTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/internal/AllChecksTest.java
@@ -330,8 +330,6 @@
     public void testAllModulesAreReferencedInConfigFile() throws Exception {
         final Set<String> modulesReferencedInConfig = CheckUtil.getConfigCheckStyleModules();
         final Set<String> moduleNames = CheckUtil.getSimpleNames(CheckUtil.getCheckstyleModules());
-        //Issue: https://github.com/checkstyle/checkstyle/issues/4421
-        moduleNames.remove("SuppressionXpathFilter");
 
         moduleNames.stream().filter(check -> !modulesReferencedInConfig.contains(check))
             .forEach(check -> {
@@ -428,8 +426,6 @@
         // these are documented on non-'config_' pages
         checkstyleModulesNames.remove("TreeWalker");
         checkstyleModulesNames.remove("Checker");
-        //Issue: https://github.com/checkstyle/checkstyle/issues/4421
-        checkstyleModulesNames.remove("SuppressionXpathFilter");
 
         checkstyleModulesNames.stream()
             .filter(moduleName -> !modulesNamesWhichHaveXdocs.contains(moduleName))
@@ -445,8 +441,6 @@
     public void testAllCheckstyleModulesInCheckstyleConfig() throws Exception {
         final Set<String> configChecks = CheckUtil.getConfigCheckStyleModules();
         final Set<String> moduleNames = CheckUtil.getSimpleNames(CheckUtil.getCheckstyleModules());
-        //Issue: https://github.com/checkstyle/checkstyle/issues/4421
-        moduleNames.remove("SuppressionXpathFilter");
 
         for (String moduleName : moduleNames) {
             Assert.assertTrue("checkstyle_checks.xml is missing module: " + moduleName,
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/internal/XdocsPagesTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/internal/XdocsPagesTest.java
index 59d3751..f57c9bc 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/internal/XdocsPagesTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/internal/XdocsPagesTest.java
@@ -215,6 +215,7 @@
         // can't process non-existent examples, or out of context snippets
         if (!code.contains("com.mycompany") && !code.contains("checkstyle-packages")
                 && !code.contains("MethodLimit") && !code.contains("<suppress ")
+                && !code.contains("<suppress-xpath ")
                 && !code.contains("<import-control ")
                 && !unserializedSource.startsWith("<property ")
                 && !unserializedSource.startsWith("<taskdef ")) {
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/internal/utils/ConfigurationUtil.java b/src/test/java/com/puppycrawl/tools/checkstyle/internal/utils/ConfigurationUtil.java
index 948a551..a88fb96 100644
--- a/src/test/java/com/puppycrawl/tools/checkstyle/internal/utils/ConfigurationUtil.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/internal/utils/ConfigurationUtil.java
@@ -38,6 +38,7 @@
         props.setProperty("checkstyle.basedir", "basedir");
         props.setProperty("checkstyle.cache.file", "file");
         props.setProperty("checkstyle.suppressions.file", "file");
+        props.setProperty("checkstyle.suppressions-xpath.file", "file");
         props.setProperty("checkstyle.header.file", "file");
         props.setProperty("checkstyle.regexp.header.file", "file");
         props.setProperty("checkstyle.importcontrol.file", "file");
diff --git a/src/xdocs/config_filters.xml b/src/xdocs/config_filters.xml
index a421177..ff73d6f 100644
--- a/src/xdocs/config_filters.xml
+++ b/src/xdocs/config_filters.xml
@@ -648,6 +648,229 @@
       </subsection>
     </section>
 
+    <section name="SuppressionXpathFilter">
+      <subsection name="Description">
+        <p>Since Checkstyle 8.6</p>
+        <p>
+          Filter <code>SuppressionXpathFilter</code> works as
+          <a href="config_filters.html#SuppressionFilter">SuppressionFilter</a>.
+          Additionally, filter processes <code>suppress-xpath</code> elements,
+          which contains xpath-expressions. Xpath-expressions
+          are queries for suppressed nodes inside the AST tree.
+        </p>
+        <p>
+          Note, after resolving
+          <a href="https://github.com/checkstyle/checkstyle/issues/4530">issue 4530</a>
+          CLI will have option to generate the basic suppression xpath to elements.
+        </p>
+        <p>
+          Currently, filter supports the following
+          checks:
+        </p>
+        <ul>
+          <li>CyclomaticComplexityCheck</li>
+          <li>DeclarationOrderCheck</li>
+          <li>DefaultComesLastCheck</li>
+          <li>ExplicitInitializationCheck</li>
+          <li>FallThroughCheck</li>
+          <li>HiddenFieldCheck</li>
+          <li>IllegalCatchCheck</li>
+          <li>IllegalThrowsCheck</li>
+          <li>JavadocMethodCheck</li>
+          <li>JavadocVariableCheck</li>
+          <li>ImportControlCheck</li>
+          <li>LeftCurlyCheck</li>
+          <li>MethodParamPadCheck</li>
+          <li>MultipleVariableDeclarationCheck</li>
+          <li>NestedForDepthCheck</li>
+          <li>NestedIfDepthCheck</li>
+          <li>NestedTryDepthCheck</li>
+          <li>NPathComplexityCheck</li>
+          <li>OneStatementPerLineCheck</li>
+          <li>OuterTypeNumberCheck</li>
+          <li>RequireThisCheck</li>
+          <li>RightCurlyCheck</li>
+        </ul>
+        <p>
+          Note, that support for other Checks will be available after resolving
+          <a href="https://github.com/checkstyle/checkstyle/issues/4830">issue 4830</a>.
+        </p>
+      </subsection>
+      <subsection name="Properties">
+        <table>
+          <tr>
+            <th>name</th>
+            <th>description</th>
+            <th>type</th>
+            <th>default value</th>
+            <th>since</th>
+          </tr>
+          <tr>
+            <td>file</td>
+            <td>
+              the location of the <em>suppressions XML document</em> file.
+              The order the location is checked is:
+              <ol>
+                <li>as a filesystem location</li>
+                <li>
+                  if no file found, and the location starts with either
+                  <code>http://</code> or <code>https://</code>, then it
+                  is interpreted as a URL
+                </li>
+                <li>
+                  if no file found, then passed to the
+                  <code>ClassLoader.getResource()</code> method.
+                </li>
+              </ol>
+            </td>
+            <td><a href="property_types.html#string">string</a></td>
+            <td><code>none</code></td>
+            <td>8.6</td>
+          </tr>
+          <tr>
+            <td>optional</td>
+            <td>
+              Tells what to do when the file is not existing. If
+              optional is set to false the file must exist, or else
+              it ends with error. On the other hand if optional is
+              true and file is not found, the filter accept all
+              audit events.
+            </td>
+            <td><a href="property_types.html#boolean">Boolean</a></td>
+            <td><code>false</code></td>
+            <td>8.6</td>
+          </tr>
+        </table>
+      </subsection>
+      <subsection name="Examples">
+        <p>
+          For example, the following configuration fragment directs the
+          Checker to use a <code>SuppressionXpathFilter</code>
+          with suppressions
+          file <code>config/suppressions.xml</code>:
+        </p>
+        <source>
+&lt;module name=&quot;SuppressionXpathFilter&quot;&gt;
+  &lt;property name=&quot;file&quot; value=&quot;config/suppressions.xml&quot;/&gt;
+  &lt;property name=&quot;optional&quot; value=&quot;false&quot;/&gt;
+&lt;/module&gt;
+        </source>
+        <p>
+          A <a href="config.html#XML_Structure"><em>suppressions XML
+          document</em></a> contains a set
+          of <code>suppress</code> and <code>suppress-xpath</code> elements, where
+          each <code>suppress-xpath</code> element can have the
+          following attributes:
+        </p>
+        <ul>
+          <li>
+            <code>files</code> -
+            a <a href="property_types.html#regexp">Regular Expression</a>
+            matched against the file name associated with an audit
+            event. It is optional.
+          </li>
+          <li>
+            <code>checks</code> -
+            a <a href="property_types.html#regexp">Regular Expression</a>
+            matched against the name of the check associated with an audit
+            event. Optional as long as <code>id</code> or <code>message</code> is specified.
+          </li>
+          <li>
+            <code>message</code> -
+            a <a href="property_types.html#regexp">Regular Expression</a>
+            matched against the message of the check associated with an audit
+            event.  Optional as long as <code>checks</code> or <code>id</code> is specified.
+          </li>
+          <li>
+            <code>id</code> -
+            a <a href="property_types.html#string">string</a>
+            matched against the ID of the check associated with an audit
+            event.  Optional as long as <code>checks</code> or <code>message</code> is specified.
+          </li>
+          <li>
+            <code>query</code> -
+            a <a href="property_types.html#string">string</a>
+            xpath query. It is optional.
+          </li>
+        </ul>
+        <p>
+          Each audit event is checked against
+          each <code>suppress</code> and <code>suppress-xpath</code> element. It is
+          suppressed if all specified attributes match against the audit
+          event.
+        </p>
+        <p>
+          The following suppressions XML document directs
+          a <code>SuppressionXpathFilter</code> to
+          reject <code>CyclomaticComplexity</code> errors for
+          all methods with name <i>sayHelloWorld</i> inside <i>FileOne</i>
+          and <i>FileTwo</i> classes:
+        </p>
+        <source>
+&lt;?xml version=&quot;1.0&quot;?&gt;
+
+&lt;!DOCTYPE suppressions PUBLIC
+&quot;-//Puppy Crawl//DTD Suppressions Xpath Experimental 1.2//EN&quot;
+&quot;http://checkstyle.sourceforge.net/dtds/suppressions_1_2_xpath_experimental.dtd&quot;&gt;
+
+&lt;suppressions&gt;
+  &lt;suppress-xpath checks=&quot;CyclomaticComplexity&quot;
+  files=&quot;FileOne.java,FileTwo.java&quot;
+  query=&quot;//METHOD_DEF[@text='sayHelloWorld']&quot;/&gt;
+&lt;/suppressions&gt;
+        </source>
+        <p>
+          Suppress checks for package definitions:
+        </p>
+        <source>
+&lt;suppress-xpath checks=&quot;.*&quot; query=&quot;/PACKAGE_DEF&quot;/&gt;
+        </source>
+        <p>
+          Suppress checks for parent element of the first variable definition:
+        </p>
+        <source>
+&lt;suppress-xpath checks=&quot;.*&quot; query=&quot;(//VARIABLE_DEF)[1]/..&quot;/&gt;
+        </source>
+        <p>
+          Suppress checks for elements which are either class definitions,
+          either method definitions.
+        </p>
+        <source>
+&lt;suppress-xpath checks=&quot;.*&quot; query=&quot;//CLASS_DEF | //METHOD_DEF&quot;/&gt;
+        </source>
+        <p>
+          Suppress checks for certain methods:
+        </p>
+        <source>
+&lt;suppress-xpath checks=&quot;.*&quot; query=&quot;//METHOD_DEF[@text='getSomeVar'
+          or @text='setSomeVar']&quot;/&gt;
+        </source>
+        <p>
+          Suppress checks for variable <i>testVariable</i> inside <i>testMethod</i> method
+          inside <i>TestClass</i> class.
+        </p>
+        <source>
+&lt;suppress-xpath checks=&quot;.*&quot; query=&quot;/CLASS_DEF[@text='TestClass']
+          //METHOD_DEF[@text='testMethod']//VARIABLE_DEF[@text='testVariable']&quot;/&gt;
+        </source>
+      </subsection>
+      <subsection name="Example of Usage">
+        <ul>
+          <li>
+            <a href="https://github.com/search?q=path%3Aconfig+filename%3Acheckstyle_checks.xml+repo%3Acheckstyle%2Fcheckstyle+SuppressionXpathFilter">
+              Checkstyle Style</a>
+          </li>
+        </ul>
+      </subsection>
+      <subsection name="Package">
+        <p> com.puppycrawl.tools.checkstyle.filters </p>
+      </subsection>
+
+      <subsection name="Parent Module">
+        <p> <a href="config.html#TreeWalker">TreeWalker</a> </p>
+      </subsection>
+    </section>
+
     <section name="SuppressWarningsFilter">
       <subsection name="Description">
           <p>Since Checkstyle 5.7</p>