Giter Club home page Giter Club logo

restrict-imports-enforcer-rule's Introduction

Maven Central Gradle Plugin Portal Coverage Status Twitter Follow

restrict-imports-enforcer-rule

Keep your code base clean and free from usage of unwanted classes! More

NEW in 2.5.0: We now also provide a Gradle plugin!

Supported source files:

  • Java
  • Kotlin (since 0.15)
  • Groovy (since 0.15)

Compatibility:

  • Works with Java 8+
  • Tested against maven-enforcer-plugin versions 1.4.1 and 3.4.1.

Maven quick start

This is a minimal usage example. Please scroll down for detailed configuration information or have a look at the Full configuration example.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-enforcer-plugin</artifactId>
    <version>3.4.1</version>
    <dependencies>
        <dependency>
            <groupId>de.skuzzle.enforcer</groupId>
            <artifactId>restrict-imports-enforcer-rule</artifactId>
            <version>2.5.0</version>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <id>check-logging-imports</id> <!-- put an explanatory ID here -->
            <phase>process-sources</phase>
            <goals>
                <goal>enforce</goal>
            </goals>
            <configuration>
                <rules>
                    <RestrictImports>
                        <!-- Define an explanatory reason why these imports are prohibited -->
                        <reason>Use SLF4j for logging</reason>
                        <!-- Specify a single pattern to be banned -->
                        <bannedImport>java.util.logging.**</bannedImport>
                    </RestrictImports>

                    <!-- You could have another rule instance here for restricting further imports -->
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

Gradle quick start

Caution

Gradle support is quite new and should be considered experimental.

Feedback is welcome and should be filed as new GitHub issue.

... with Groovy DSL

plugins {
    id("de.skuzzle.restrictimports") version("2.5.0")
}

restrictImports {
    reason = "Use slf4j for logging"
    bannedImports = ["java.util.logging.**"]
}

... with Kotlin DSL

plugins {
    id("de.skuzzle.restrictimports") version("2.5.0")
}

restrictImports {
    reason.set("Use slf4j for logging")
    bannedImports.set(listOf("java.util.logging.**"))
}

Contents

Rationale

Grown code bases often have a huge number of dependencies. That leads to a lot of clutter in their compile time classpath. My favorite example here is logging frameworks: every java project of decent size likely has numerous classes named Logger available on the classpath and your favorite IDE happily lists them all for auto completion. How should someone new to the project know which Logger to use? You certainly do not want to mix logging frameworks in your code base.

Another example is to force your developers to only use AssertJ assertions instead of JUnit or TestNG assertions.

Using this enforcer rule gives you fine grained control over which classes are allowed to be used in your application without having to exclude whole artifacts from your classpath.

Includes and Excludes

To refine the classes that are banned you may use the allowedImports tag in addition to the bannedImports tag. For example, you can exclude a whole sub package using a wildcard operator but still allow some concrete classes:

Maven
<configuration>
    <rules>
        <RestrictImports>
            <bannedImport>java.util.logging.**</bannedImport>
            <allowedImport>java.util.logging.Handler</allowedImport>
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    bannedImports = listOf("java.util.logging.**")
    allowedImports = listOf("java.util.logging.Handler")
}
Gradle (Groovy)
restrictImports {
    bannedImports = ["java.util.logging.**"]
    allowedImports = ["java.util.logging.Handler"]
}

It is possible to exclude certain source files from being affected by the bans at all. You can use basePackage to specify a package pattern of classes that are affected by the rule. You may then exclude some classes to refine the matches using the exclusion tag. It is also possible to specify multiple base packages.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <basePackages>
                <basePackage>com.your.domain.**</basePackage>
                <basePackage>com.your.company.**</basePackage>
            </basePackages>
            <bannedImport>java.util.logging.**</bannedImport>
            <allowedImport>java.util.logging.Handler</allowedImport>
            <!-- The following packages will not be checked for banned imports -->
            <exclusion>com.your.domain.treat.special.*</exclusion>
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    basePackes = listOf("com.your.domain.**", "com.your.company.**")
    bannedImports = listOf("java.util.logging.**")
    allowedImports = listOf("java.util.logging.Handler")
    exclusions = listOf("com.your.domain.treat.special.*")
}
Gradle (Groovy)
restrictImports {
    basePackes = ["com.your.domain.**", "com.your.company.**"]
    bannedImports = ["java.util.logging.**"]
    allowedImports = ["java.util.logging.Handler"]
    exclusions = ["com.your.domain.treat.special.*"]
}

Wherever you write package patterns you can also specify a list of patterns. Thus it is possible to define multiple banned imports/exclusions/allowed imports or base packages.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <bannedImports>
                <bannedImport>java.util.logging.**</bannedImport>
                <bannedImport>what.ever.**</bannedImport>
            </bannedImports>
            <allowedImports>
                <allowedImport>java.util.logging.Handler</allowedImport>
                <allowedImport>what.ever.IsCool</allowedImport>
            </allowedImports>
            <exclusions>
                <exclusion>com.your.domain.treat.special.*</exclusion>
                <exclusion>com.your.domain.treat.special.too.*</exclusion>
            </exclusions>
            <!-- ... -->
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    bannedImports = listOf("java.util.logging.**", "what.ever.**")
    allowedImports = listOf("java.util.logging.Handler", "what.ever.IsCool")
    exclusions = listOf("com.your.domain.treat.special.*", "com.your.domain.treat.special.too.*")
}
Gradle (Groovy)
restrictImports {
    bannedImports = ["java.util.logging.**", "what.ever.**"]
    allowedImports = ["java.util.logging.Handler", "what.ever.IsCool"]
    exclusions = ["com.your.domain.treat.special.*", "com.your.domain.treat.special.too.*"]
}

Not-fixable imports

Note

This is an experimental feature added in 2.4.0

In certain situations you might not be able to avoid using a banned import. For example if you implement an interface which requires a banned type as either return- or parameter type. Instead of globally allowing such imports, you can allow them to be used only in some explicitly configured locations.

Maven

You can add multiple not-fixable definitions if you nest them in <notFixables></notFixables>.

<configuration>
    <rules>
        <RestrictImports>
            <bannedImport>com.foo.BannedClass</bannedImport>
            <notFixable>
                <in>com.yourdomain.persistence.SomeClass</in>
                <allowedImports>
                    <allowedImport>com.foo.BannedClass</allowedImport>
                </allowedImports>
                <because>Type required by implemented interface</because>
            </notFixable>
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    bannedImports = listOf("com.foo.BannedClass")
    notFixable {
        in = "com.yourdomain.persistence.SomeClass"
        allowedImports = listOf("com.foo.BannedClass")
        because = "Type required by implemented interface"
    }
}
Gradle (Groovy)
restrictImports {
    bannedImports = ["com.foo.BannedClass"]
    notFixable {
        in = "com.yourdomain.persistence.SomeClass"
        allowedImports = ["com.foo.BannedClass"]
        because = "Type required by implemented interface"
    }
}

Note

Not fixable definitions can not be nested in <groups> (see Rule groups below). Not-fixables apply globally per RestrictImports rule instance.

Rule groups

