<commit_before>Groovy 2.5 comes with a host of new features and capabilities.

[[Groovy2.5releasenotes-Macros]]

== Macro support

Groovy is well known for its extensible compilation process.
The compiler supports a compile-time metaprogramming extension mechanism
which allows the compilation process to be enhanced with new functionality.
The mechanism allows the definition and execution of AST transformations at
various stages of the compilation process.

Groovy comes with a number of built-in AST transformations and, importantly,
you can write your own. To write such transformations until now, you have
had to become familiar with the compiler's internal representation of
Groovy syntactic structures. But that has now changed.

Groovy 2.5+ supports macros. Macros let you use Groovy syntax directly
rather than using the internal compiler representations when creating
compile-time metaprogramming extensions. This puts the creation of transformations
in the hands of all Groovy programmers not just Groovy compiler gurus.

=== Expressions and statements

Suppose we want to create an `@Info` transform which when placed on a class
creates a `getInfo()` method. Calling the `getInfo()` method will display some
information about the class. The traditional way to write the transformation
code might be something like:
[source,groovy]
----
...
def clazz = new MethodCallExpression(new VariableExpression("this"), "getClass", EMPTY_ARGUMENTS)
def body = new ExpressionStatement(new MethodCallExpression(clazz, "getName", EMPTY_ARGUMENTS))
classNode.addMethod('getInfo', ACC_PUBLIC, STRING_TYPE, EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body)
...
----

This will print out the class name when the `getInfo()` method is called.

With macros, the first two lines become:

[source,groovy]
----
def body = macro(true) { getClass().name }
----

Creation of both expressions and statements are supported. The `true` parameter
to the `macro` method ensures we get a statement here which is what `addMethod` needs.

Frequently you will want to receive parameters or resolve surrounding variables.
This is supported using a special `$v` notation. Suppose we want to augment the `getInfo`
method to additionally output the time the class was compiled and the Groovy version used.
Our code might look like this:

[source,groovy]
----
def date = new ConstantExpression(new Date().toString())
def version = new ConstantExpression(GroovySystem.version)
def body = macro(true) {
    """\
    Name: ${getClass().name}
    Compiled: ${$v{ date }}
    Using Groovy version: ${$v{ version }}""".stripIndent()
}
----

What the macro does is convert any Groovy code inside the closure
argument into its internal representation. When it gets to the
`$v` placeholders it does no conversion and just inserts
those sections directly. This is exactly what we want here since
we wrote those parts old-school style using the internal
AST data structures.

=== Macro classes

In addition to the ability to create statements and expressions, as supported by `macro`,
you might want to create whole classes. The `MacroClass` capability allows you to do that.
You supply the class you are wanting to create in a similar fashion to creating an anonymous
inner class in Java. The following example shows the approach for a scenario where you
might want to extract the information collected in previous examples in a class of its own:

[source,groovy]
----
ClassNode buildInfoClass(ClassNode reference) {
    def date = new ConstantExpression(new Date().toString())
    def vers = new ConstantExpression(GroovySystem.version)
    def name = new ClassExpression(reference)
    ClassNode infoClass = new MacroClass() {
        class DummyName {
            java.lang.String getName() { $v{ name }.name }
            java.lang.String getVersion() { $v{ vers } }
            java.lang.String getCompiled() { $v{ date } }
        }
    }
    infoClass.name = reference.name + 'Info'
    return infoClass
}
----

NOTE: Types inside the `MacroClass` implementation should be resolved inside, that's why we had to write
`java.lang.String` instead of simply writing `String`. See the documentation for further details.

The result of all this is that if we annotate a `Foo` class with `@Info`,
then it is as if we also typed the following source code in as well:

[source,groovy]
----
class FooInfo {
    String getName() { 'Foo' }
    String getVersion() { '2.5.0' }
    String getCompiled() { 'Mon May 28 02:09:35 AEST 2018' }
}
----

=== AST matching

AST transforms typically add additional ASTNodes into the AST tree
or change some existing subtrees of nodes with a transformed subtree.
Macros are great for allowing us to succinctly describe the added or
transformed subtree of nodes but don't offer us anything for identifying
the subtree which might be the candidate to be replaced.
This is where `ASTMatcher` comes to the rescue. `ASTMatcher` compares
two expressions and each expression can be created with or without macros.

Here's a trivial example of a transformer that looks for binary expressions
of the form `1 + 1` in our code and if it finds it, replaces it with the constant `2`:

[source,groovy]
----
Expression transform(Expression exp) {
    Expression pattern = macro { 1 + 1 }
    if (ASTMatcher.matches(exp, pattern)) {
        return macro { 2 }
    }
    return super.transform(exp)
}
----

This happens at compile time, so speeds up our code at runtime.

We can generalise this a bit more since `ASTMatcher` supports constraints:

[source,groovy]
----
Expression pattern = macro { a + b }.withConstraints {
    placeholder a
    placeholder b
    anyToken()
}
----

The `placeholder` constraint allows any term to appear
and the `anyToken` allows `'-'`, `'*'`, `'/'`, `'<'` and other tokens to replace the `'+'`.

We can make our transform a bit smarter too as follows:

[source,groovy]
----
Expression transform(Expression exp) {
    if (ASTMatcher.matches(exp, pattern)) {
        BinaryExpression be = exp
        Expression lhs = be.leftExpression
        Expression rhs = be.rightExpression
        if (lhs instanceof ConstantExpression && rhs instanceof ConstantExpression) {
            def left = lhs.value
            def right = rhs.value
            if ((left instanceof String && right instanceof String)||
                    (left instanceof Integer && right instanceof Integer)) {
                def op = be.operation.text
                left = left instanceof String ? "'" + left + "'" : left
                right = right instanceof String ? "'" + right + "'" : right
                def result = new GroovyShell().evaluate "$left $op $right"
                return constX(result)
            }
        }
    }
    return exp.transformExpression(this)
}
----

Now we can use our transform, e.g.:

[source,groovy]
----
@SmartOps
class Foo {
    int theAnswer = 40 + 2
    int eight = 4 * 2
    def foobar = 'foo' + 'bar'
    def test() {
        assert 3 < 4
        assert foobar.size() == 6
        assert theAnswer + eight == 25 + 25
    }
}
----

