Repository: mojohaus/versions
Pull request #1093 (2024-06-20 21:02:13)

Add range type in ignoreVersion


Patch:
diff --git a/pom.xml b/pom.xml
index 5532b561..d2f9c04b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -189,6 +189,12 @@
         <version>1.4.1</version>
         <scope>provided</scope>
       </dependency>
+      <dependency>
+        <groupId>org.apache.maven.resolver</groupId>
+        <artifactId>maven-resolver-util</artifactId>
+        <version>1.4.1</version>
+      </dependency>
+
       <dependency>
         <groupId>org.eclipse.sisu</groupId>
         <artifactId>org.eclipse.sisu.plexus</artifactId>
diff --git a/versions-common/pom.xml b/versions-common/pom.xml
index 6ba27ef7..7a14c174 100644
--- a/versions-common/pom.xml
+++ b/versions-common/pom.xml
@@ -82,6 +82,12 @@
       <groupId>javax.inject</groupId>
       <artifactId>javax.inject</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>org.apache.maven.resolver</groupId>
+      <artifactId>maven-resolver-util</artifactId>
+      <scope>test</scope>
+    </dependency>
     <dependency>
       <groupId>org.apache.maven.plugin-testing</groupId>
       <artifactId>maven-plugin-testing-harness</artifactId>
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java
index 1abc65f2..382348eb 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java
@@ -108,10 +108,6 @@ import static org.apache.maven.RepositoryUtils.toArtifact;
 public class DefaultVersionsHelper implements VersionsHelper {
     private static final String CLASSPATH_PROTOCOL = "classpath";
 
-    private static final String TYPE_EXACT = "exact";
-
-    private static final String TYPE_REGEX = "regex";
-
     private static final int LOOKUP_PARALLEL_THREADS = 5;
 
     /**
@@ -258,8 +254,7 @@ public class DefaultVersionsHelper implements VersionsHelper {
         try {
             Collection<IgnoreVersion> ignoredVersions = getIgnoredVersions(artifact);
             if (!ignoredVersions.isEmpty() && getLog().isDebugEnabled()) {
-                getLog().debug("Found ignored versions: "
-                        + ignoredVersions.stream().map(IgnoreVersion::toString).collect(Collectors.joining(", ")));
+                getLog().debug("Found ignored versions: " + ignoredVersions + " for artifact" + artifact);
             }
 
             final List<RemoteRepository> repositories;
@@ -292,21 +287,7 @@ public class DefaultVersionsHelper implements VersionsHelper {
                             .getVersions()
                             .stream()
                             .filter(v -> ignoredVersions.stream().noneMatch(i -> {
-                                if (TYPE_REGEX.equals(i.getType())
-                                        && Pattern.compile(i.getVersion())
-                                                .matcher(v.toString())
-                                                .matches()) {
-                                    if (getLog().isDebugEnabled()) {
-                                        getLog().debug("Version " + v + " for artifact "
-                                                + ArtifactUtils.versionlessKey(artifact)
-                                                + " found on ignore list: "
-                                                + i);
-                                    }
-                                    return true;
-                                }
-
-                                if (TYPE_EXACT.equals(i.getType())
-                                        && i.getVersion().equals(v.toString())) {
+                                if (IgnoreVersionHelper.isVersionIgnored(v, i)) {
                                     if (getLog().isDebugEnabled()) {
                                         getLog().debug("Version " + v + " for artifact "
                                                 + ArtifactUtils.versionlessKey(artifact)
@@ -342,13 +323,12 @@ public class DefaultVersionsHelper implements VersionsHelper {
         final List<IgnoreVersion> ret = new ArrayList<>();
 
         for (final IgnoreVersion ignoreVersion : ruleSet.getIgnoreVersions()) {
-            if (!TYPE_EXACT.equals(ignoreVersion.getType()) && !TYPE_REGEX.equals(ignoreVersion.getType())) {
+            if (IgnoreVersionHelper.isValidType(ignoreVersion)) {
+                ret.add(ignoreVersion);
+            } else {
                 getLog().warn("The type attribute '" + ignoreVersion.getType() + "' for global ignoreVersion["
-                        + ignoreVersion + "] is not valid." + " Please use either '" + TYPE_EXACT + "' or '"
-                        + TYPE_REGEX
+                        + ignoreVersion + "] is not valid. Please use one of '" + IgnoreVersionHelper.VALID_TYPES
                         + "'.");
-            } else {
-                ret.add(ignoreVersion);
             }
         }
 
@@ -356,11 +336,11 @@ public class DefaultVersionsHelper implements VersionsHelper {
 
         if (rule != null) {
             for (IgnoreVersion ignoreVersion : rule.getIgnoreVersions()) {
-                if (!TYPE_EXACT.equals(ignoreVersion.getType()) && !TYPE_REGEX.equals(ignoreVersion.getType())) {
-                    getLog().warn("The type attribute '" + ignoreVersion.getType() + "' for " + rule + " is not valid."
-                            + " Please use either '" + TYPE_EXACT + "' or '" + TYPE_REGEX + "'.");
-                } else {
+                if (IgnoreVersionHelper.isValidType(ignoreVersion)) {
                     ret.add(ignoreVersion);
+                } else {
+                    getLog().warn("The type attribute '" + ignoreVersion.getType() + "' for " + rule + " is not valid."
+                            + " Please use one of '" + IgnoreVersionHelper.VALID_TYPES + "'.");
                 }
             }
         }
@@ -789,7 +769,7 @@ public class DefaultVersionsHelper implements VersionsHelper {
                     .addAll(ignoredVersions.stream()
                             .map(v -> {
                                 IgnoreVersion ignoreVersion = new IgnoreVersion();
-                                ignoreVersion.setType(TYPE_REGEX);
+                                ignoreVersion.setType(IgnoreVersion.TYPE_REGEX);
                                 ignoreVersion.setVersion(v);
                                 return ignoreVersion;
                             })
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/IgnoreVersionHelper.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/IgnoreVersionHelper.java
new file mode 100644
index 00000000..e06510b3
--- /dev/null
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/IgnoreVersionHelper.java
@@ -0,0 +1,95 @@
+package org.codehaus.mojo.versions.api;
+
+/*
+ * Copyright MojoHaus and Contributors
+ * Licensed 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
+ *
+ *    https://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.
+ */
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.regex.Pattern;
+
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.codehaus.mojo.versions.model.IgnoreVersion;
+import org.codehaus.mojo.versions.utils.DefaultArtifactVersionCache;
+import org.eclipse.aether.version.Version;
+
+/**
+ * Helper class for {@link IgnoreVersion}
+ */
+public class IgnoreVersionHelper {
+
+    static class IgnoreVersionException extends RuntimeException {
+        IgnoreVersionException(Throwable cause) {
+            super(cause);
+        }
+    }
+
+    public static final List<String> VALID_TYPES = Collections.unmodifiableList(
+            Arrays.asList(IgnoreVersion.TYPE_EXACT, IgnoreVersion.TYPE_REGEX, IgnoreVersion.TYPE_RANGE));
+
+    private static final Map<String, BiFunction<Version, IgnoreVersion, Boolean>> VERSION_MATCHERS;
+
+    static {
+        VERSION_MATCHERS = new HashMap<>();
+        VERSION_MATCHERS.put(IgnoreVersion.TYPE_EXACT, IgnoreVersionHelper::isVersionIgnoredExact);
+        VERSION_MATCHERS.put(IgnoreVersion.TYPE_REGEX, IgnoreVersionHelper::isVersionIgnoredRegex);
+        VERSION_MATCHERS.put(IgnoreVersion.TYPE_RANGE, IgnoreVersionHelper::isVersionIgnoredRange);
+    }
+
+    private IgnoreVersionHelper() {}
+
+    /**
+     * Check if type for given ignoredVersion is valid.
+     *
+     * @param ignoreVersion an ignored version to check
+     * @return true if type is valid
+     */
+    public static boolean isValidType(IgnoreVersion ignoreVersion) {
+        return VALID_TYPES.contains(ignoreVersion.getType());
+    }
+
+    public static boolean isVersionIgnored(Version version, IgnoreVersion ignoreVersion) {
+        return VERSION_MATCHERS.get(ignoreVersion.getType()).apply(version, ignoreVersion);
+    }
+
+    private static boolean isVersionIgnoredExact(Version version, IgnoreVersion ignoreVersion) {
+        return ignoreVersion.getVersion().equals(version.toString());
+    }
+
+    private static boolean isVersionIgnoredRegex(Version version, IgnoreVersion ignoreVersion) {
+        return Pattern.compile(ignoreVersion.getVersion())
+                .matcher(version.toString())
+                .matches();
+    }
+
+    private static boolean isVersionIgnoredRange(Version version, IgnoreVersion ignoreVersion) {
+        try {
+            ArtifactVersion aVersion = DefaultArtifactVersionCache.of(version.toString());
+            VersionRange versionRange = VersionRange.createFromVersionSpec(ignoreVersion.getVersion());
+            if (versionRange.hasRestrictions()) {
+                return versionRange.containsVersion(aVersion);
+            } else {
+                return versionRange.getRecommendedVersion().equals(aVersion);
+            }
+        } catch (InvalidVersionSpecificationException e) {
+            throw new IgnoreVersionException(e);
+        }
+    }
+}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/DefaultArtifactVersionCache.java b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/DefaultArtifactVersionCache.java
index 14d6cdff..1abfc1be 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/DefaultArtifactVersionCache.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/DefaultArtifactVersionCache.java
@@ -28,7 +28,7 @@ import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
  * Simple cache for {@link org.apache.maven.artifact.versioning.ArtifactVersion}
  */
 public class DefaultArtifactVersionCache {
-    private static final int MAX_CACHE_SIZE = 0x200;
+    private static final int MAX_CACHE_SIZE = 512;
     private static final Map<String, DefaultArtifactVersion> CACHE = new LRUMap<>(MAX_CACHE_SIZE);
     private static final ReentrantReadWriteLock CACHE_LOCK = new ReentrantReadWriteLock();
 
diff --git a/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
index e851c34d..4b26b64e 100644
--- a/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
+++ b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
@@ -240,7 +240,7 @@ public class MaxDependencyUpdates extends AbstractEnforcerRule {
      * <p>Allows specifying the {@linkplain RuleSet} object describing rules
      * on artifact versions to ignore when considering updates.</p>
      *
-     * @see <a href="https://www.mojohaus.org/versions-maven-plugin/version-rules.html#Using_the_ruleSet_element_in_the_POM">
+     * @see <a href="https://www.mojohaus.org/versions/versions-maven-plugin/version-rules.html#Using_the_ruleSet_element_in_the_POM">
      *     Using the ruleSet element in the POM</a>
      *
      * @since 2.14.0
diff --git a/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/pom.xml b/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/pom.xml
index b3023c50..658a7f40 100644
--- a/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/pom.xml
+++ b/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/pom.xml
@@ -12,7 +12,13 @@
       <artifactId>dummy-api</artifactId>
       <version>1.1</version>
     </dependency>
+    <dependency>
+      <groupId>localhost</groupId>
+      <artifactId>dummy-impl</artifactId>
+      <version>1.0</version>
+    </dependency>
   </dependencies>
+
   <build>
     <pluginManagement>
       <plugins>
@@ -27,6 +33,18 @@
                   <version>3.0</version>
                 </ignoreVersion>
               </ignoreVersions>
+              <rules>
+                <rule>
+                  <groupId>localhost</groupId>
+                  <artifactId>dummy-impl</artifactId>
+                  <ignoreVersions>
+                    <ignoreVersion>
+                      <type>range</type>
+                      <version>[2.0,)</version>
+                    </ignoreVersion>
+                  </ignoreVersions>
+                </rule>
+              </rules>
             </ruleSet>
           </configuration>
         </plugin>
diff --git a/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/verify.groovy b/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/verify.groovy
index a150dec3..db39fd6a 100644
--- a/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/verify.groovy
+++ b/versions-maven-plugin/src/it/it-display-dependency-updates-issue-684-pom-based-rules/verify.groovy
@@ -1,7 +1,9 @@
 output = new File(basedir, "output1.txt").text
 assert ! ( output =~ /\Qlocalhost:dummy-api\E\s*\.*\s*1\.1\s+->\s+3\.0\b/ )
 assert output =~ /\Qlocalhost:dummy-api\E\s*\.*\s*1\.1\s+->\s+2\.1\b/
+assert output =~ /\Qlocalhost:dummy-impl\E\s*\.*\s*1\.0\s+->\s+1\.4\b/
 
 output = new File(basedir, "output2.txt").text
 assert ! ( output =~ /\Qlocalhost:dummy-api\E\s*\.*\s*1\.1\s+->\s+2\.1\b/ )
 assert output =~ /\Qlocalhost:dummy-api\E\s*\.*\s*1\.1\s+->\s+2\.0\b/
+assert output =~ /\Qlocalhost:dummy-impl\E\s*\.*\s*1\.0\s+->\s+1\.4\b/
diff --git a/versions-maven-plugin/src/it/it-encoding-001/verify.bsh b/versions-maven-plugin/src/it/it-encoding-001/verify.bsh
deleted file mode 100644
index 1153e771..00000000
--- a/versions-maven-plugin/src/it/it-encoding-001/verify.bsh
+++ /dev/null
@@ -1,26 +0,0 @@
-import java.io.*;
-import org.codehaus.plexus.util.FileUtils;
-
-try
-{
-    File file = new File( basedir, "pom.xml" );
-    String buf = FileUtils.fileRead( file, "ISO-8859-15" );
-
-    if ( buf.indexOf( "<version>2.0</version>" ) < 0 )
-    { // if pom.xml has not been modified, the test is not useful...
-        System.err.println( "Version not changed to 2.0" );
-        return false;
-    }
-    if ( buf.indexOf( "<name>Modify a POM stored with ISO-8859-15 encoding (check euro symbol: \u20AC)</name>" ) < 0 )
-    {
-        System.err.println( "Encoding not preserved while modifying pom.xml" );
-        return false;
-    }
-}
-catch( Throwable t )
-{
-    t.printStackTrace();
-    return false;
-}
-
-return true;
diff --git a/versions-maven-plugin/src/it/it-encoding-001/verify.groovy b/versions-maven-plugin/src/it/it-encoding-001/verify.groovy
new file mode 100644
index 00000000..780d5d61
--- /dev/null
+++ b/versions-maven-plugin/src/it/it-encoding-001/verify.groovy
@@ -0,0 +1,7 @@
+def pom = new File(basedir, "pom.xml").getText("ISO-8859-15")
+
+// if pom.xml has not been modified, the test is not useful...
+assert pom.contains('<version>2.0</version>')
+
+assert pom.contains('<name>Modify a POM stored with ISO-8859-15 encoding (check euro symbol: \u20AC)</name>') :
+        'Encoding not preserved while modifying pom.xml'
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java
index 1029da13..dc85e3de 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java
@@ -126,7 +126,7 @@ public abstract class AbstractVersionsReport<T> extends AbstractMavenReport {
      * <p>Allows specifying the {@linkplain RuleSet} object describing rules
      * on artifact versions to ignore when considering updates.</p>
      *
-     * @see <a href="https://www.mojohaus.org/versions-maven-plugin/version-rules.html#Using_the_ruleSet_element_in_the_POM">
+     * @see <a href="https://www.mojohaus.org/versions/versions-maven-plugin/version-rules.html#Using_the_ruleSet_element_in_the_POM">
      *     Using the ruleSet element in the POM</a>
      *
      * @since 2.13.0
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java
index 5be9c69f..a3222a4c 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java
@@ -169,7 +169,7 @@ public abstract class AbstractVersionsUpdaterMojo extends AbstractMojo {
      * <p>Allows specifying the {@linkplain RuleSet} object describing rules
      * on artifact versions to ignore when considering updates.</p>
      *
-     * @see <a href="https://www.mojohaus.org/versions-maven-plugin/version-rules.html#Using_the_ruleSet_element_in_the_POM">
+     * @see <a href="https://www.mojohaus.org/versions/versions-maven-plugin/version-rules.html#Using_the_ruleSet_element_in_the_POM">
      *     Using the ruleSet element in the POM</a>
      *
      * @since 2.13.0
diff --git a/versions-maven-plugin/src/site/markdown/version-rules.md.vm b/versions-maven-plugin/src/site/markdown/version-rules.md.vm
index dcbb066d..d9007eca 100644
--- a/versions-maven-plugin/src/site/markdown/version-rules.md.vm
+++ b/versions-maven-plugin/src/site/markdown/version-rules.md.vm
@@ -55,14 +55,14 @@ The `versions-maven-plugin` knows three rules for comparing version numbers:
 The `versions-maven-plugin` will assume that all version numbers follow the `maven` scheme unless you tell
 it otherwise.
 
-Rules.xml
+rules.xml
 ---------
 
 To specify the version schemes to use, you may define a [rule-set xml file](../versions-model/rule.html), use the `ruleSet`
 element in the `versions-maven-plugin` plugin configuration, or specify ignored versions via
 the `maven.version.ignore` property.
 
-$h3 Using the `Rules.xml` file
+$h3 Using the `rules.xml` file
 
 The `rules.xml` file should conform to the xsd specified in the example below.
 
@@ -113,7 +113,7 @@ the `rules.xml` file:
                 <version>1.0.0</version>
               </ignoreVersion>
               <ignoreVersion>
-                <!-- can be either: 'exact' (default) or 'regex' -->
+                <!-- can be either: 'exact' (default), 'regex' or 'range' -->
                 <type>regex</type>
                 <version>(.+-SNAPSHOT|.+-M\d)</version>
               </ignoreVersion>
@@ -121,7 +121,11 @@ the `rules.xml` file:
                 <type>regex</type>
                 <version>.+-(alpha|beta)</version>
               </ignoreVersion>
-          </ignoreVersions>
+              <ignoreVersion>
+                <type>range</type>
+                <version>[2.0,)</version>
+              </ignoreVersion>
+            </ignoreVersions>
 
           <!-- zero or more elements -->
           <rules>
@@ -164,8 +168,8 @@ explained above. The described
 
 ```xml
 <ruleset comparisonMethod="maven"
-         xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0 https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd">
+         xmlns="https://www.mojohaus.org/VERSIONS/RULE/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="https://www.mojohaus.org/VERSIONS/RULE/2.1.0 https://www.mojohaus.org/versions/versions-model/xsd/rule-2.1.0.xsd">
   <ignoreVersions>
     <ignoreVersion type="regex">.*-beta</ignoreVersion>
   </ignoreVersions>
@@ -173,6 +177,7 @@ explained above. The described
     <rule groupId="com.mycompany.maven" comparisonMethod="maven">
       <ignoreVersions>
         <ignoreVersion type="regex">.*-RELEASE</ignoreVersion>
+        <ignoreVersion type="range">[2.0.0,)</ignoreVersion>
         <ignoreVersion>2.1.0</ignoreVersion>
       </ignoreVersions>
     </rule>
@@ -182,7 +187,7 @@ explained above. The described
 
 Note: it is possible to ignore versions using regular expressions.
 
-If you have your ruleset xml file hosted at, for example, http://www.mycompany.com/maven-version-rules.xml
+If you have your ruleset xml file hosted at, for example, `http://www.mycompany.com/maven-version-rules.xml`
 then the following configuration in your corporate pom would ensure that all projects use this rule set.
 
 ```xml
diff --git a/versions-model/src/main/mdo/rule.mdo b/versions-model/src/main/mdo/rule.mdo
index 0134a4b0..eccd9fc5 100644
--- a/versions-model/src/main/mdo/rule.mdo
+++ b/versions-model/src/main/mdo/rule.mdo
@@ -207,14 +207,21 @@ under the License.
           <name>type</name>
           <version>2.0+</version>
           <type>String</type>
-          <description>The type of ignore mechanism to use. Allowed values are 'exact' and 'regex'.</description>
+          <description>The type of ignore mechanism to use. Allowed values are 'exact', 'regex' or 'range'.</description>
           <defaultValue>exact</defaultValue>
         </field>
       </fields>
       <codeSegments>
         <codeSegment>
           <version>2.0+</version>
-          <code><![CDATA[public String toString()
+          <code>
+            <![CDATA[
+    public static final String TYPE_EXACT = "exact";
+    public static final String TYPE_REGEX = "regex";
+    public static final String TYPE_RANGE = "range";
+
+    @Override
+    public String toString()
     {
         StringBuilder buf = new StringBuilder( 128 );
         buf.append( version );
@@ -222,7 +229,9 @@ under the License.
         buf.append( type );
         buf.append( ")" );
         return buf.toString();
-    }]]></code>
+    }
+        ]]>
+          </code>
         </codeSegment>
       </codeSegments>
     </class>


Test patch:
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/api/IgnoreVersionHelperTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/api/IgnoreVersionHelperTest.java
new file mode 100644
index 00000000..d7f7dc26
--- /dev/null
+++ b/versions-common/src/test/java/org/codehaus/mojo/versions/api/IgnoreVersionHelperTest.java
@@ -0,0 +1,105 @@
+package org.codehaus.mojo.versions.api;
+
+/*
+ * Copyright MojoHaus and Contributors
+ * Licensed 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
+ *
+ *    https://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.
+ */
+
+import java.util.stream.Stream;
+
+import org.codehaus.mojo.versions.model.IgnoreVersion;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.eclipse.aether.version.VersionScheme;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.params.provider.Arguments.of;
+
+class IgnoreVersionHelperTest {
+
+    private static final VersionScheme VERSION_SCHEME = new GenericVersionScheme();
+
+    static Stream<Arguments> ignoredTypeExact() {
+        return Stream.of(of("1.0.0", "1.0.0", true), of("2.0.0", "1.0.0", false));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void ignoredTypeExact(String version, String ignore, boolean expected) throws Exception {
+
+        IgnoreVersion ignoreVersion = aIgnoreVersion(ignore, IgnoreVersion.TYPE_EXACT);
+
+        assertEquals(
+                expected, IgnoreVersionHelper.isVersionIgnored(VERSION_SCHEME.parseVersion(version), ignoreVersion));
+    }
+
+    public static Stream<Arguments> ignoredTypeRegex() {
+        return Stream.of(
+                of("1.0.0", "1\\.0\\..*", true),
+                of("1.0.0-SNAPSHOT", ".*-SNAPSHOT", true),
+                of("1.0.0", "2\\.0\\..*", false));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void ignoredTypeRegex(String version, String ignore, boolean expected) throws Exception {
+
+        IgnoreVersion ignoreVersion = aIgnoreVersion(ignore, IgnoreVersion.TYPE_REGEX);
+
+        assertEquals(
+                expected, IgnoreVersionHelper.isVersionIgnored(VERSION_SCHEME.parseVersion(version), ignoreVersion));
+    }
+
+    public static Stream<Arguments> ignoredTypeRange() {
+        return Stream.of(
+                of("1.0.0", "[1.0,)", true),
+                of("1.0.0", "(,2.0.0]", true),
+                of("2.2.0", "[1.0,3)", true),
+                of("2.2.0", "[1.0,2.0)", false),
+                of("2.2.0", "(,2.0.0]", false),
+                of("1.0.0", "1.0.0", true),
+                of("1.0.0", "1.0", true),
+                of("1.0.1", "1.0", false),
+                of("1.0.0", "2.0.0", false));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void ignoredTypeRange(String version, String ignore, boolean expected) throws Exception {
+
+        IgnoreVersion ignoreVersion = aIgnoreVersion(ignore, IgnoreVersion.TYPE_RANGE);
+
+        assertEquals(
+                expected, IgnoreVersionHelper.isVersionIgnored(VERSION_SCHEME.parseVersion(version), ignoreVersion));
+    }
+
+    @Test
+    void invalidRangeShouldThrowException() throws Exception {
+        IgnoreVersion ignoreVersion = aIgnoreVersion("[1,,", IgnoreVersion.TYPE_RANGE);
+
+        assertThrows(
+                IgnoreVersionHelper.IgnoreVersionException.class,
+                () -> IgnoreVersionHelper.isVersionIgnored(VERSION_SCHEME.parseVersion("1.0.0"), ignoreVersion));
+    }
+
+    private IgnoreVersion aIgnoreVersion(String version, String type) {
+        IgnoreVersion ignoreVersion = new IgnoreVersion();
+        ignoreVersion.setVersion(version);
+        ignoreVersion.setType(type);
+        return ignoreVersion;
+    }
+}