Rule groups add another level of refining which imports will be matched. You can group the bannedImport(s), allowedImport(s) and basePackage(s) tags and specify multiple of this groups within a single enforcer rule.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <groups>
                <group>
                    <reason>Persistence classes must only be used from within .persistence package</reason>
                    <basePackage>**</basePackage>
                    <bannedImports>
                        <bannedImport>javax.persistence.EntityManager</bannedImport>
                        <bannedImport>javax.sql.DataSource</bannedImport>
                        <bannedImport>javax.persistence.NamedQueries</bannedImport>
                        <bannedImport>javax.persistence.NamedQuery</bannedImport>
                        <bannedImport>javax.ejb.Stateful</bannedImport>
                        <bannedImport>javax.ejb.EJB</bannedImport>
                    </bannedImports>
                </group>
                <group>
                    <basePackage>com.yourdomain.persistence.**</basePackage>
                    <bannedImports>
                        <bannedImport>javax.persistence.NamedQueries</bannedImport>
                        <bannedImport>javax.persistence.NamedQuery</bannedImport>
                        <bannedImport>javax.ejb.Stateful</bannedImport>
                        <bannedImport>javax.ejb.EJB</bannedImport>
                    </bannedImports>
                </group>
            </groups>
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    group {
        reason = "Persistence classes must only be used from within .persistence package"
        basePackages = listOf("**")
        bannedImports = listOf(
          "javax.persistence.EntityManager",
          "javax.sql.DataSource",
          "javax.persistence.NamedQueries",
          "javax.persistence.NamedQuery",
          "javax.ejb.Stateful",
          "javax.ejb.EJB"
        )
    }
    group {
        basePackages = listOf("com.yourdomain.persistence.**")
        bannedImports = listOf(
            "javax.persistence.NamedQueries",
            "javax.persistence.NamedQuery",
            "javax.ejb.Stateful",
            "javax.ejb.EJB"
        )
    }
}
Gradle (Groovy)
restrictImports {
    group {
        reason = "Persistence classes must only be used from within .persistence package"
        basePackages = ["**"]
        bannedImports = [
          "javax.persistence.EntityManager",
          "javax.sql.DataSource",
          "javax.persistence.NamedQueries",
          "javax.persistence.NamedQuery",
          "javax.ejb.Stateful",
          "javax.ejb.EJB"
        ]
    }
    group {
        basePackages = ["com.yourdomain.persistence.**"]
        bannedImports = [
            "javax.persistence.NamedQueries",
            "javax.persistence.NamedQuery",
            "javax.ejb.Stateful",
            "javax.ejb.EJB"
        ]
    }
}

When analysing a source file, the plugin collects all groups where the group's basePackage matches the source file's package name. In case multiple groups are matching, only the group with the most specific base package is retained and the others are ignored for this file. Have a look at this file to have a glance at how specificity works.

In the above example, the first group is chosen by default (as by basePackage=**) unless a class is matched by the more specific basePackage of the second group. In that case, only the definitions from the second group apply to this class.

Static imports

Note

Behavior of static import detection has been changed with version 2.0.0

Every package pattern also automatically matches static imports. However, it is possible to explicitly mention the static keyword in the pattern. In that case, the pattern will only match a resp. static import.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <bannedImport>static org.junit.Assert.*</bannedImport>
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    bannedImports = listOf("static org.junit.Assert.*")
}
Gradle (Groovy)
restrictImports {
    bannedImports = ["static org.junit.Assert.*"]
}

Inclusions and exclusion will work identically.

Test code

By default, test code is also subject to the banned import checks (this is new since version 2.0.0). You can disable analysis of test code using the includeTestCode option.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <includeTestCode>false</includeTestCode>
            <!-- ... -->
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    includeTestCode = false
}
Gradle (Groovy)
restrictImports {
    includeTestCode = false
}

Skipping

Using the configuration option skip you are able to temporarily disable a rule instance.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <skip>true</skip>
            <!-- ... -->
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)

No direct equivalent

Gradle (Groovy)

No direct equivalent

If you want banned import analysis but without breaking your build you can set failBuild to false.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <failBuild>false</failBuild>
            <!-- ... -->
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    failBuild = false
}
Gradle (Groovy)
restrictImports {
    failBuild = false
}

You can also pass these parameters as property to the maven build using -Drestrictimports.skip resp. -Drestrictimports.failBuild. When passed as property, the property's value takes precedence over what has been configured in the pom file.

Exclude source roots

By default, all source roots reported by Maven/Gradle are subject to the banned import checks, which for example includes but is not limited to ${project.basedir}/src/main/java, ${project.basedir}/src/test/java, ${project.build.directory}/generated-sources/main/java and ${project.build.directory}/generated-test-sources/main/java. You can exclude source root(s) using the excludedSourceRoot(s) option, either absolute or relative path.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <excludedSourceRoots>
                <excludedSourceRoot>${project.build.directory}/generated-sources/main/java</excludedSourceRoot>
                <excludedSourceRoot>target/generated-test-sources/main/java</excludedSourceRoot>
            </excludedSourceRoots>
            <!-- ... -->
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)

No direct equivalent

Gradle (Groovy)

No direct equivalent

Parallel Analysis

We support basic parallelization of the analysis. This is enabled by default but can be disabled either in the pom file using the <parallel> option or by passing -Drestrictimports.parallel to the maven build.

Maven
<configuration>
    <rules>
        <RestrictImports>
            <parallel>false</parallel>
            <!-- ... -->
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
    parallel = false
}
Gradle (Groovy)
restrictImports {
    parallel = false
}

Detecting full qualified class usage

To overcome some of the limitations mentioned here, you can enable 'full compilation unit' parsing mode using

Maven
<configuration>
    <rules>
        <RestrictImports>
            <parseFullCompilationUnit>true</parseFullCompilationUnit>
            <!-- ... -->
        </RestrictImports>
    </rules>
</configuration>
Gradle (Kotlin)
restrictImports {
  parseFullCompilationUnit = true
}
Gradle (Groovy)
restrictImports {
    parseFullCompilationUnit = true
}

The option currently only affects parsing of java source files. When enabled, we will attempt a full parse of each java source file, creating an actual AST. This allows to also detect full qualified class usages but will be considerably slower.

Warning

In case a source file cannot be properly parsed, we try to fall back to our native line-by-line parsing approach described here. A respective warning will be issued in the report that is generated at the end.

This is especially the case when using Java language features introduced with version 16 or higher. See #60 for details.

Package Patterns

Package patterns are dot separated strings that can be compared case sensitively part by part. Every part must adhere to the java identifier rules with the exception of a some special literals:

  1. * matches every package part but exactly one.
  2. ** matches multiple package parts but at least one.
  3. '*' matches a literal * in an import statement.

The pattern java.util.* matches java.util.ArrayList but not java.util.regex.Pattern.

Likewise the pattern java.util.** matches all classes and subclasses contained in java.util. Double wildcards are supported everywhere within a pattern. **.DumbName would match every import which ends in DumbName. Wildcards are forbidden to be used in combination with other characters within a single part, like in com.foo**. Also parts within a package must not be empty like in foo..bar.

If a pattern does not contain any wildcards, matching degrades to a simple String comparison.