If we look at this class at the end of the SEMANTIC_ANALYSIS phase in the AST
browser, we'll indeed see that the initial value expression for `theAnswer`, `eight` and `foobar`
are binary expressions as is the expression for the first assert and the right-hand side of the `==`
expression for the third assert`. If we move forward to the end of the CANONICALIZATION phase
we'll see that all 5 of those binary expressions are now constant expressions. It is as if we had typed
our source code in as:

[source,groovy]
----
class Foo {
    int theAnswer = 42
    int eight = 8
    def foobar = 'foobar'
    def test() {
        assert true
        assert foobar.size() == 6
        assert theAnswer + eight == 50
    }
}
----

=== Macro methods

The compiler capability to _expand_ macros with their replacements can also be enhanced by your own methods.
Consider the following definition:

[source,groovy]
----
class StringMacroMethods {
    @Macro
    static Expression upper(MacroContext macroContext, ConstantExpression constX) {
        if (constX.value instanceof String) {
            return new ConstantExpression(constX.value.toUpperCase())
        }
        macroContext.sourceUnit.addError(new SyntaxException("Can't use upper with non-String", constX))
    }
}
----

If you register the method in the same way as you would with extension methods (by creating
a reference to the class in a `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionMethods` file).

Now, assuming the META-INF file and class are on your classpath, you can use the `upper` method in
your code such as shown in the following test code:

[source,groovy]
----
assertScript '''
    def foo = upper('Foo')
    assert foo == 'FOO'
'''
def msg = shouldFail '''
    def foo = upper(42)
'''
assert msg.contains("Can't use upper with non-String")
----

It's important to realise that use of `upper` doesn't cause a call to `toUpperCase`
to be embedded in the bytecode but rather causes `toUpperCase` to be called at compile time.

[[Groovy2.5releasenotes-NewAstTransforms]]
== New AST Transformations

* `@AutoFinal` automatically applies the "final"-keyword to every parameter/field
of an annotated class/method/closure/ctor.
(link:https://issues.apache.org/jira/browse/GROOVY-8300[GROOVY-8300]).
* `@AutoImplement` allows you to provide dummy implementations of any abstract
methods that might be inherited from super classes or interfaces
(link:https://issues.apache.org/jira/browse/GROOVY-7860[GROOVY-7860]).
* `@ImmutableBase` checks the validity of an immutable class and makes some preliminary changes
to the class to support immutability.
Normally not used directly but brought in automatically by the `@Immutable` meta annotation.
* `@ImmutableOptions` allows known immutable properties/classes to be declared.
Normally not used directly but brought in automatically by the `@Immutable` meta annotation.
* `@KnownImmutable` is a marker interface used to designate a class as being immutable.
Not usually used explicitly but rather implicitly added via the `@Immutable` meta annotation.
If you create your own Java or Groovy immutable class manually, you can add this annotation
to save you having to list your class as one of the known immutable classes.
* `@MapConstructor` adds a `Map`-based constructor to a class. This allows a usage
style similar to Groovy's named parameters but doesn't use the no-arg constructor
and then call setters. This is useful if you have final properties in your class or you
need the class file to have the `Map` constructor for polyglot integration purposes
(link:https://issues.apache.org/jira/browse/GROOVY-7353[GROOVY-7353]).
* `@NamedDelegate` is a marker interface used to indicate that the property names of the annotated
parameter represent valid key names when using named arguments and that the property types are
applicable for type checking purposes
* `@NamedParam` is a marker interface used to indicate that the name of the annotated parameter
(or specified optional name) is a valid key name when using named arguments
and that the parameter type is applicable for type checking purposes
* `@NamedParams` is the collector annotation for `@NamedParam`
* `@NamedVariant` allows construction of a named-arg equivalent of a method or constructor.
This allows the creation of methods or constructors which can be used with Groovy's named-argument
syntax yet still retain good type checking capabilities.
Combining `@NamedDelegate` and `@NamedParam` when using `@NamedVariant` can be quite powerful.
For example, given these class definitions:
[source,groovy]
----
class Animal {
    String type, name
}

@ToString(includeNames=true)
class Color {
    Integer r, g, b
}

@NamedVariant
String foo(String s1, @NamedParam String s2,
           @NamedDelegate Color shade,
           @NamedDelegate Animal pet) {
    "$s1 $s2 ${pet.type?.toUpperCase()}:$pet.name $shade"
}
----
the constructed foo method will look like:
[source,groovy]
----
String foo(@NamedParam(value = 's2', type = String)
           @NamedParam(value = 'r', type = Integer)
           @NamedParam(value = 'g', type = Integer)
           @NamedParam(value = 'b', type = Integer)
           @NamedParam(value = 'type', type = String)
           @NamedParam(value = 'name', type = String)
           Map __namedArgs, String s1) {
    // some key validation code ...
    return this.foo(s1, __namedArgs.s2,
            ['r': __namedArgs.r, 'g': __namedArgs.g, 'b': __namedArgs.b] as Color,
            ['type': __namedArgs.type, 'name': __namedArgs.name] as Animal)
}
----
* `@PropertyOptions` a marker annotation used to indicate that special property handling code will be generated for this class.
It can be used to override how properties are set within the constructor or accessed via getters. This lets you customize
for instance how `@Immutable` classes are generated.
* `@VisibilityOptions` is a marker annotation used in the context of AST transformations to provide a custom visibility.
One example of its use would be when you want to create a private constructor
that might perhaps be called only from within a static factory method of the class:
[source,groovy]
----
import groovy.transform.*
import static groovy.transform.options.Visibility.PRIVATE

@TupleConstructor
@VisibilityOptions(PRIVATE)
class Person {
    String name
    static makePerson(String first, String last) {
        new Person("$first $last")
    }
}
----
Here using `@TupleConstructor` saves us the effort of writing the typical
boiler-plate code within the constructor but we don't need to make it public
but instead use it within our hand-written factory method which can focus
on our particular business logic relevant to that factory method.

[[Groovy2.5releasenotes-AstTransformImprovements]]
== AST Transformation improvements

* `@Canonical` is now a meta-annotation (link:https://issues.apache.org/jira/browse/GROOVY-6319[GROOVY-6319])
allowing more flexible usage of the annotation attributes from its constituent annotations and allowing
you to define an alternative custom meta-annotation.
`@Canonical` expands into the `@TupleConstructor`, `@EqualsAndHashCode` and `@ToString` annotations.
Any annotation attributes are automatically distributed to the component annotations that support them
as shown in the following example:
[source,groovy]
----
@Canonical(cache = true, useSetters = true, includeNames = true)
class Point {
  int x, y
}
----
is expanded into:
[source,groovy]
----
@ToString(cache = true, includeNames = true)
@TupleConstructor(useSetters = true)
@EqualsAndHashCode(cache = true)
class Point {
  int x, y
}
----
* `@Immutable` is now a meta-annotation (link:https://issues.apache.org/jira/browse/GROOVY-8440[GROOVY-8440])
with the same advantages as described for `@Canonical`. It expands into numerous other annotations as shown
in the following example:
[source,groovy]
----
@Immutable
class Point {
  int x, y
}
----
is equivalent to:
[source,groovy]
----
@ToString(includeSuperProperties = true, cache = true)
@EqualsAndHashCode(cache = true)
@ImmutableBase
@ImmutableOptions
@PropertyOptions(propertyHandler = groovy.transform.options.ImmutablePropertyHandler)
@TupleConstructor(defaults = false)
@MapConstructor(noArg = true, includeSuperProperties = true, includeFields = true)
@KnownImmutable
class Point {
  int x, y
}
----
This might seem like quite a few component annotations but you rarely see
the expanded list and having these annotations gives you fine-grained control
when combing the bits you want, for example you can create a dependency injection
(one constructor only) friendly immutable class by using this combination:
[source,groovy]
----
@ImmutableBase
@PropertyOptions(propertyHandler = ImmutablePropertyHandler)
@Canonical(defaults=false)
class Shopper {
  String first, last
  Date born
  List items
}
----
* `@Immutable` now supports Java's `Optional` container class
(link:https://issues.apache.org/jira/browse/GROOVY-7600[GROOVY-7600]).
* `@Immutable` handles inheritance hierarchies
(link:https://issues.apache.org/jira/browse/GROOVY-7162[GROOVY-7162]).
* `@Immutable` handles JSR-310 `java.time` classes
(link:https://issues.apache.org/jira/browse/GROOVY-7599[GROOVY-7599]).
* `@Delegate` can now be used on getters
(link:https://issues.apache.org/jira/browse/GROOVY-7769[GROOVY-7769]).
* `@TupleConstructor` now supports `pre` and `post` closure conditions to match the functionality provided by `@MapConstructor`
(link:https://issues.apache.org/jira/browse/GROOVY-7769[GROOVY-7769]).
* `@TupleConstructor` and `@Builder` should be able to use defined setters rather than the field directly
(link:https://issues.apache.org/jira/browse/GROOVY-7087[GROOVY-7087]).
* `@Newify` supports an additional attribute that allows selecting the classes whose constructors
can be invoked without the `new` keyword using a regex pattern for the class name:
[source,groovy]
----
@Newify(pattern="[A-Z].*")}
class MyTreeProcessor {
    final myTree = Tree(Tree(Leaf("A"), Leaf("B")), Leaf("C"))
    ...
}
----
* Most annotations check property and field names provided to annotation attributes
(link:https://issues.apache.org/jira/browse/GROOVY-7087[GROOVY-7087]).

[[Groovy2.5releasenotes-Toolimprovements]]
== Tool improvements

Some improvements were made to various tools:

* `groovy` and `groovyConsole` now let you run JUnit 5 tests directly:
[source,groovy]
----
import org.junit.jupiter.api.*
// other imports not shown ...

class MyTest {
  @Test
  void streamSum() {
    assert Stream.of(1, 2, 3).mapToInt{ i -> i }.sum() > 5
  }

  @RepeatedTest(value=2, name = "{displayName} {currentRepetition}/{totalRepetitions}")
  void streamSumRepeated() {
    assert Stream.of(1, 2, 3).mapToInt{i -> i}.sum() == 6
  }

  private boolean isPalindrome(s) { s == s.reverse()  }

  @ParameterizedTest // requires org.junit.jupiter:junit-jupiter-params
  @ValueSource(strings = [ "racecar", "radar", "able was I ere I saw elba" ])
  void palindromes(String candidate) {
    assert isPalindrome(candidate)
  }

  @TestFactory
  def dynamicTestCollection() {[
    dynamicTest("Add test") { -> assert 1 + 1 == 2 },
    dynamicTest("Multiply Test") { -> assert 2 * 3 == 6 }
  ]}
}
----
which when run will show:
[source]
----
JUnit5 launcher: passed=8, failed=0, skipped=0, time=246ms
----
with additional information available via logging if needed.
* `groovysh` offers easier access to grapes
(link:https://issues.apache.org/jira/browse/GROOVY-6514[GROOVY-6514]).
[source,groovy]
----
groovy:000> :grab 'com.google.guava:guava:24.1-jre'
groovy:000> import com.google.common.collect.ImmutableBiMap
===> com.google.common.collect.ImmutableBiMap
groovy:000> m = ImmutableBiMap.of('foo', 'bar')
===> [foo:bar]
groovy:000> m.inverse()
===> [bar:foo]
groovy:000>
----
* `groovyConsole` now provides an ASMifier tab within the AstBrowser
(link:https://issues.apache.org/jira/browse/GROOVY-8091[GROOVY-8091]).

[[Groovy2.5releasenotes-clibuilder]]
== CliBuilder changes

* Groovy's CliBuilder now supports annotation style definitions
(link:https://issues.apache.org/jira/browse/GROOVY-7825[GROOVY-7825]).
* Revamped versions of CliBuilder now exist supporting Commons CLI and Picocli backed implementations. See
this https://blogs.apache.org/logging/entry/groovy-2-5-clibuilder-renewal[blog post] for more details.

[[Groovy2.5releasenotes-OtherImprovements]]
== Other improvements

* Repeated annotation support has been added
* Alternative to `with` called `tap` that has an implicit `return delegate`
(link:https://issues.apache.org/jira/browse/GROOVY-3976[GROOVY-3976]).
* Various JSON customization options are now supported
(link:https://issues.apache.org/jira/browse/GROOVY-3976[GROOVY-6975] and
link:https://issues.apache.org/jira/browse/GROOVY-3976[GROOVY-6854]).
* Method parameter names are now accessible at runtime
(link:https://issues.apache.org/jira/browse/GROOVY-7423[GROOVY-7423]).

[[Groovy2.5releasenotes-Breakingchanges]]
== Breaking changes

A few issues fixed might also be considered breaking changes in some
situations:

* The extension methods for the `java.util.Date` class are now in a separate
`groovy-dateutil` module which isn't included by default when using the `groovy-all`
pom dependency. Add the additional module as a dependency if you need it or consider
migrating to the java.time JSR-310 classes (similar Groovy extension methods exist
for those classes and they are included by default when using the `groovy-all` pom dependency).
* @TupleConstructor could use the order of properties listed in 'includes' when that option is used
(link:https://issues.apache.org/jira/browse/GROOVY-8016[GROOVY-8016])
* @ToString could output properties in a predefined order when 'includes' is used
(link:https://issues.apache.org/jira/browse/GROOVY-8014[GROOVY-8014])
* AstNodeToScriptAdapter should output source using the recommended modifier order
(link:https://issues.apache.org/jira/browse/GROOVY-7967[GROOVY-7967])
* ObjectRange iterator returns null instead of NoSuchElementException
(link:https://issues.apache.org/jira/browse/GROOVY-7961[GROOVY-7961])
* IntRange iterator returns null instead of NoSuchElementException
(link:https://issues.apache.org/jira/browse/GROOVY-7960[GROOVY-7960])
(link:https://issues.apache.org/jira/browse/GROOVY-7937[GROOVY-7937])
* o.c.g.r.t.DefaultTypeTransformation does not apply the right toString on primitive arrays when transforming to String
(link:https://issues.apache.org/jira/browse/GROOVY-7853[GROOVY-7853])
* Remove synchronized methods of groovy.sql.Sql and document it as not thread-safe
(link:https://issues.apache.org/jira/browse/GROOVY-7673[GROOVY-7673])
* InvokerHelper formatting methods have inconsistent API
(link:https://issues.apache.org/jira/browse/GROOVY-7563[GROOVY-7563])
* Fix up transforms (apart from TupleConstructor) which are affected by empty includes default
(link:https://issues.apache.org/jira/browse/GROOVY-7529[GROOVY-7529])
* TupleConstructor with empty includes includes all
(link:https://issues.apache.org/jira/browse/GROOVY-7523[GROOVY-7523])
* TupleConstructor overwrites empty default constructors
(link:https://issues.apache.org/jira/browse/GROOVY-7522[GROOVY-7522])
* ResourceGroovyMethods/NioGroovyMethods BOM behavior is inconsistent
(link:https://issues.apache.org/jira/browse/GROOVY-7465[GROOVY-7465])
* API inconsistency between takeWhile, dropWhile and collectReplacements for CharSequences
(link:https://issues.apache.org/jira/browse/GROOVY-7433[GROOVY-7433])
* @ToString could support non-field properties
(link:https://issues.apache.org/jira/browse/GROOVY-7394[GROOVY-7394])
* same linkedlist code different behavior between groovy and java
(link:https://issues.apache.org/jira/browse/GROOVY-6396[GROOVY-6396])
* CLONE - same linkedlist code different behavior between groovy and java (fix priority of DGM methods vs actual methods on an object)
* Accessing private methods from public ones using categories and inheritance causes MissingMethodException
(link:https://issues.apache.org/jira/browse/GROOVY-6263[GROOVY-6263])
* Have the elvis operator (?:) support the Optional type in Java 8
(link:https://issues.apache.org/jira/browse/GROOVY-6744[GROOVY-6744])
* java.util.Optional should evaluate to false if empty
(link:https://issues.apache.org/jira/browse/GROOVY-7611[GROOVY-7611])
* If you use the FileSystemCompiler class programmatically (rather than via the groovyc commandline) and you use the
part of it for handling commandline processing, then you might notice that it has been converted to picocli and usage
of a handful of methods will throw a DeprecationException which mentions the alternative approach you should use.

[[Groovy2.5releasenotes-Knownissues]]
== Known issues

* The GDK documentation for the java.time extensions wasn't included in the release.
It will be included in the first maintenance release (2.5.1) but can be found in the meantime at:
http://docs.groovy-lang.org/docs/groovy-2.5.1-SNAPSHOT/html/groovy-jdk/
* Users of `groovy.util.CliBuilder` need to also include the `org.codehaus.groovy:groovy-cli-commons`
dependency on their compile classpath in addition to `groovy` or `groovy-all`. This won't be required
after the next maintenance release but users should migrate away from that class in any case as it
will be removed from the next major version of Groovy.
* Users of Spock 1.1-groovy-2.4 may find strange ClassCastException errors with some tests, e.g. with `cleanup:`
clauses. Using Spock 1.2-groovy-2.4-SNAPSHOT from the https://oss.sonatype.org/content/repositories/snapshots/
repo may help but work is on-going to improve Spock support.
* Users combining `final` and `@CompileStatic` or `final` and Spock may see errors from
the final variable analyzer. Work is underway to resolve those error messages.
You may need to temporarily remove the `final` modifier in the meantime.
* Users needing the groovy-xml module and running on JDK9 and above may need to use
the `--add-modules java.xml.bind` command-line option to fix the break in backwards
compatibility caused by JDK9+.
* JDK9+ produces warnings with many libraries including Groovy due to some planned
future restrictions in the JDK. Work is underway to re-engineer parts of Groovy
to reduce/remove those warnings. Users wanting to hush the warnings as an interim
measure may consider using the `--add-opens` escape clause offered by JDK9+.
See commit `92bd96f` (currently reverted) on the Groovy master branch for a potential
list to add.
* Users of the Spring Boot Gradle plugin wanting to upgrade to Groovy 2.5.0
might like to re-check the plugin doco if you have any troubles. More comments here:
https://github.com/spring-projects/spring-boot/issues/13444

[[Groovy2.5releasenotes-JDKrequirements]]
== JDK requirements changes

Groovy 2.5 requires JDK8+ to build and JDK7 is the minimum version of the JRE that we support.

[[Groovy2.5releasenotes-Moreinformation]]
== More information

You can browse all the link:../changelogs/changelog-2.5.0.html[tickets closed for Groovy 2.5.0 in JIRA].
<commit_msg>GROOVY-8666, GROOVY-8644: improve 2.5 release notes around packaging/JDK9<commit_after>Groovy 2.5 comes with a host of new features and capabilities.

[[Groovy2.5releasenotes-Packaging]]

== Jar packaging and JDK9 support

Groovy versions prior to 2.5 included a core groovy jar and
numerous "module" jars, e.g. groovy-xml.jar. Note: Groovy's
existing "modules" aren't (currently) the same as Java9+ modules (more on that later).

We also provided a convenience "all" jar by jarjar'ing the core and
"module" jars. As part of moving to better JDK9+ support,
the "all" jar is no longer available but we do provide an
"all" pom which brings in the equivalent component jars.

For most users referencing Groovy's "all" dependency, simply
bumping the version number should be sufficient for you to
get all the same class files and transitive dependencies as before.
For users with complex builds, which might (perhaps implcitly)
reference the actual "all" jar itself, perhaps more remediation
work might be required. There are two caveats:

* please read the breaking changes section for unrelated changes
to the included classes referenced by the "all" pom
* also note that since the current jars don't comply fully
yet with JDK9+ requirements (e.g. split package requirements)
you may not be able to place the Groovy jars on your module path.
Instead, place them on the classpath. On-going work for
Groovy 3.0 will address split package issues (see https://issues.apache.org/jira/browse/GROOVY-8647). If the
Groovy "modules" you want to use aren't in the list being rectified for Groovy 3.0, then
you can probably use them on the module path. The Groovy build currently
doesn't test such usage.

[[Groovy2.5releasenotes-Macros]]

== Macro support

Groovy is well known for its extensible compilation process.
The compiler supports a compile-time metaprogramming extension mechanism
which allows the compilation process to be enhanced with new functionality.
The mechanism allows the definition and execution of AST transformations at
various stages of the compilation process.

Groovy comes with a number of built-in AST transformations and, importantly,
you can write your own. To write such transformations until now, you have
had to become familiar with the compiler's internal representation of
Groovy syntactic structures. But that has now changed.

Groovy 2.5+ supports macros. Macros let you use Groovy syntax directly
rather than using the internal compiler representations when creating
compile-time metaprogramming extensions. This puts the creation of transformations
in the hands of all Groovy programmers not just Groovy compiler gurus.

=== Expressions and statements

Suppose we want to create an `@Info` transform which when placed on a class
creates a `getInfo()` method. Calling the `getInfo()` method will display some
information about the class. The traditional way to write the transformation
code might be something like:
[source,groovy]
----
...
def clazz = new MethodCallExpression(new VariableExpression("this"), "getClass", EMPTY_ARGUMENTS)
def body = new ExpressionStatement(new MethodCallExpression(clazz, "getName", EMPTY_ARGUMENTS))
classNode.addMethod('getInfo', ACC_PUBLIC, STRING_TYPE, EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, body)
...
----

This will print out the class name when the `getInfo()` method is called.

With macros, the first two lines become:

[source,groovy]
----
def body = macro(true) { getClass().name }
----

Creation of both expressions and statements are supported. The `true` parameter
to the `macro` method ensures we get a statement here which is what `addMethod` needs.

Frequently you will want to receive parameters or resolve surrounding variables.
This is supported using a special `$v` notation. Suppose we want to augment the `getInfo`
method to additionally output the time the class was compiled and the Groovy version used.
Our code might look like this:

[source,groovy]
----
def date = new ConstantExpression(new Date().toString())
def version = new ConstantExpression(GroovySystem.version)
def body = macro(true) {
    """\
    Name: ${getClass().name}
    Compiled: ${$v{ date }}
    Using Groovy version: ${$v{ version }}""".stripIndent()
}
----

What the macro does is convert any Groovy code inside the closure
argument into its internal representation. When it gets to the
`$v` placeholders it does no conversion and just inserts
those sections directly. This is exactly what we want here since
we wrote those parts old-school style using the internal
AST data structures.

=== Macro classes

In addition to the ability to create statements and expressions, as supported by `macro`,
you might want to create whole classes. The `MacroClass` capability allows you to do that.
You supply the class you are wanting to create in a similar fashion to creating an anonymous
inner class in Java. The following example shows the approach for a scenario where you
might want to extract the information collected in previous examples in a class of its own:

[source,groovy]
----
ClassNode buildInfoClass(ClassNode reference) {
    def date = new ConstantExpression(new Date().toString())
    def vers = new ConstantExpression(GroovySystem.version)
    def name = new ClassExpression(reference)
    ClassNode infoClass = new MacroClass() {
        class DummyName {
            java.lang.String getName() { $v{ name }.name }
            java.lang.String getVersion() { $v{ vers } }
            java.lang.String getCompiled() { $v{ date } }
        }
    }
    infoClass.name = reference.name + 'Info'
    return infoClass
}
----

NOTE: Types inside the `MacroClass` implementation should be resolved inside, that's why we had to write
`java.lang.String` instead of simply writing `String`. See the documentation for further details.

The result of all this is that if we annotate a `Foo` class with `@Info`,
then it is as if we also typed the following source code in as well:

[source,groovy]
----
class FooInfo {
    String getName() { 'Foo' }
    String getVersion() { '2.5.0' }
    String getCompiled() { 'Mon May 28 02:09:35 AEST 2018' }
}
----

=== AST matching

AST transforms typically add additional ASTNodes into the AST tree
or change some existing subtrees of nodes with a transformed subtree.
Macros are great for allowing us to succinctly describe the added or
transformed subtree of nodes but don't offer us anything for identifying
the subtree which might be the candidate to be replaced.
This is where `ASTMatcher` comes to the rescue. `ASTMatcher` compares
two expressions and each expression can be created with or without macros.

Here's a trivial example of a transformer that looks for binary expressions
of the form `1 + 1` in our code and if it finds it, replaces it with the constant `2`:

[source,groovy]
----
Expression transform(Expression exp) {
    Expression pattern = macro { 1 + 1 }
    if (ASTMatcher.matches(exp, pattern)) {
        return macro { 2 }
    }
    return super.transform(exp)
}
----

This happens at compile time, so speeds up our code at runtime.

We can generalise this a bit more since `ASTMatcher` supports constraints:

[source,groovy]
----
Expression pattern = macro { a + b }.withConstraints {
    placeholder a
    placeholder b
    anyToken()
}
----

The `placeholder` constraint allows any term to appear
and the `anyToken` allows `'-'`, `'*'`, `'/'`, `'<'` and other tokens to replace the `'+'`.

We can make our transform a bit smarter too as follows:

[source,groovy]
----
Expression transform(Expression exp) {
    if (ASTMatcher.matches(exp, pattern)) {
        BinaryExpression be = exp
        Expression lhs = be.leftExpression
        Expression rhs = be.rightExpression
        if (lhs instanceof ConstantExpression && rhs instanceof ConstantExpression) {
            def left = lhs.value
            def right = rhs.value
            if ((left instanceof String && right instanceof String)||
                    (left instanceof Integer && right instanceof Integer)) {
                def op = be.operation.text
                left = left instanceof String ? "'" + left + "'" : left
                right = right instanceof String ? "'" + right + "'" : right
                def result = new GroovyShell().evaluate "$left $op $right"
                return constX(result)
            }
        }
    }
    return exp.transformExpression(this)
}
----

Now we can use our transform, e.g.:

[source,groovy]
----
@SmartOps
class Foo {
    int theAnswer = 40 + 2
    int eight = 4 * 2
    def foobar = 'foo' + 'bar'
    def test() {
        assert 3 < 4
        assert foobar.size() == 6
        assert theAnswer + eight == 25 + 25
    }
}
----

If we look at this class at the end of the SEMANTIC_ANALYSIS phase in the AST
browser, we'll indeed see that the initial value expression for `theAnswer`, `eight` and `foobar`
are binary expressions as is the expression for the first assert and the right-hand side of the `==`
expression for the third assert`. If we move forward to the end of the CANONICALIZATION phase
we'll see that all 5 of those binary expressions are now constant expressions. It is as if we had typed
our source code in as:

[source,groovy]
----
class Foo {
    int theAnswer = 42
    int eight = 8
    def foobar = 'foobar'
    def test() {
        assert true
        assert foobar.size() == 6
        assert theAnswer + eight == 50
    }
}
----

=== Macro methods

The compiler capability to _expand_ macros with their replacements can also be enhanced by your own methods.
Consider the following definition:

[source,groovy]
----
class StringMacroMethods {
    @Macro
    static Expression upper(MacroContext macroContext, ConstantExpression constX) {
        if (constX.value instanceof String) {
            return new ConstantExpression(constX.value.toUpperCase())
        }
        macroContext.sourceUnit.addError(new SyntaxException("Can't use upper with non-String", constX))
    }
}
----

If you register the method in the same way as you would with extension methods (by creating
a reference to the class in a `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionMethods` file).

Now, assuming the META-INF file and class are on your classpath, you can use the `upper` method in
your code such as shown in the following test code:

[source,groovy]
----
assertScript '''
    def foo = upper('Foo')
    assert foo == 'FOO'
'''
def msg = shouldFail '''
    def foo = upper(42)
'''
assert msg.contains("Can't use upper with non-String")
----

It's important to realise that use of `upper` doesn't cause a call to `toUpperCase`
to be embedded in the bytecode but rather causes `toUpperCase` to be called at compile time.

