Skip to content

Commit

Permalink
[Filtering] Add support for filtering packages like Maven plugin (#53)
Browse files Browse the repository at this point in the history
This adds package filters similar to how the Maven plugin filters in its `<packages>` block. This supports ant-like patterns similar to how patterns filter.

Partial implementation of #16.
  • Loading branch information
Nava2 authored Apr 25, 2023
1 parent 73a391b commit b8acd28
Show file tree
Hide file tree
Showing 7 changed files with 669 additions and 16 deletions.
43 changes: 33 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,29 @@ sourceSets {
}
```

#### packages - `JavaPackageNamePatternFilterable`

Default: All packages

Package filters can be used to include, or exclude individual or groups of packages to be generated. These filters use a
similar pattern to `include`/`exclude` with `SourceSet`.

```groovy
sourceSets {
main {
assertJ {
packages {
include "org.example**" // include *all* packages in `org.example`
exclude "org.example.foo" // exclude `org.example.foo` specifically
}
}
}
}
```

See [`PackageFilter` tests](src/test/groovy/org/assertj/generator/gradle/parameter/PackageFilter.groovy) for more
examples.

#### entryPoints - `EntryPointsGeneratorOptions`

Default: Only generate "standard" assertion entrypoint
Expand Down Expand Up @@ -238,25 +261,25 @@ A useful trick to turn on _only_ a set of values is to just set the `entryPoints

```groovy
sourceSets {
main {
assertJ {
entryPoints = ['BDD', 'JUNIT_SOFT'] // turn on _only_ BDD and JUnit Soft
}
main {
assertJ {
entryPoints = ['BDD', 'JUNIT_SOFT'] // turn on _only_ BDD and JUnit Soft
}
}
}
```

Or within the `entryPoints` closure:

```groovy
sourceSets {
main {
assertJ {
entryPoints {
only 'BDD', 'JUNIT_SOFT' // turn on _only_ BDD and JUnit Soft
}
}
main {
assertJ {
entryPoints {
only 'BDD', 'JUNIT_SOFT' // turn on _only_ BDD and JUnit Soft
}
}
}
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.assertj.assertions.generator.description.converter.ClassToClassDescri
import org.assertj.assertions.generator.util.ClassUtil
import org.assertj.generator.gradle.tasks.config.AssertJGeneratorExtension
import org.assertj.generator.gradle.tasks.config.SerializedTemplate
import org.assertj.generator.gradle.tasks.config.patterns.JavaPackageNamePatternFilterable
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
Expand Down Expand Up @@ -77,10 +78,13 @@ open class AssertJGenerationTask @Inject internal constructor(
val outputDir: DirectoryProperty

@get:Input
internal val skip: Property<Boolean> = objects.property<Boolean>()
internal val skip: Property<Boolean> = objects.property()

@get:Input
internal val hierarchical: Property<Boolean> = objects.property<Boolean>()
internal val hierarchical: Property<Boolean> = objects.property()

@get:Input
internal val packagesFilterable: Property<JavaPackageNamePatternFilterable> = objects.property()

@get:Input
internal val entryPoints: SetProperty<AssertionsEntryPointType> = objects.setProperty()
Expand Down Expand Up @@ -110,6 +114,7 @@ open class AssertJGenerationTask @Inject internal constructor(

skip.set(project.provider { assertJOptions.skip })
hierarchical.set(project.provider { assertJOptions.hierarchical })
packagesFilterable.set(project.provider { assertJOptions.packages })
entryPoints.set(project.provider { assertJOptions.entryPoints.entryPoints })
entryPointsClassPackage.set(project.provider { assertJOptions.entryPoints.classPackage })
}
Expand All @@ -125,7 +130,9 @@ open class AssertJGenerationTask @Inject internal constructor(

val classLoader = URLClassLoader((generationClasspath + classDirectories).map { it.toURI().toURL() }.toTypedArray())

val packagesPredicate = packagesFilterable.get().asPredicate()
val allClassNames = getClassNames(classDirectories)
.filter { packagesPredicate.test(it.substringBeforeLast('.')) }

@Suppress("SpreadOperator") // Java interop
val allClasses = ClassUtil.collectClasses(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.assertj.generator.gradle.tasks.config

import org.assertj.assertions.generator.AssertionsEntryPointType
import org.assertj.generator.gradle.tasks.config.patterns.JavaPackageNamePatternFilterable
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
Expand All @@ -34,6 +35,12 @@ open class AssertJGeneratorExtension @Inject internal constructor(
val classDirectories: SourceDirectorySet =
objects.sourceDirectorySet("assertJClasses", "Classes to generate AssertJ assertions from")

val packages: JavaPackageNamePatternFilterable = objects.newInstance()

fun packages(action: Action<in JavaPackageNamePatternFilterable>): AssertJGeneratorExtension = apply {
action.execute(packages)
}

/**
* Generate generating Soft Assertions entry point class.
* @return templates value, never {@code null}
Expand All @@ -44,9 +51,8 @@ open class AssertJGeneratorExtension @Inject internal constructor(
* Method used for improving configuration DSL
* @return {@code this}
*/
fun templates(action: Action<in Templates>): AssertJGeneratorExtension {
fun templates(action: Action<in Templates>): AssertJGeneratorExtension = apply {
action.execute(templates)
return this
}

/**
Expand Down Expand Up @@ -82,9 +88,8 @@ open class AssertJGeneratorExtension @Inject internal constructor(
* Used to change "entry point" class generation.
* @return this
*/
fun entryPoints(action: Action<in EntryPointGeneratorOptions>): AssertJGeneratorExtension {
fun entryPoints(action: Action<in EntryPointGeneratorOptions>): AssertJGeneratorExtension = apply {
action.execute(entryPoints)
return this
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2023. assertj-generator-gradle-plugin 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.assertj.generator.gradle.tasks.config.patterns

import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.util.function.Predicate

@Suppress("UNCHECKED_CAST")
sealed class JavaIdentifierNamePatternFilterableBase<T, SELF>
where SELF : JavaIdentifierNamePatternFilterableBase<T, SELF> {
private var includePredicates = mutableSetOf<PatternPredicate<T>>()
private var excludePredicates = mutableSetOf<PatternPredicate<T>>()

internal abstract fun compilePatterns(patterns: Iterable<String>): Sequence<PatternPredicate<T>>

internal fun asPredicate(): Predicate<T> {
return Predicate { t ->
(includePredicates.isEmpty() || includePredicates.any { it.test(t) }) &&
(excludePredicates.isEmpty() || excludePredicates.none { it.test(t) })
}
}

fun setIncludes(includes: Iterable<String>): SELF {
this.includePredicates = compilePatterns(includes).toMutableSet()
return this as SELF
}

fun setExcludes(excludes: Iterable<String>): SELF {
this.excludePredicates = compilePatterns(excludes).toMutableSet()
return this as SELF
}

fun include(vararg includes: String): SELF {
this.includePredicates += compilePatterns(includes.asIterable())
return this as SELF
}

fun include(includes: Iterable<String>): SELF {
this.includePredicates += compilePatterns(includes)
return this as SELF
}

fun exclude(vararg excludes: String): SELF {
this.excludePredicates += compilePatterns(excludes.asIterable())
return this as SELF
}

fun exclude(excludes: Iterable<String>): SELF {
this.excludePredicates += compilePatterns(excludes)
return this as SELF
}

internal interface PatternPredicate<T> : Predicate<T> {
val pattern: String
}

@Throws(IOException::class)
protected fun writeObjectImpl(o: ObjectOutputStream) {
o.writeInt(includePredicates.size)
for (pattern in includePredicates.map { it.pattern }) {
o.writeUTF(pattern)
}

o.writeInt(excludePredicates.size)
for (pattern in excludePredicates.map { it.pattern }) {
o.writeUTF(pattern)
}
}

@Throws(IOException::class, ClassNotFoundException::class)
protected fun readObjectImpl(i: ObjectInputStream) {
val includesSize = i.readInt()
setIncludes((0 until includesSize).map { i.readUTF() })

val excludesSize = i.readInt()
setExcludes((0 until excludesSize).map { i.readUTF() })
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2023. assertj-generator-gradle-plugin 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.assertj.generator.gradle.tasks.config.patterns

private const val WILDCARD_MARKER = "!!WILDCARD_MARKER!!"

internal object JavaNamePatternCompiler {
fun compilePattern(pattern: String): Regex {
val escapedPackageCharacters = pattern
.replace(".", "\\.")
.replace("$", "\\$")

// Order matters because if we do single "*" first we double remove the "**"
val withWildcardMarkers = escapedPackageCharacters
.replace("**", "[\\w\\.]$WILDCARD_MARKER")
.replace("*", "\\w$WILDCARD_MARKER")

val withRegexWildcards = withWildcardMarkers.replace(WILDCARD_MARKER, "*")

return Regex(withRegexWildcards)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2023. assertj-generator-gradle-plugin 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.assertj.generator.gradle.tasks.config.patterns

import com.google.common.reflect.TypeToken
import org.assertj.generator.gradle.tasks.config.patterns.JavaNamePatternCompiler.compilePattern
import java.io.IOException
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable

/**
* Implements a similar construction to [org.gradle.api.tasks.util.PatternFilterable] that will match
* [TypeToken] instances.
*/
open class JavaPackageNamePatternFilterable internal constructor() :
JavaIdentifierNamePatternFilterableBase<String, JavaPackageNamePatternFilterable>(), Serializable {

override fun compilePatterns(patterns: Iterable<String>): Sequence<PatternPredicate<String>> {
return patterns.asSequence().map { PackagePatternPredicate.compile(it) }
}

@Throws(IOException::class)
protected fun writeObject(o: ObjectOutputStream) {
super.writeObjectImpl(o)
}

@Throws(IOException::class, ClassNotFoundException::class)
protected fun readObject(i: ObjectInputStream) {
super.readObjectImpl(i)
}

internal data class PackagePatternPredicate(
override val pattern: String,
private val namePattern: Regex,
) : PatternPredicate<String> {
override fun test(t: String): Boolean {
return namePattern.matches(t)
}

companion object {
fun compile(pattern: String) = PackagePatternPredicate(pattern, compilePattern(pattern))
}
}

companion object {
private const val serialVersionUID = 48634795L
}
}
Loading

0 comments on commit b8acd28

Please sign in to comment.