Limitation

Syntactical limitation

This rule implementation assumes that every analyzed java source file is syntactically correct. If a source file is not, the analysis result is undefined. We don't use a formal parser to parse the whole source file into an abstract syntax tree. Instead, import statements are extracted by relying on more or less simple String split operations and only reading each source file up until a non-import statement (like class declaration) is discovered. We cover a set of esoteric edge cases, for example block comments within a single import statement and the like.

!NOTE] Plus side to this approach is, that we are mostly agnostic to the Java version you are using. Our parser doesn't need updates even if you want to use latest Java language features in your code base.

Conceptual limitation

Import recognition works by comparing the import statements within your source files against the specified patterns. If your class uses wildcard imports like in

import java.util.*;

this plugin will not be able to match that import against a banned pattern pointing to a concrete class like java.util.ArrayList. However, wildcard recognition would still work as expected.

Also, it is not possible to detect full qualified class usages, where a type is used without an import statement.

For checking the basePackage and exclusion patterns, the plugin tries to construct the full qualified class name (FQCN) of each analyzed source file. It does so by concatenating the file name to the source file's value of the package <value>; statement. Thus if your exclusion pattern points to a concrete class like com.name.ClassName the exclusion will only match if this class is declared in a file with the exact name ClassName.java. The same applies in case you use a base package pattern with no wild cards.

Full configuration example

Maven
<RestrictImports>
    <failBuild>true</failBuild> <!-- Can be overridden with -Drestrictimports.failBuild=... -->
    <skip>false</skip> <!-- Can be overridden with -Drestrictimports.skip=... -->
    <parseFullCompilationUnit>false</parseFullCompilationUnit>
    <parallel>true</parallel> <!-- Can be overridden with -Drestrictimports.parallel=... -->
    <excludedSourceRoots> <!-- Optional. Nesting not needed when specifying a excluded root -->
        <excludedSourceRoot>${project.build.directory}/generated-sources/main/java</excludedSourceRoot>
    </excludedSourceRoots>
    <groups>
        <group> <!-- Optional. groups and group can be left out in simple configurations -->
            <reason>...</reason>
            <basePackages> <!-- Optional. Nesting not needed when specifying a single package -->
                <basePackage>**</basePackage>
            </basePackages>
            <bannedImports> <!-- Optional. Nesting not needed when specifying a single package -->
                <bannedImport>..</bannedImport>
            </bannedImports>
            <allowedImports> <!-- Optional. Nesting not needed when specifying a single package -->
                <allowedImport>...</allowedImport>
            </allowedImports>
            <exclusions> <!-- Optional. Nesting not needed when specifying a single package -->
                <exclusion>...</exclusion>
            </exclusions>
        </group>
    </groups>
    <notFixables> <!-- Optional. Nesting not needed when specifying a single not-fixable -->
        <notFixable>
            <in>**</in>
            <allowedImports> <!-- Optional. Nesting not needed when specifying a single package -->
                <allowedImport>..</allowedImport>
            </allowedImports>
        </notFixable>
    </notFixables>
</RestrictImports>
Gradle (Kotlin)
restrictImports {
    reason = "..."
    bannedImports = listOf("...")
    allowedImports = listOf("...")
    exclusions = listOf("...")
    parallel = false
    includeCompileCode = false
    includeTestCode = false
    parseFullCompilationUnit = false
}
Gradle (Groovy)
restrictImports {
    reason = "..."
    bannedImports = ["..."]
    allowedImports = ["..."]
    exclusions = ["..."]
    parallel = false
    includeCompileCode = false
    includeTestCode = false
    parseFullCompilationUnit = false
}

restrict-imports-enforcer-rule's People

Contributors

connorlanigan avatar dependabot[bot] avatar fc-staddiken avatar honnix avatar simy4 avatar skuzzle avatar yeikel avatar zeldigas avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

restrict-imports-enforcer-rule's Issues

Allow multiple base patterns

(Please see later comment for actual implementation!)

Allow multiple base patterns either like this:

<basePatterns>
  <basePattern>com.foo.**</basePattern>
  <basePattern>com.bar.**</basePattern>
</basePatterns>

or this

<basePattern>com.foo.**, com.bar.**</basePattern>

Suggestion 1 is more consistent to the other configuration options while suggestion 2 is less verbose. Not sure what to favor.
Maybe even both ways might be allowed in every configurable entity which takes package patterns?

Not possible to use multiple rules?

I want to ban DataSource and EntityManager imports in all modules except in one module but the build fails for module1 with the below configuration.

It would also be handy to specify multiple basePackage patterns separated with comma characters!

<configuration>
  <rules>
    <restrictImports implementation="de.skuzzle.enforcer.restrictimports.RestrictImports">
      <basePackage>**</basePackage>
      <bannedImports>
        <bannedImport>javax.persistence.EntityManager</bannedImport>
        <bannedImport>javax.sql.DataSource</bannedImport>
        <bannedImport>javax.persistence.NamedQueries</bannedImport>
        <bannedImport>javax.persistence.NamedQuery</bannedImport>
        <bannedImport>javax.ejb.Stateful</bannedImport>
        <bannedImport>javax.ejb.EJB</bannedImport>
      </bannedImports>
    </restrictImports>
    <restrictImports implementation="de.skuzzle.enforcer.restrictimports.RestrictImports">
      <basePackage>se.company.module1.**</basePackage>
      <allowedImports>
        <allowedImport>javax.persistence.EntityManager</allowedImport>
        <allowedImport>javax.sql.DataSource</allowedImport>
      </allowedImports>
      <bannedImports>
        <bannedImport>javax.persistence.NamedQueries</bannedImport>
        <bannedImport>javax.persistence.NamedQuery</bannedImport>
        <bannedImport>javax.ejb.Stateful</bannedImport>
        <bannedImport>javax.ejb.EJB</bannedImport>
      </bannedImports>
    </restrictImports>
  </rules>
</configuration>

Sanity check: no base package should match any other base package

For example, the following should be forbidden:

<basePackages>
    <basePackage>java.util.**</basePackage>
    <basePackage>java.util.text.**</basePackage>
</basePackages>

The same applies to <exclusions> and <bannedImports> and ``<allowedImports> where it is equally useless to have intersecting matches

Review specifictiy calculation for package patterns

Calculation quite arbitrarily counts numbers of wildcards. I feel that also the wildcard's position should be taken into account as well. For example com.foo.bar.**.abc.de feels more specific than com.foo.**.bar.abc.de.
In general, if a pattern is a prefix of another, the pattern where the first wildcard occurs later (farther right) is more specific than the other

rule failed with NoSuchMethodError

After updating to 0.12.0 enforcer plugin starts failing with:

Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:3.0.0-M2:enforce (enforce-restrict-imports) on project id-hydra-transformer: Execution enforce-restrict-imports of goal org.apache.maven.plugins:maven-enforcer-plugin:3.0.0-M2:enforce failed: An API incompatibility was encountered while executing org.apache.maven.plugins:maven-enforcer-plugin:3.0.0-M2:enforce: java.lang.NoSuchMethodError: com.google.common.collect.ImmutableList.copyOf(Ljava/util/Collection;)Lcom/google/common/collect/ImmutableList;