[[Groovy2.5releasenotes-NewAstTransforms]]
== New AST Transformations

* `@AutoFinal` automatically applies the "final"-keyword to every parameter/field
of an annotated class/method/closure/ctor.
(link:https://issues.apache.org/jira/browse/GROOVY-8300[GROOVY-8300]).
* `@AutoImplement` allows you to provide dummy implementations of any abstract
methods that might be inherited from super classes or interfaces
(link:https://issues.apache.org/jira/browse/GROOVY-7860[GROOVY-7860]).
* `@ImmutableBase` checks the validity of an immutable class and makes some preliminary changes
to the class to support immutability.
Normally not used directly but brought in automatically by the `@Immutable` meta annotation.
* `@ImmutableOptions` allows known immutable properties/classes to be declared.
Normally not used directly but brought in automatically by the `@Immutable` meta annotation.
* `@KnownImmutable` is a marker interface used to designate a class as being immutable.
Not usually used explicitly but rather implicitly added via the `@Immutable` meta annotation.
If you create your own Java or Groovy immutable class manually, you can add this annotation
to save you having to list your class as one of the known immutable classes.
* `@MapConstructor` adds a `Map`-based constructor to a class. This allows a usage
style similar to Groovy's named parameters but doesn't use the no-arg constructor
and then call setters. This is useful if you have final properties in your class or you
need the class file to have the `Map` constructor for polyglot integration purposes
(link:https://issues.apache.org/jira/browse/GROOVY-7353[GROOVY-7353]).
* `@NamedDelegate` is a marker interface used to indicate that the property names of the annotated
parameter represent valid key names when using named arguments and that the property types are
applicable for type checking purposes
* `@NamedParam` is a marker interface used to indicate that the name of the annotated parameter
(or specified optional name) is a valid key name when using named arguments
and that the parameter type is applicable for type checking purposes
* `@NamedParams` is the collector annotation for `@NamedParam`
* `@NamedVariant` allows construction of a named-arg equivalent of a method or constructor.
This allows the creation of methods or constructors which can be used with Groovy's named-argument
syntax yet still retain good type checking capabilities.
Combining `@NamedDelegate` and `@NamedParam` when using `@NamedVariant` can be quite powerful.
For example, given these class definitions:
[source,groovy]
----
class Animal {
    String type, name
}

@ToString(includeNames=true)
class Color {
    Integer r, g, b
}

@NamedVariant
String foo(String s1, @NamedParam String s2,
           @NamedDelegate Color shade,
           @NamedDelegate Animal pet) {
    "$s1 $s2 ${pet.type?.toUpperCase()}:$pet.name $shade"
}
----
the constructed foo method will look like:
[source,groovy]
----
String foo(@NamedParam(value = 's2', type = String)
           @NamedParam(value = 'r', type = Integer)
           @NamedParam(value = 'g', type = Integer)
           @NamedParam(value = 'b', type = Integer)
           @NamedParam(value = 'type', type = String)
           @NamedParam(value = 'name', type = String)
           Map __namedArgs, String s1) {
    // some key validation code ...
    return this.foo(s1, __namedArgs.s2,
            ['r': __namedArgs.r, 'g': __namedArgs.g, 'b': __namedArgs.b] as Color,
            ['type': __namedArgs.type, 'name': __namedArgs.name] as Animal)
}
----
* `@PropertyOptions` a marker annotation used to indicate that special property handling code will be generated for this class.
It can be used to override how properties are set within the constructor or accessed via getters. This lets you customize
for instance how `@Immutable` classes are generated.
* `@VisibilityOptions` is a marker annotation used in the context of AST transformations to provide a custom visibility.
One example of its use would be when you want to create a private constructor
that might perhaps be called only from within a static factory method of the class:
[source,groovy]
----
import groovy.transform.*
import static groovy.transform.options.Visibility.PRIVATE

@TupleConstructor
@VisibilityOptions(PRIVATE)
class Person {
    String name
    static makePerson(String first, String last) {
        new Person("$first $last")
    }
}
----
Here using `@TupleConstructor` saves us the effort of writing the typical
boiler-plate code within the constructor but we don't need to make it public
but instead use it within our hand-written factory method which can focus
on our particular business logic relevant to that factory method.

[[Groovy2.5releasenotes-AstTransformImprovements]]
== AST Transformation improvements

* `@Canonical` is now a meta-annotation (link:https://issues.apache.org/jira/browse/GROOVY-6319[GROOVY-6319])
allowing more flexible usage of the annotation attributes from its constituent annotations and allowing
you to define an alternative custom meta-annotation.
`@Canonical` expands into the `@TupleConstructor`, `@EqualsAndHashCode` and `@ToString` annotations.
Any annotation attributes are automatically distributed to the component annotations that support them
as shown in the following example:
[source,groovy]
----
@Canonical(cache = true, useSetters = true, includeNames = true)
class Point {
  int x, y
}
----
is expanded into:
[source,groovy]
----
@ToString(cache = true, includeNames = true)
@TupleConstructor(useSetters = true)
@EqualsAndHashCode(cache = true)
class Point {
  int x, y
}
----
* `@Immutable` is now a meta-annotation (link:https://issues.apache.org/jira/browse/GROOVY-8440[GROOVY-8440])
with the same advantages as described for `@Canonical`. It expands into numerous other annotations as shown
in the following example:
[source,groovy]
----
@Immutable
class Point {
  int x, y
}
----
is equivalent to:
[source,groovy]
----
@ToString(includeSuperProperties = true, cache = true)
@EqualsAndHashCode(cache = true)
@ImmutableBase
@ImmutableOptions
@PropertyOptions(propertyHandler = groovy.transform.options.ImmutablePropertyHandler)
@TupleConstructor(defaults = false)
@MapConstructor(noArg = true, includeSuperProperties = true, includeFields = true)
@KnownImmutable
class Point {
  int x, y
}
----
This might seem like quite a few component annotations but you rarely see
the expanded list and having these annotations gives you fine-grained control
when combing the bits you want, for example you can create a dependency injection
(one constructor only) friendly immutable class by using this combination:
[source,groovy]
----
@ImmutableBase
@PropertyOptions(propertyHandler = ImmutablePropertyHandler)
@Canonical(defaults=false)
class Shopper {
  String first, last
  Date born
  List items
}
----
* `@Immutable` now supports Java's `Optional` container class
(link:https://issues.apache.org/jira/browse/GROOVY-7600[GROOVY-7600]).
* `@Immutable` handles inheritance hierarchies
(link:https://issues.apache.org/jira/browse/GROOVY-7162[GROOVY-7162]).
* `@Immutable` handles JSR-310 `java.time` classes
(link:https://issues.apache.org/jira/browse/GROOVY-7599[GROOVY-7599]).
* `@Delegate` can now be used on getters
(link:https://issues.apache.org/jira/browse/GROOVY-7769[GROOVY-7769]).
* `@TupleConstructor` now supports `pre` and `post` closure conditions to match the functionality provided by `@MapConstructor`
(link:https://issues.apache.org/jira/browse/GROOVY-7769[GROOVY-7769]).
* `@TupleConstructor` and `@Builder` should be able to use defined setters rather than the field directly
(link:https://issues.apache.org/jira/browse/GROOVY-7087[GROOVY-7087]).
* `@Newify` supports an additional attribute that allows selecting the classes whose constructors
can be invoked without the `new` keyword using a regex pattern for the class name:
[source,groovy]
----
@Newify(pattern="[A-Z].*")}
class MyTreeProcessor {
    final myTree = Tree(Tree(Leaf("A"), Leaf("B")), Leaf("C"))
    ...
}
----
* Most annotations check property and field names provided to annotation attributes
(link:https://issues.apache.org/jira/browse/GROOVY-7087[GROOVY-7087]).

[[Groovy2.5releasenotes-Toolimprovements]]
== Tool improvements

Some improvements were made to various tools:

* `groovy` and `groovyConsole` now let you run JUnit 5 tests directly:
[source,groovy]
----
import org.junit.jupiter.api.*
// other imports not shown ...

class MyTest {
  @Test
  void streamSum() {
    assert Stream.of(1, 2, 3).mapToInt{ i -> i }.sum() > 5
  }

  @RepeatedTest(value=2, name = "{displayName} {currentRepetition}/{totalRepetitions}")
  void streamSumRepeated() {
    assert Stream.of(1, 2, 3).mapToInt{i -> i}.sum() == 6
  }

  private boolean isPalindrome(s) { s == s.reverse()  }

  @ParameterizedTest // requires org.junit.jupiter:junit-jupiter-params
  @ValueSource(strings = [ "racecar", "radar", "able was I ere I saw elba" ])
  void palindromes(String candidate) {
    assert isPalindrome(candidate)
  }

  @TestFactory
  def dynamicTestCollection() {[
    dynamicTest("Add test") { -> assert 1 + 1 == 2 },
    dynamicTest("Multiply Test") { -> assert 2 * 3 == 6 }
  ]}
}
----
which when run will show:
[source]
----
JUnit5 launcher: passed=8, failed=0, skipped=0, time=246ms
----
with additional information available via logging if needed.
* `groovysh` offers easier access to grapes
(link:https://issues.apache.org/jira/browse/GROOVY-6514[GROOVY-6514]).
[source,groovy]
----
groovy:000> :grab 'com.google.guava:guava:24.1-jre'
groovy:000> import com.google.common.collect.ImmutableBiMap
===> com.google.common.collect.ImmutableBiMap
groovy:000> m = ImmutableBiMap.of('foo', 'bar')
===> [foo:bar]
groovy:000> m.inverse()
===> [bar:foo]
groovy:000>
----
* `groovyConsole` now provides an ASMifier tab within the AstBrowser
(link:https://issues.apache.org/jira/browse/GROOVY-8091[GROOVY-8091]).

[[Groovy2.5releasenotes-clibuilder]]
== CliBuilder changes

* Groovy's CliBuilder now supports annotation style definitions
(link:https://issues.apache.org/jira/browse/GROOVY-7825[GROOVY-7825]).
* Revamped versions of CliBuilder now exist supporting Commons CLI and Picocli backed implementations. See
this https://blogs.apache.org/logging/entry/groovy-2-5-clibuilder-renewal[blog post] for more details.

[[Groovy2.5releasenotes-OtherImprovements]]
== Other improvements

* Repeated annotation support has been added
* Alternative to `with` called `tap` that has an implicit `return delegate`
(link:https://issues.apache.org/jira/browse/GROOVY-3976[GROOVY-3976]).
* Various JSON customization options are now supported
(link:https://issues.apache.org/jira/browse/GROOVY-3976[GROOVY-6975] and
link:https://issues.apache.org/jira/browse/GROOVY-3976[GROOVY-6854]).
* Method parameter names are now accessible at runtime
(link:https://issues.apache.org/jira/browse/GROOVY-7423[GROOVY-7423]).

[[Groovy2.5releasenotes-Breakingchanges]]
== Breaking changes

A few issues fixed might also be considered breaking changes in some
situations:

* The extension methods for the `java.util.Date` class are now in a separate
`groovy-dateutil` module which isn't included by default when using the `groovy-all`
pom dependency. Add the additional module as a dependency if you need it or consider
migrating to the java.time JSR-310 classes (similar Groovy extension methods exist
for those classes and they are included by default when using the `groovy-all` pom dependency).
* @TupleConstructor could use the order of properties listed in 'includes' when that option is used
(link:https://issues.apache.org/jira/browse/GROOVY-8016[GROOVY-8016])
* @ToString could output properties in a predefined order when 'includes' is used
(link:https://issues.apache.org/jira/browse/GROOVY-8014[GROOVY-8014])
* AstNodeToScriptAdapter should output source using the recommended modifier order
(link:https://issues.apache.org/jira/browse/GROOVY-7967[GROOVY-7967])
* ObjectRange iterator returns null instead of NoSuchElementException
(link:https://issues.apache.org/jira/browse/GROOVY-7961[GROOVY-7961])
* IntRange iterator returns null instead of NoSuchElementException
(link:https://issues.apache.org/jira/browse/GROOVY-7960[GROOVY-7960])
(link:https://issues.apache.org/jira/browse/GROOVY-7937[GROOVY-7937])
* o.c.g.r.t.DefaultTypeTransformation does not apply the right toString on primitive arrays when transforming to String
(link:https://issues.apache.org/jira/browse/GROOVY-7853[GROOVY-7853])
* Remove synchronized methods of groovy.sql.Sql and document it as not thread-safe
(link:https://issues.apache.org/jira/browse/GROOVY-7673[GROOVY-7673])
* InvokerHelper formatting methods have inconsistent API
(link:https://issues.apache.org/jira/browse/GROOVY-7563[GROOVY-7563])
* Fix up transforms (apart from TupleConstructor) which are affected by empty includes default
(link:https://issues.apache.org/jira/browse/GROOVY-7529[GROOVY-7529])
* TupleConstructor with empty includes includes all
(link:https://issues.apache.org/jira/browse/GROOVY-7523[GROOVY-7523])
* TupleConstructor overwrites empty default constructors
(link:https://issues.apache.org/jira/browse/GROOVY-7522[GROOVY-7522])
* ResourceGroovyMethods/NioGroovyMethods BOM behavior is inconsistent
(link:https://issues.apache.org/jira/browse/GROOVY-7465[GROOVY-7465])
* API inconsistency between takeWhile, dropWhile and collectReplacements for CharSequences
(link:https://issues.apache.org/jira/browse/GROOVY-7433[GROOVY-7433])
* @ToString could support non-field properties
(link:https://issues.apache.org/jira/browse/GROOVY-7394[GROOVY-7394])
* same linkedlist code different behavior between groovy and java
(link:https://issues.apache.org/jira/browse/GROOVY-6396[GROOVY-6396])
* CLONE - same linkedlist code different behavior between groovy and java (fix priority of DGM methods vs actual methods on an object)
* Accessing private methods from public ones using categories and inheritance causes MissingMethodException
(link:https://issues.apache.org/jira/browse/GROOVY-6263[GROOVY-6263])
* Have the elvis operator (?:) support the Optional type in Java 8
(link:https://issues.apache.org/jira/browse/GROOVY-6744[GROOVY-6744])
* java.util.Optional should evaluate to false if empty
(link:https://issues.apache.org/jira/browse/GROOVY-7611[GROOVY-7611])
* If you use the FileSystemCompiler class programmatically (rather than via the groovyc commandline) and you use the
part of it for handling commandline processing, then you might notice that it has been converted to picocli and usage
of a handful of methods will throw a DeprecationException which mentions the alternative approach you should use.

[[Groovy2.5releasenotes-Knownissues]]
== Known issues

* The GDK documentation for the java.time extensions wasn't included in the release.
It will be included in the first maintenance release (2.5.1) but can be found in the meantime at:
http://docs.groovy-lang.org/docs/groovy-2.5.1-SNAPSHOT/html/groovy-jdk/
* Users of `groovy.util.CliBuilder` need to also include the `org.codehaus.groovy:groovy-cli-commons`
dependency on their compile classpath in addition to `groovy` or `groovy-all`. This won't be required
after the next maintenance release but users should migrate away from that class in any case as it
will be removed from the next major version of Groovy.
* Users of Spock 1.1-groovy-2.4 may find strange ClassCastException errors with some tests, e.g. with `cleanup:`
clauses. Using Spock 1.2-groovy-2.4-SNAPSHOT from the https://oss.sonatype.org/content/repositories/snapshots/
repo may help but work is on-going to improve Spock support.
* Users combining `final` and `@CompileStatic` or `final` and Spock may see errors from
the final variable analyzer. Work is underway to resolve those error messages.
You may need to temporarily remove the `final` modifier in the meantime.
* Users needing the groovy-xml module and running on JDK9 and above may need to use
the `--add-modules java.xml.bind` command-line option to fix the break in backwards
compatibility caused by JDK9+.
* JDK9+ produces warnings with many libraries including Groovy due to some planned
future restrictions in the JDK. Work is underway to re-engineer parts of Groovy
to reduce/remove those warnings. Users wanting to hush the warnings as an interim
measure may consider using the `--add-opens` escape clause offered by JDK9+.
See commit `92bd96f` (currently reverted) on the Groovy master branch for a potential
list to add.
* Users of the Spring Boot Gradle plugin wanting to upgrade to Groovy 2.5.0
might like to re-check the plugin doco if you have any troubles. More comments here:
https://github.com/spring-projects/spring-boot/issues/13444

[[Groovy2.5releasenotes-JDKrequirements]]
== JDK requirements changes

Groovy 2.5 requires JDK8+ to build and JDK7 is the minimum version of the JRE that we support.

[[Groovy2.5releasenotes-Moreinformation]]
== More information

You can browse all the link:../changelogs/changelog-2.5.0.html[tickets closed for Groovy 2.5.0 in JIRA].

{'fraction_non_alphanumeric': 0.09407982756057678, 'fraction_numerical': 0.01789430652594024, 'mean_word_length': 4.271008814887366, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 29, 'https://': 83, 'lorem ipsum': 0, 'www.': 0, 'xml': 5}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1777362', 'n_tokens_mistral': 16532, 'n_tokens_neox': 15290, 'n_words': 6055}