This is an automated email from the ASF dual-hosted git repository.

ppalaga pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-enforcer.git


The following commit(s) were added to refs/heads/master by this push:
     new 26d2ba8  [MENFORCER-507] Add xsltLocation parameter to ExternalRules
26d2ba8 is described below

commit 26d2ba89b0f43a3d4fbb7b52ec92888cfd80bc8a
Author: Peter Palaga <ppal...@redhat.com>
AuthorDate: Mon Jul 1 15:25:02 2024 +0200

    [MENFORCER-507] Add xsltLocation parameter to ExternalRules
---
 .../apache/maven/enforcer/rules/ExternalRules.java | 118 +++++++++++++++++++--
 enforcer-rules/src/site/apt/externalRules.apt.vm   |  60 +++++++++++
 .../maven/enforcer/rules/TestExternalRules.java    |  21 ++++
 .../resources/enforcer-rules/allow-findbugs.xsl    |  34 ++++++
 .../enforcer-rules/banned-dependencies.xml         |  31 ++++++
 5 files changed, 256 insertions(+), 8 deletions(-)

diff --git 
a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/ExternalRules.java
 
b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/ExternalRules.java
index 35fdb4a..52dd8f2 100644
--- 
a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/ExternalRules.java
+++ 
b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/ExternalRules.java
@@ -20,15 +20,26 @@ package org.apache.maven.enforcer.rules;
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.Objects;
 
 import org.apache.maven.enforcer.rule.api.AbstractEnforcerRuleConfigProvider;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleError;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
 import org.apache.maven.enforcer.rules.utils.ExpressionEvaluator;
 import org.apache.maven.plugin.MojoExecution;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