I'm using latest enforer plugin version and latest maven version.

UPD: rolled back to 0.10.0 - the last version without this error.

UPD2: turn out the problem here lies in the fact that maven enforcer plugin shares single classloader between all enforcer rules. and guava historically has an issue of conflicting class definitions with google-collections dependency. The problem can be reproduced if you have another rule with uses google collections internally.

UPD3: google-collections is a dependency for plexus-container-default which is part of some enforcer rules prerequisites

PackagePattern should allow to match both static and non-static imports

We should introduce a syntax that would optionally match a static modifier. I'm thinking about using ? as in regular expressions. Example: static? java.util.**
Another idea would be to introduce another flag that would make all configured patterns automatically optionally match the static modifier.
See also the discussion in #51

Match direct fully qualified uses when banning an import

I noticed that if I, say, ban org.apache.commons.lang.* then

import org.apache.commons.lang.StringUtils;

is properly flagged, as expected, but one can bypass this by using StringUtils with its fully qualified name, e.g.:

org.apache.commons.lang.StringUtils.isBlank(string);

This is a bit unfortunate, as it leaves a way to still explicitly use a class from a banned import. Is there currently a way of disallowing this? If not, it would be very nice to have the feature!

Remove 'sourceFileCharset' in favor of maven default

There is no benefit in introducing an own property for specifying the source file charset. There is already a maven property (which we already use as default): project.build.sourceEncoding

sourceFileCharset will be deprecated in version 0.15.0 and removed in subsequent versions.

Incorrect import statement detection

Using a variable called imported produces a StringIndexOutOfBoundsException, as de.skuzzle.enforcer.restrictimports.impl.ImportMatcherImpl#isImport wrongly detects this line as an import statement. The search for the expectedly following (space) leads to the aforementioned exception.

(The method de.skuzzle.enforcer.restrictimports.impl.ImportMatcherImpl#isPackage will likely be prone to the same issue.)

org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce (check-imports) on project swp: Execution check-imports of goal org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce failed: String index out of range: -1
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:225)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
Caused by: org.apache.maven.plugin.PluginExecutionException: Execution check-imports of goal org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce failed: String index out of range: -1
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:110)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
    ... 19 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -1
    at java.lang.String.substring(String.java:1960)
    at de.skuzzle.enforcer.restrictimports.impl.ImportMatcherImpl.extractPackageName(ImportMatcherImpl.java:94)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:373)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
    at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:373)
    at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:373)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.Iterator.forEachRemaining(Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at de.skuzzle.enforcer.restrictimports.impl.ImportMatcherImpl.matchFile(ImportMatcherImpl.java:47)
    at de.skuzzle.enforcer.restrictimports.impl.SourceTreeAnalyzerImpl.lambda$analyze$2(SourceTreeAnalyzerImpl.java:27)
    at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:267)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
    at java.util.Iterator.forEachRemaining(Iterator.java:116)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
    at java.util.stream.ReferencePipeline$11$1.accept(ReferencePipeline.java:373)
    at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at de.skuzzle.enforcer.restrictimports.impl.SourceTreeAnalyzerImpl.analyze(SourceTreeAnalyzerImpl.java:28)
    at de.skuzzle.enforcer.restrictimports.RestrictImports.execute(RestrictImports.java:46)
    at org.apache.maven.plugins.enforcer.EnforceMojo.execute(EnforceMojo.java:193)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
    ... 20 more

Make parallel analysis the default

Version 2.0.0 will include the experimental parallel flag, which will cause the analysis to run in parallel. I think the flag should probably be true by default. In the end, this is just an implementation detail as it only has effect on the order of reported findings which should not be important to an end user.

RestrictImports doesn't handle multiple whitespaces well

This works:

<allowedImport>static org.junit.jupiter.api.**</allowedImport>

This (thanks to formatter wrapping long lines) fails:

<allowedImport>static 
    org.junit.jupiter.api.**</allowedImport>

[INFO] --- maven-enforcer-plugin:3.0.0:enforce (ban-transitive-imports) @ oshi-parent ---
[WARNING] Rule 0: org.apache.maven.plugins.enforcer.RestrictImports failed with message:
Encountered unexpected exception: class java.lang.String cannot be cast to class de.skuzzle.enforcer.restrictimports.analyze.PackagePattern (java.lang.String is in module java.base of loader 'bootstrap'; de.skuzzle.enforcer.restrictimports.analyze.PackagePattern is in unnamed module of loader org.codehaus.plexus.classworlds.realm.ClassRealm @328d044f)

This also fails (two spaces):

<allowedImport>static  org.junit.jupiter.api.**</allowedImport>

Since these rules are all package-based and there aren't supposed to be spaces in package names, this seems to be entirely a side effect of having to include "static " with a single space.

Add system properties for failBuild and skip

I think it would be good to introduce the parameters -Drestrictimports.skip and -Drestrictimports.failBuild to control the respective parameters from command line. This can already be achieved with manual configuration, but I think ootb support would be nice

Error message is wrong

The error message that is presented to the user in case an allowed package pattern does not match any banned package name is wrong. It currently states

The allowed import pattern '<package>' does not match any base package."

while it should be

The allowed import pattern '<package>' does not match any banned package."

Add Scala support

Now that we have better support for different languages, we could try implementing Scala support on top of the current matching algorithm.
This poses the following challenges:

  1. Scala allows you to place import statements everywhere. So we can not eagerly break the file consumption in case no import has been found. We have to scan the whole file.
  2. Scala allows nested package declarations everywhere. So we can not find a class's package name by simply scanning finding the first line starting with package.

Overall, Scala's import and package statements do not fit our current matching architecture at all. This ticket may be used to collect ideas and use cases for scala support before I'll actually start to implement this.

Restricting based on modules

A few nights ago I wasted a few hours debugging what turned out to be a few unwanted imports snuck into my code by well-meaning committers. One of my fellow maintainers recommended your project, which looks fantastic. I've mostly got things configured as I want them but I have a few implementation questions and possibly a related feature request.

Use case / TLDR:

  • I want to use this enforcer rule to only permit packages aligned with the modules I require.

Easier said than done. ๐Ÿ˜

Motivation:

  • I support JDK8, and plan to until EOL, so my main development branch is JDK8 based
  • Some users want a module descriptor to support JDK11+, so I have a separate branch, with a few minor tweaks. I keep this up to date by cherry-picking into it, but it doesn't see the same testing with CI except when I copy stuff over
  • When I run into testing issues, they're nearly always related to java modules somehow. In the latest case, I had Junit 4 code that "worked" due to a transitive dependency, but was unwanted in my Junit 5 setup.
  • I could try to restrict expected problem areas (JUnit/Hamcrest) but other transitive issues have cropped up (Apache Commons, etc.) and it can end up being a game of whack-a-mole.
  • What I really want is a "exclude everything but what I allow"
    • This is currently supported (see my example below) but on the package level
    • I can read the module descriptors for my dependencies and add all their exported packages, except...
    • The package level is unwieldy for some modules, specifically java.base.

Here's what I currently have which is mostly what I want:

<RestrictImports>
    <includeTestCode>true</includeTestCode>
    <!-- Disallow all imports except explicitly allowed -->
    <reason>Disallow transitive dependencies</reason>
    <bannedImport>**</bannedImport>
    <allowedImports>
        <!-- Allow oshi itself :-) -->
        <allowedImport>oshi.**</allowedImport>
        <!-- Allow core Java usages -->
        <allowedImport>java.**</allowedImport>
        <!-- Allow known dependencies -->
        <allowedImport>com.sun.jna.**</allowedImport>
        <allowedImport>org.slf4j.**</allowedImport>
        <allowedImport>org.junit.jupiter.api.**</allowedImport>
    </allowedImports>
    <!-- No restrictions on oshi-demo -->
    <exclusion>oshi.demo.**</exclusion>
</RestrictImports>

Two problems here:

  • I would really like to restrict to the java.base module. Your README includes an example of excluding JUL, whose implementations are in the java.logging module, which I don't include in my modular descriptor. I tried just listing by packages, but there are a ludicrous number of packages in java.base.
    • While I understand that it may be impractical/impossible to restrict based on modules (one would have to read the modular descriptor and parse out all the requires, then find those modules and parse out the exports, and that's not even possible for projects like my own and JNA who publish separate JDK8 and JDK11 artifacts), I do think that the JDK, or the java.base module in particular, is special enough that it might deserve its own special treatment in your code. (I'd be happy to try to contribute something along these lines if you think it's a good idea.)
  • The documentation isn't clear in which order these rules are applied.
    • Is it always restrict-then-allow? Can I allow broadly and then restrict?
    • Can I restrict **, allow foo.**, and then restrict foo.bar.** in that order?
      • Specifically I've been lazy and just allowed java.** but I'd like to exclude JUL...
    • How do multiple <RestrictImports> sections interact with each other? Both must apply? Either must apply? If I add a new section restricting JUL to my above configuration would it achieve my goal?

A third "problem" -- looks like I need to duplicate this entire setup for static imports? Is there no easy switch to use the same config for both?

Thanks for a great project. Looking forward to tweaking it "just right".

Full compilation unit parsing errors with recent Java language features

We have been using <parseFullCompilationUnit>true</parseFullCompilationUnit>(#57) smoothly for quite a while.
Now we encounter an error after converting an enum to local enum: (line 298,col 14) Parse error. Found "{", expected one of "," ";" "=" "@" "["

@Test
void enumMap() {
    enum E {
        A, B;
    }
    ...
}

The used JavaParser README states that Java language features up to version 15 are supported. It turns out we are affected by javaparser/javaparser#3990, a Java 16 language feature.

Therefore some improvements could be made:

  • It would be very useful to mention the filename in the error message to identify the cause of a build break much more easily.
  • The supported language versions can be added to the Limitation section in the readme.
  • As Java 17 LTS has been released for more than a year, are there other java parsers that support Java 17+?

What do you think?

Corr. stacktrace:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:3.2.1:enforce (enforce-java-imports) on project core-utils: Execution enforce-java-imports of goal org.apache.maven.plugins:maven-enforcer-plugin:3.2.1:enforce failed: (line 298,col 14) Parse error. Found "{", expected one of  "," ";" "=" "@" "["
[ERROR] Problem stacktrace :
[ERROR]   com.github.javaparser.GeneratedJavaParser.generateParseException(GeneratedJavaParser.java:13770)
[ERROR]   com.github.javaparser.GeneratedJavaParser.jj_consume_token(GeneratedJavaParser.java:13615)
[ERROR]   com.github.javaparser.GeneratedJavaParser.BlockStatement(GeneratedJavaParser.java:5707)
[ERROR]   com.github.javaparser.GeneratedJavaParser.Statements(GeneratedJavaParser.java:2673)
[ERROR]   com.github.javaparser.GeneratedJavaParser.Block(GeneratedJavaParser.java:5644)
[ERROR]   com.github.javaparser.GeneratedJavaParser.MethodDeclaration(GeneratedJavaParser.java:2074)
[ERROR]   com.github.javaparser.GeneratedJavaParser.ClassOrInterfaceBodyDeclaration(GeneratedJavaParser.java:1675)
[ERROR]   com.github.javaparser.GeneratedJavaParser.ClassOrInterfaceBody(GeneratedJavaParser.java:1187)
[ERROR]   com.github.javaparser.GeneratedJavaParser.ClassOrInterfaceDeclaration(GeneratedJavaParser.java:501)
[ERROR]   com.github.javaparser.GeneratedJavaParser.CompilationUnit(GeneratedJavaParser.java:152)
[ERROR]   com.github.javaparser.JavaParser.parse(JavaParser.java:125)
[ERROR]   com.github.javaparser.JavaParser.parse(JavaParser.java:231)
[ERROR]   com.github.javaparser.JavaParserAdapter.parse(JavaParserAdapter.java:79)
[ERROR]   com.github.javaparser.StaticJavaParser.parse(StaticJavaParser.java:173)
[ERROR]   de.skuzzle.enforcer.restrictimports.parser.lang.JavaCompilationUnitParser.parseCompilationUnit(JavaCompilationUnitParser.java:37)

excludedClass does not take wildcard patterns

I tried to use a patterns such as **.ObjectFactory in the excludedClass but the enforcer rule fails.

Input:

<rules>
  <restrictImports implementation="de.skuzzle.enforcer.restrictimports.RestrictImports">
    <basePackage>**</basePackage>
    <includeTestCode>false</includeTestCode>
    <bannedImports>
      <bannedImport>javax.xml.bind.JAXBElement</bannedImport>
    </bannedImports>
    <excludedClasses>
      <excludedClass>**.ObjectFactory</excludedClass>
    </excludedClasses>
  </restrictImports>
</rules>

Expected:

Rule fails on all classes importing the banned class except the ones called ObjectFactory at any package level.

Actual:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce (check-jaxb-generated-imports) on project xxx: A type incompatibility occurred while executing org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce: java.lang.String cannot be cast to de.skuzzle.enforcer.restrictimports.PackagePattern

Dynamic instantiation of SourceLineParsers

Instead of hardcoding the SourceLineParser instances, they should by dynamically looked up via Java's ServiceLoader. That would also allow to plug them in via separate jar files

Use maven-shade plugin to eventually resolve classloading issue?

The following pom snippet relocates all guava classes into a new package. That would resolve classloading issues as in #20 or #21.
However, this grows the final jar's size from 70kb to 2800kb which are quite some numbers.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <configuration>
                    <artifactSet>
                        <includes>
                            <include>com.google.guava:guava</include>
                        </includes>
                    </artifactSet>
                    <relocations>
                        <relocation>
                            <pattern>com.google.common</pattern>
                            <shadedPattern>com.shaded.google.common</shadedPattern>
                        </relocation>
                    </relocations>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

Allow to run on test code only

Currently its possible to analyze either only compile or both compile and test code. Maybe it would be usefull to only run on test code?

Allowing subpackages that are banned by a wildcard

I have the following configuration:

      <configuration>
          <rules>
              <restrictImports implementation="de.skuzzle.enforcer.restrictimports.rule.RestrictImports">
                  <reason>Allow only module imports</reason>
                  <bannedImports>
                      <bannedImport>org.kie.kogito.**</bannedImport>
                  </bannedImports>
                  <allowedImports>
                      <!-- we always allow process.* as these will be movable to internal -->
                      <allowedImport>org.kie.kogito.incubation.decisions.**</allowedImport>
                      <!-- we also allow common.*  -->
                      <allowedImport>org.kie.kogito.incubation.common.**</allowedImport>
                  </allowedImports>
              </restrictImports>
          </rules>
      </configuration>
                        

this class however causes an error:

package org.kie.kogito.incubation.processes.services;

import org.kie.kogito.incubation.common.DataContext;
import org.kie.kogito.incubation.processes.ProcessId;

public interface StraightThroughProcessService {
    DataContext evaluate(ProcessId processId, DataContext inputContext);
}

because of the rule:

<bannedImport>org.kie.kogito.**</bannedImport>

but I thought:

<allowedImport>org.kie.kogito.incubation.common.**</allowedImport>

would allow

import org.kie.kogito.incubation.common.DataContext;

what am I doing wrong? :)

Support multiple java imports in same line

Java supports writing multiple import statements in the same line, separated by ; like

import java.util.List; import java.util.Collection;

Though that is not the preferred formatting for most code bases it should be easy to support this.

Allow multiple banned-import-groups within a single enforcer rule

This would allow influences of rules to other rules. My favorite:

  • A source file is only subject to a single group
  • The group to which a source file is subject is chosen by the longest basePackage pattern which matches the source file
...
<bannedImportGroups>
    <bannedImportGroup>
        <basePackage>com.foo.**</basePackage>
        <allowedImports>...</allowedImports>
    </bannedImportGroup>
    <bannedImportGroup>
        <basePackage>com.foo.bar.**</basePackage>
        <allowedImports>...</allowedImports>
    </bannedImportGroup>
</bannedImportGroups>
...

In this sample, source files from com.foo.bar would only be subject to the rules within the second group.

Another approach could be that all groups are applied to a single source file and groups with a more specific basePackage can override rules from groups with less specific basePackage.

Get rid of commentLineBufferSize

Manual configuration of the buffer size adds useless clutter to the plugin's configuration which otherwise is aimed to be as concise as possible.

The fixed value for commentLineBufferSize arises from the use of java's PushbackReader which uses a fixed size pushback buffer under the hood. Stripping comments from the input source is already quite complicated but I feel there must be a better way involving a dynamically growing buffer

Separate file parsing from banned import analysis

Currently, the ImportMatcher class has multiple concerns:

  • parsing the source file
  • selecting the applicable BannedImportGroup
  • matching the found import statements against the banned import definitions

These concerns should be split up into multiple classes so that parsing and checking for banned imports become separate processes.

Support for ignoring classes that cannot be fixed

See https://github.com/gaul/modernizer-maven-plugin/tree/master/modernizer-maven-annotations for example.

Currently if a specific class has a restricted import that cannot be addressed such as some usage of spring jdbc extended logic that forces imports on say commons logging due to required overrides, we must turn this completely off. That loses the point entirely due to one class. It would be better to have ability to add an annotation to a class similar to done with modernizer maven annotations project does for similar reasons. I have no experience doing that, but given the example above, I'd be more than happy to help contribute to make this happen.

For my usage, I am one of the leads on a very large supporting devops team and we try to enforce best practice across 2k+ repos. We use this and have ability to turn off and on globally per project and try to discourage class usage that conflicts. Currently we are also starting to to block all junit 4 or before usage but some things like junit4 rules are harder to deal with when entirely customized and tools like open rewrite are unable to magically rewrite them. Having such an annotation to scan and skip looking on listed classes is a modern way to do this and would greatly benefit in our goal if blocking all junit 4 as possible without entirely turning this off.

Allowed import should match any banned import

There is a bug in SourceTreeAnalyzerImpl.checkGroupConsistency. Here, all allowed imports are expected to be matched by a base package . The correct logic would be that every allowed import must be matched by at least one banned import (otherwise it would make no sense to mark it as allowed).

Added to plugin section of parent pom, but nothing happens

Hi,

My goal is to fail the build if there are any JUnit4 imports present in my test code.

I've added the following configuration (see below) to pluginManagement section of my parent pom, but when I run a child project which contains org.junit.Test (which is configured as bannedImport), nothing happens and the build is succesful.

I've also tried defining the plugin in the plugin section instead of the pluginManagement section, and I also tried copying the plugin to the child project. The result is the same.

In all cases I do see the Maven output:

[INFO] --- maven-enforcer-plugin:3.0.0-M2:enforce (check-junit4-imports) @ myproject ---

I've also tried with a regulular package containing classes (instead of test classes) and also here nothing happens. Build is always successful.

What am I missing here. My configuration is as follows:

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-enforcer-plugin</artifactId>
	<dependencies>
		<dependency>
			<groupId>de.skuzzle.enforcer</groupId>
			<artifactId>restrict-imports-enforcer-rule</artifactId>
			<version>1.0.1</version>
		</dependency>
	</dependencies>
	<executions>
		<execution>
			<id>check-junit4-imports</id>
			<phase>process-sources</phase>
			<goals>
				<goal>enforce</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<rules>
			<restrictImports implementation="de.skuzzle.enforcer.restrictimports.rule.RestrictImports">
				<includeTestCode>true</includeTestCode>
				<reason>Gebruik geen JUnit 4 imports; gebruik in plaatst daarvan JUnit 5 imports</reason>
				<!-- JUnit 4 classes met static methoden -->
				<bannedImport>static org.junit.Assert.*</bannedImport>
				<bannedImport>static org.junit.Assume.*</bannedImport>
				<bannedImport>org.junit.AssumptionViolatedException.*</bannedImport>
				<!-- JUnit 4 interfaces -->
				<bannedImport>org.junit.After</bannedImport>
				<bannedImport>org.junit.AfterClass</bannedImport>
				<bannedImport>org.junit.Before</bannedImport>
				<bannedImport>org.junit.BeforeClass</bannedImport>
				<bannedImport>org.junit.ClassRule</bannedImport>
				<bannedImport>org.junit.FixMethodOrder</bannedImport>
				<bannedImport>org.junit.Ignore</bannedImport>
				<bannedImport>org.junit.Rule</bannedImport>
				<bannedImport>org.junit.Test</bannedImport>
				<!-- JUnit 4 packages -->
				<bannedImport>org.junit.ComparisonFailure.**</bannedImport>
				<bannedImport>org.junit.experimental.**</bannedImport>
				<bannedImport>org.junit.matchers.**</bannedImport>
				<bannedImport>org.junit.rules.**</bannedImport>
				<bannedImport>org.junit.runner.**</bannedImport>
				<bannedImport>org.junit.runners.**</bannedImport>
				<bannedImport>org.junit.validator.**</bannedImport>
			</restrictImports>
		</rules>
	</configuration>
</plugin>

Feature request: exclude generate-sources and/or generate-test-sources

A maven plugin or annotation processor may generate code using imports that we normally ban, and it is not possible to control the code generation.

It would be nice we can ignore a whole directory such as target/generated-sources. I understand this can be done on package level, but sometimes we have generated code and human written code in the same package but different directories.

"java.lang.StringIndexOutOfBoundsException: String index out of range: -7" when import statement ends with two semicolons

when parsing import statement that ends with two semicolons, error like below will happen:
org.apache.maven.enforcer.rule.api.EnforcerRuleException: Encountered unexpected exception: String index out of range: -7
at de.skuzzle.enforcer.restrictimports.rule.RestrictImports.execute (RestrictImports.java:85)
at org.apache.maven.plugins.enforcer.EnforceMojo.execute (EnforceMojo.java:194)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute (MavenCli.java:956)
at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:288)
at org.apache.maven.cli.MavenCli.main (MavenCli.java:192)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -7
at java.lang.String.substring (String.java:1931)
at de.skuzzle.enforcer.restrictimports.parser.lang.JavaLanguageSupport.parseImport (JavaLanguageSupport.java:48)
at de.skuzzle.enforcer.restrictimports.parser.ImportStatementParserImpl.parse (ImportStatementParserImpl.java:68)
at de.skuzzle.enforcer.restrictimports.analyze.SourceTreeAnalyzerImpl.lambda$parseFileUsing$0 (SourceTreeAnalyzerImpl.java:57)
at java.util.stream.ReferencePipeline$3$1.accept (ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$3$1.accept (ReferencePipeline.java:193)
at java.util.stream.ReferencePipeline$2$1.accept (ReferencePipeline.java:175)
at java.util.Iterator.forEachRemaining (Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining (Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential (ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential (ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach (ReferencePipeline.java:418)
at de.skuzzle.enforcer.restrictimports.analyze.SourceTreeAnalyzerImpl.analyzeDirectories (SourceTreeAnalyzerImpl.java:50)
at de.skuzzle.enforcer.restrictimports.analyze.SourceTreeAnalyzerImpl.analyze (SourceTreeAnalyzerImpl.java:32)
at de.skuzzle.enforcer.restrictimports.rule.RestrictImports.execute (RestrictImports.java:61)
at org.apache.maven.plugins.enforcer.EnforceMojo.execute (EnforceMojo.java:194)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo (DefaultBuildPluginManager.java:137)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:210)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:156)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute (MojoExecutor.java:148)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:117)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject (LifecycleModuleBuilder.java:81)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build (SingleThreadedBuilder.java:56)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute (LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:305)
at org.apache.maven.DefaultMaven.doExecute (DefaultMaven.java:192)
at org.apache.maven.DefaultMaven.execute (DefaultMaven.java:105)
at org.apache.maven.cli.MavenCli.execute (MavenCli.java:956)
at org.apache.maven.cli.MavenCli.doMain (MavenCli.java:288)
at org.apache.maven.cli.MavenCli.main (MavenCli.java:192)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced (Launcher.java:282)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch (Launcher.java:225)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode (Launcher.java:406)
at org.codehaus.plexus.classworlds.launcher.Launcher.main (Launcher.java:347)

Add control flags as configuration options

Introduce 2 new flags for configuring the enforcer rule instances:

  • failBuild (default: true): When set to false, the error messages will be logged but the build will not fail
  • skip (default: false): When set to true the whole rule execution will be skipped

Rename 'excludedClasses'

The excludedClasses configuration option should be renamed. It is currently confusing because it not only excludes classes but also packages

Allow to restrict usage of wildcard imports

As wildcard imports may lead to inaccurate results, we could implement a flag that will cause the analysis to fail if one is encountered.

Wildcard imports can be considered a bad practice, so this might be a helpful feature anyway

Parent pom without source code makes the plugin crash

I have setup the plugin in a parent pom inherited by other poms but the plugin crashes as the parent pom has no source code. Stacktrace follows.

Caused by: java.nio.file.NoSuchFileException: C:\..\Whatever\src\main\java at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(WindowsFileAttributeViews.java:53) at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(WindowsFileAttributeViews.java:38) at sun.nio.fs.WindowsFileSystemProvider.readAttributes(WindowsFileSystemProvider.java:193) at java.nio.file.Files.readAttributes(Files.java:1737) at java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:219) at java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:276) at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:322) at java.nio.file.FileTreeIterator.<init>(FileTreeIterator.java:72) at java.nio.file.Files.find(Files.java:3687) at de.skuzzle.enforcer.restrictimports.impl.IOUtilsImpl.listFiles(IOUtilsImpl.java:25) ... 35 more [WARNING] Rule 0: de.skuzzle.enforcer.restrictimports.RestrictImports failed with message: Encountered IO exception