@@ -46,11 +57,75 @@ public final class ExternalRules extends 
AbstractEnforcerRuleConfigProvider {
     private static final String LOCATION_PREFIX_CLASSPATH = "classpath:";
 
     /**
-     * The external rules location. If it starts with "classpath:", the 
resource is read from the classpath.
+     * The external rules location. If it starts with <code>classpath:</code> 
the resource is read from the classpath.
      * Otherwise, it is handled as a filesystem path, either absolute, or 
relative to <code>${project.basedir}</code>
+     *
+     * @since 3.2.0
      */
     private String location;
 
+    /**
+     * An optional location of an XSLT file used to transform the rule 
document available via {@link #location} before
+     * it is applied. If it starts with <code>classpath:</code> the resource 
is read from the classpath.
+     * Otherwise, it is handled as a filesystem path, either absolute, or 
relative to <code>${project.basedir}</code>
+     * <p>
+     * This is useful, when you want to consume rules defined in an external 
project, but you need to
+     * remove or adapt some of those for the local circumstances.
+     * <p>
+     * <strong>Example</strong>
+     * <p>
+     * If <code>location</code> points at the following rule set:
+     *
+     * <pre>{@code
+     * <enforcer>
+     *   <rules>
+     *     <bannedDependencies>
+     *        <excludes>
+     *          <exclude>com.google.code.findbugs:jsr305</exclude>
+     *          <exclude>com.google.guava:listenablefuture</exclude>
+     *        </excludes>
+     *     </bannedDependencies>
+     *   </rules>
+     * </enforcer>
+     * }</pre>
+     *
+     * And if <code>xsltLocation</code> points at the following transformation
+     *
+     * <pre>{@code
+     * <xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
+     *   <xsl:output omit-xml-declaration="yes"/>
+     *
+     *   <!-- Copy everything unless there is a template with a more specific 
matcher -->
+     *   <xsl:template match="node()|@*">
+     *     <xsl:copy>
+     *       <xsl:apply-templates select="node()|@*"/>
+     *     </xsl:copy>
+     *   </xsl:template>
+     *
+     *   <!-- An empty template will effectively remove the matching nodes -->
+     *   <xsl:template match=
+     * "//bannedDependencies/excludes/exclude[contains(text(), 
'com.google.code.findbugs:jsr305')]"/>
+     * </xsl:stylesheet>
+     * }</pre>
+     *
+     * Then the effective rule set will look like to following:
+     *
+     * <pre>{@code
+     * <enforcer>
+     *   <rules>
+     *     <bannedDependencies>
+     *        <excludes>
+     *          <exclude>com.google.guava:listenablefuture</exclude>
+     *        </excludes>
+     *     </bannedDependencies>
+     *   </rules>
+     * </enforcer>
+     * }</pre>
+     *
+     * @since 3.6.0
+     */
+    private String xsltLocation;
+
     private final MojoExecution mojoExecution;
 
     private final ExpressionEvaluator evaluator;
@@ -65,10 +140,14 @@ public final class ExternalRules extends 
AbstractEnforcerRuleConfigProvider {
         this.location = location;
     }
 
+    public void setXsltLocation(String xsltLocation) {
+        this.xsltLocation = xsltLocation;
+    }
+
     @Override
     public Xpp3Dom getRulesConfig() throws EnforcerRuleError {
 
-        try (InputStream descriptorStream = resolveDescriptor()) {
+        try (InputStream descriptorStream = transform(location, 
resolveDescriptor(location), xsltLocation)) {
             Xpp3Dom enforcerRules = Xpp3DomBuilder.build(descriptorStream, 
"UTF-8");
             if (enforcerRules.getChildCount() == 1 && 
"enforcer".equals(enforcerRules.getName())) {
                 return enforcerRules.getChild(0);
@@ -80,11 +159,11 @@ public final class ExternalRules extends 
AbstractEnforcerRuleConfigProvider {
         }
     }
 
-    private InputStream resolveDescriptor() throws EnforcerRuleError {
+    private InputStream resolveDescriptor(String path) throws 
EnforcerRuleError {
         InputStream descriptorStream;
-        if (location != null) {
-            if (location.startsWith(LOCATION_PREFIX_CLASSPATH)) {
-                String classpathLocation = 
location.substring(LOCATION_PREFIX_CLASSPATH.length());
+        if (path != null) {
+            if (path.startsWith(LOCATION_PREFIX_CLASSPATH)) {
+                String classpathLocation = 
path.substring(LOCATION_PREFIX_CLASSPATH.length());
                 getLog().debug("Read rules form classpath location: " + 
classpathLocation);
                 ClassLoader classRealm = 
mojoExecution.getMojoDescriptor().getRealm();
                 descriptorStream = 
classRealm.getResourceAsStream(classpathLocation);
@@ -92,7 +171,7 @@ public final class ExternalRules extends 
AbstractEnforcerRuleConfigProvider {
                     throw new EnforcerRuleError("Location '" + 
classpathLocation + "' not found in classpath");
                 }
             } else {
-                File descriptorFile = evaluator.alignToBaseDirectory(new 
File(location));
+                File descriptorFile = evaluator.alignToBaseDirectory(new 
File(path));
                 getLog().debug("Read rules form file location: " + 
descriptorFile);
                 try {
                     descriptorStream = 
Files.newInputStream(descriptorFile.toPath());
@@ -108,6 +187,29 @@ public final class ExternalRules extends 
AbstractEnforcerRuleConfigProvider {
 
     @Override
     public String toString() {
-        return String.format("ExternalRules[location=%s]", location);
+        return String.format("ExternalRules[location=%s, xsltLocation=%s]", 
location, xsltLocation);
+    }
+
+    InputStream transform(String sourceLocation, InputStream sourceXml, String 
xsltLocation) {
+        if (xsltLocation == null || xsltLocation.trim().isEmpty()) {
+            return sourceXml;
+        }
+
+        try (InputStream in = resolveDescriptor(xsltLocation);
+                ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            Transformer transformer = 
TransformerFactory.newInstance().newTransformer(new StreamSource(in));
+            transformer.transform(new StreamSource(sourceXml), new 
StreamResult(baos));
+            final byte[] bytes = baos.toByteArray();
+            getLog().info(() -> (CharSequence) ("Rules transformed by " + 
xsltLocation + " from " + location + ":\n\n"
+                    + new String(bytes, StandardCharsets.UTF_8)));
+            return new ByteArrayInputStream(bytes);
+        } catch (IOException
+                | EnforcerRuleException
+                | TransformerConfigurationException
+                | TransformerFactoryConfigurationError e) {
+            throw new RuntimeException("Could not open resource " + 
xsltLocation);
+        } catch (TransformerException e) {
+            throw new RuntimeException("Could not transform " + sourceLocation 
+ " usinng XSLT " + xsltLocation);
+        }
     }
 }
diff --git a/enforcer-rules/src/site/apt/externalRules.apt.vm 
b/enforcer-rules/src/site/apt/externalRules.apt.vm
index fba231c..7fc1c95 100644
--- a/enforcer-rules/src/site/apt/externalRules.apt.vm
+++ b/enforcer-rules/src/site/apt/externalRules.apt.vm
@@ -99,3 +99,63 @@ The External Enforcer Rule Descriptor
     </rules>
 </enforcer>
 +---+
+
+<<<xsltLocation>>>: Rule Descriptor Transformation (since Enforcer 3.6.0)
+
+    In addition to <<<location>>> parameter, <<<externalRules>>> accepts 
another optional parameter <<<xsltLocation>>>.
+
+    It is useful, when you want to consume rules defined in an external 
project, but you need to
+    remove or adapt some of those for the local circumstances.
+
+    <<<xsltLocation>>> specifies the location of an XSLT file used to 
transform the rule document available via
+    <<<location>>> before it is applied. If it starts with <<<classpath:>>> 
the resource is read from the classpath.
+    Otherwise, it is handled as a filesystem path, either absolute, or 
relative to <<<$\{project.basedir\}>>>.
+
+    Here is an example: If <<<location>>> points at the following rule set:
+
++---+
+<enforcer>
+  <rules>
+    <bannedDependencies>
+       <excludes>
+         <exclude>com.google.code.findbugs:jsr305</exclude>
+         <exclude>com.google.guava:listenablefuture</exclude>
+       </excludes>
+    </bannedDependencies>
+  </rules>
+</enforcer>
++---+
+
+    and if <<<xsltLocation>>> points at the following transformation
+
++---+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
+  <xsl:output omit-xml-declaration="yes"/>
+
+  <!-- Copy everything unless there is a template with a more specific matcher 
-->
+  <xsl:template match="node()|@*">
+    <xsl:copy>
+      <xsl:apply-templates select="node()|@*"/>
+    </xsl:copy>
+  </xsl:template>
+
+  <!-- An empty template will effectively remove the matching nodes -->
+  <xsl:template match=
+"//bannedDependencies/excludes/exclude[contains(text(), 
'com.google.code.findbugs:jsr305')]"/>
+</xsl:stylesheet>
++---+
+
+    Then the effective rule set will look like to following:
+
++---+
+<enforcer>
+  <rules>
+    <bannedDependencies>
+       <excludes>
+         <exclude>com.google.guava:listenablefuture</exclude>
+       </excludes>
+    </bannedDependencies>
+  </rules>
+</enforcer>
++---+
+
diff --git 
a/enforcer-rules/src/test/java/org/apache/maven/enforcer/rules/TestExternalRules.java
 
b/enforcer-rules/src/test/java/org/apache/maven/enforcer/rules/TestExternalRules.java
index 78af21b..53a27cd 100644
--- 
a/enforcer-rules/src/test/java/org/apache/maven/enforcer/rules/TestExternalRules.java
+++ 
b/enforcer-rules/src/test/java/org/apache/maven/enforcer/rules/TestExternalRules.java
@@ -98,4 +98,25 @@ class TestExternalRules {
         assertNotNull(rulesConfig);
         assertEquals(2, rulesConfig.getChildCount());
     }
+
+    @Test
+    void shouldFilterRules() throws EnforcerRuleException {
+        MojoDescriptor mojoDescriptor = new MojoDescriptor();
+        mojoDescriptor.setRealm(EnforcerTestUtils.getTestClassRealm());
+        when(mojoExecution.getMojoDescriptor()).thenReturn(mojoDescriptor);
+        rule.setLocation("classpath:enforcer-rules/banned-dependencies.xml");
+        rule.setXsltLocation("classpath:enforcer-rules/allow-findbugs.xsl");
+
+        Xpp3Dom rulesConfig = rule.getRulesConfig();
+        assertNotNull(rulesConfig);
+        assertEquals(1, rulesConfig.getChildCount());
+        assertEquals("bannedDependencies", rulesConfig.getChild(0).getName());
+        assertEquals(1, rulesConfig.getChild(0).getChildCount());
+        assertEquals("excludes", 
rulesConfig.getChild(0).getChild(0).getName());
+        assertEquals(1, rulesConfig.getChild(0).getChild(0).getChildCount());
+        assertEquals("exclude", 
rulesConfig.getChild(0).getChild(0).getChild(0).getName());
+        assertEquals(
+                "com.google.guava:listenablefuture",
+                rulesConfig.getChild(0).getChild(0).getChild(0).getValue());
+    }
 }
diff --git 
a/enforcer-rules/src/test/resources/enforcer-rules/allow-findbugs.xsl 
b/enforcer-rules/src/test/resources/enforcer-rules/allow-findbugs.xsl
new file mode 100644
index 0000000..af7192f
--- /dev/null
+++ b/enforcer-rules/src/test/resources/enforcer-rules/allow-findbugs.xsl
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
+
+    <xsl:output omit-xml-declaration="yes"/>
+
+    <xsl:template match="node()|@*">
+        <xsl:copy>
+            <xsl:apply-templates select="node()|@*"/>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- An empty template will effectively remove the maching nodes -->
+    <xsl:template 
match="//bannedDependencies/excludes/exclude[contains(text(), 
'com.google.code.findbugs:jsr305')]"/>
+</xsl:stylesheet>
diff --git 
a/enforcer-rules/src/test/resources/enforcer-rules/banned-dependencies.xml 
b/enforcer-rules/src/test/resources/enforcer-rules/banned-dependencies.xml
new file mode 100644
index 0000000..ee5efd6
--- /dev/null
+++ b/enforcer-rules/src/test/resources/enforcer-rules/banned-dependencies.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+<enforcer>
+    <rules>
+        <bannedDependencies>
+            <excludes>
+                <exclude>com.google.code.findbugs:jsr305</exclude>
+                <exclude>com.google.guava:listenablefuture</exclude>
+            </excludes>
+        </bannedDependencies>
+    </rules>
+</enforcer>
\ No newline at end of file

Reply via email to