new RestrictImports rule results in stack over flow

Recently tried to upgrade from 0.8.0 to 0.13.0 but a configuration like below will result in a StackOverflowError. I used the new RestrictImports classes as per deprecation warning and it all went south from there. Hope this is sufficient information to look into this problem.

The last known working version with the old de.skuzzle.enforcer.restrictimports.RestrictImports is 0.10.0, even if I go to version 0.11.0 with the old rules I get the same error as below.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <executions>
    <execution>
      <id>enforce-versions</id>
      <goals>
        <goal>enforce</goal>
      </goals>
      <configuration>
        <rules>
          <requireMavenVersion>
            <version>3.3.0</version>
          </requireMavenVersion>
          <requireJavaVersion>
            <version>1.8</version>
          </requireJavaVersion>
        </rules>
      </configuration>
    </execution>
    <execution>
      <id>check-jaxb-generated-imports</id>
      <goals>
        <goal>enforce</goal>
      </goals>
      <phase>compile</phase>
      <configuration>
        <rules>
          <restrictImports implementation="de.skuzzle.enforcer.restrictimports.rule.RestrictImports">
            <reason>JAXBElement is banned from the domain model, see design principles.</reason>
            <bannedImport>javax.xml.bind.JAXBElement</bannedImport>
            <exclusion>my.model.ObjectFactory</exclusion>
          </restrictImports>
          <restrictImports implementation="de.skuzzle.enforcer.restrictimports.rule.RestrictImports">
            <reason>Dates have to be mapped to Joda or Java Time</reason>
            <bannedImport>javax.xml.datatype.XMLGregorianCalendar</bannedImport>
            <includeTestCode>true</includeTestCode>
          </restrictImports>
        </rules>
      </configuration>
    </execution>
  </executions>
  <dependencies>
    <dependency>
      <groupId>de.skuzzle.enforcer</groupId>
      <artifactId>restrict-imports-enforcer-rule</artifactId>
      <version>0.13.0</version>
    </dependency>
  </dependencies>
</plugin>

Results in:

[INFO] --- maven-enforcer-plugin:1.4.1:enforce (check-jaxb-generated-imports) @ my-model ---
[DEBUG] Configuring mojo org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce from plugin realm ClassRealm[plugin>org.apache.maven.plugins:maven-enforcer-plugin:1.4.1, parent: sun.misc.Launcher$AppClassLoader@55f96302]
[DEBUG] Configuring mojo 'org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce' with basic configurator -->
[DEBUG]   (s) fail = true
[DEBUG]   (s) failFast = false
[DEBUG]   (f) ignoreCache = false
[DEBUG]   (f) mojoExecution = org.apache.maven.plugins:maven-enforcer-plugin:1.4.1:enforce {execution: check-jaxb-generated-imports}
[DEBUG]   (s) project = MavenProject: com.example:my-model:3.0.14-SNAPSHOT @ /Users/fred/workspaces/my-model/pom.xml
[DEBUG]   (s) reason = JAXBElement is banned from the domain model, see design principles.
[DEBUG]   (s) bannedImport = javax.xml.bind.JAXBElement
[DEBUG]   (s) exclusion = my.model.ObjectFactory
[DEBUG]   (s) reason = Dates have to be mapped to Joda or Java Time
[DEBUG]   (s) bannedImport = javax.xml.datatype.XMLGregorianCalendar
[DEBUG]   (s) includeTestCode = true
[DEBUG]   (s) rules = [de.skuzzle.enforcer.restrictimports.rule.RestrictImports@16088b32, de.skuzzle.enforcer.restrictimports.rule.RestrictImports@2f3b4de2]
[DEBUG]   (s) session = org.apache.maven.execution.MavenSession@7885776b
[DEBUG]   (s) skip = false
[DEBUG] -- end configuration --
[DEBUG] Executing rule: de.skuzzle.enforcer.restrictimports.rule.RestrictImports
[DEBUG] Checking for banned imports
[DEBUG] Banned import groups:
BannedImportGroup{basePackages=[**], bannedImports=[javax.xml.bind.JAXBElement], allowedImports=[], excludedClasses=[my.model.ObjectFactory], reason=JAXBElement is banned from the domain model, see design principles.}
[DEBUG] Including source dir: /Users/fred/workspaces/my-model/src/main/java
[DEBUG] Including source dir: /Users/fred/workspaces/my-model/target/generated-sources/xjc
[DEBUG] Including source dir: /Users/fred/workspaces/my-model/target/generated-sources/annotations
[DEBUG] Analyzer settings:
AnalyzerSettings{sourceFileCharset=UTF-8, commentLineBufferSize=128, rootDirectories=[/Users/fred/workspaces/my-model/src/main/java, /Users/fred/workspaces/my-model/target/generated-sources/xjc, /Users/fred/workspaces/my-model/target/generated-sources/annotations]}
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 18.731 s
[INFO] Finished at: 2018-11-06T00:58:20+10:00
[INFO] ------------------------------------------------------------------------
---------------------------------------------------
constituent[0]: file:/usr/local/Cellar/maven/3.5.4/libexec/conf/logging/
constituent[1]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-repository-metadata-3.5.4.jar
constituent[2]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-resolver-transport-wagon-1.1.1.jar
constituent[3]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/aopalliance-1.0.jar
constituent[4]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-resolver-provider-3.5.4.jar
constituent[5]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/plexus-utils-3.1.0.jar
constituent[6]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/plexus-interpolation-1.24.jar
constituent[7]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-artifact-3.5.4.jar
constituent[8]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/plexus-cipher-1.7.jar
constituent[9]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/guava-20.0.jar
constituent[10]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-slf4j-provider-3.5.4.jar
constituent[11]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/slf4j-api-1.7.25.jar
constituent[12]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/cdi-api-1.0.jar
constituent[13]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/jcl-over-slf4j-1.7.25.jar
constituent[14]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-resolver-spi-1.1.1.jar
constituent[15]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-compat-3.5.4.jar
constituent[16]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-plugin-api-3.5.4.jar
constituent[17]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/plexus-sec-dispatcher-1.4.jar
constituent[18]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-resolver-util-1.1.1.jar
constituent[19]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/plexus-component-annotations-1.7.1.jar
constituent[20]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-settings-builder-3.5.4.jar
constituent[21]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/commons-cli-1.4.jar
constituent[22]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/commons-io-2.5.jar
constituent[23]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/jansi-1.17.1.jar
constituent[24]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-core-3.5.4.jar
constituent[25]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-resolver-impl-1.1.1.jar
constituent[26]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/guice-4.2.0-no_aop.jar
constituent[27]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/org.eclipse.sisu.inject-0.3.3.jar
constituent[28]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/wagon-file-3.1.0.jar
constituent[29]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-builder-support-3.5.4.jar
constituent[30]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-model-3.5.4.jar
constituent[31]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-settings-3.5.4.jar
constituent[32]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/wagon-http-3.1.0-shaded.jar
constituent[33]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-resolver-api-1.1.1.jar
constituent[34]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-resolver-connector-basic-1.1.1.jar
constituent[35]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-shared-utils-3.2.1.jar
constituent[36]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/org.eclipse.sisu.plexus-0.3.3.jar
constituent[37]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-model-builder-3.5.4.jar
constituent[38]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/javax.inject-1.jar
constituent[39]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/maven-embedder-3.5.4.jar
constituent[40]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/jsr250-api-1.0.jar
constituent[41]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/commons-lang3-3.5.jar
constituent[42]: file:/usr/local/Cellar/maven/3.5.4/libexec/lib/wagon-provider-api-3.1.0.jar
---------------------------------------------------
Exception in thread "main" java.lang.StackOverflowError
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:742)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
	at java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:743)
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
	at java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:270)
	at java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:419)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)

Block comments can lead to mismatches

Leading block comments will cause the class's own package not to be recognized so that it can not be matched against the base package.

Then there is the possibilty of maximum strange comments like the following:

package de.skuzzle.test;
/*Why
would you place a block comment like
this?*/import de.skuzzle.sample.Test;

In this case the import line would not be recognized as such.

The problem arises because we are currently reading the java file line by line.
Maybe the best solution to this is to build a real parser which only parses the beginning of the file until the last import statement. Parsing every Java file into a full syntax tree seems to be too unefficient.

Banning static imports

I tried using this to ban static imports:
import static org.testng.Assert.*

It doesn't fail as I had expected. Does the plugin currently support static imports?

Maybe my configuration is wrong. My POM plugin configuration:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-enforcer-plugin</artifactId> <version>3.0.0-M2</version> <dependencies> <dependency> <groupId>de.skuzzle.enforcer</groupId> <artifactId>restrict-imports-enforcer-rule</artifactId> <version>0.9.0</version> </dependency> </dependencies> <executions> <execution> <id>assert-excludes</id> <phase>process-sources</phase> <goals> <goal>enforce</goal> </goals> <configuration> <rules> <restrictImports implementation="de.skuzzle.enforcer.restrictimports.RestrictImports"> <reason>Ensure TestNG Assertions are not beeing used</reason> <bannedImport>org.testng.Assert.*</bannedImport> </restrictImports> </rules> </configuration> </execution> </executions> </plugin>

Thanks.

UPDATE: After looking at the source code, I can get it to work by doing this:
<bannedImport>static org.testng.Assert.*</bannedImport>

Was this the intended way to ban static imports?
Thanks again.

Consider to provide a gradle plugin

Providing the same functionality as a gradle plugin should be pretty straight-forward. All the core functionality is already implemented without any dependencies to maven.
We could make this a multi-module project with 3 modules: core, maven-plugin and gradle-plugin.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.