http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/AutoFinal.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/AutoFinal.java b/src/main/groovy/groovy/transform/AutoFinal.java new file mode 100644 index 0000000..58ce3ba --- /dev/null +++ b/src/main/groovy/groovy/transform/AutoFinal.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.control.CompilerConfiguration; +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to automatically add the final qualifier to method, constructor, + * and closure parameters. + * <p>The annotation can be placed at the class level in which case it applies to + * all methods, constructors, and closures within the class and any inner classes. + * It can also be applied to an individual method, constructor, field with a + * Closure initial value or a Closure assigned to a local variable. In the case + * of fields (or local variables) it only adjusts the parameters of the referenced + * Closure not the field (or local variable) itself. + * <p>If you wish to automatically apply the + * annotation to all classes of a project, consider using + * {@code groovyc --configscript}. Google "Customising The Groovy Compiler", + * or see {@link CompilerConfiguration} for further details. + * This will ensure that all arguments will automatically be final, + * completely eliminating the need to clutter the code with final keywords + * in any parameter list. + * <p> + * <em>Example usage:</em> + * <pre class="groovyTestCase"> + * {@code @groovy.transform.AutoFinal} + * class Person { + * final String first, last + * Person(String first, String last) { + * this.first = first + * this.last = last + * } + * String fullName(boolean reversed = false, String separator = ' ') { + * final concatCls = { String n0, String n1 -> "$n0$separator$n1" } + * concatCls(reversed ? last : first, reversed ? first : last) + * } + * } + * + * final js = new Person('John', 'Smith') + * assert js.fullName() == 'John Smith' + * assert js.fullName(true, ', ') == 'Smith, John' + * </pre> + * for this case, the constructor for the <code>Person</code> class will be + * equivalent to the following code: + * <pre> + * Person(final String first, final String last) { + * ... + * } + * </pre> + * And after normal default parameter processing takes place, the following overloaded methods will exist: + * <pre> + * String fullName(final boolean reversed, final String separator) { ... } + * String fullName(final boolean reversed) { fullName(reversed, ' ') } + * String fullName() { fullName(false) } + * </pre> + * and the closure will have become: + * <pre> + * { final String n0, final String n1 -> "$n0$separator$n1" } + * </pre> + * + * @since 2.5.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.AutoFinalASTTransformation") +public @interface AutoFinal { + /** + * Indicates that adding final to parameters should not be applied on this node. + * <p>Normally not required since leaving off the annotation will achieve the same affect. + * However, it can be useful for selectively disabling this annotation in just a small part + * of an otherwise annotated class. As an example, it would make sense to set this to {@code false} on + * a method which altered parameters in a class already marked as {@code @AutoFinal}. + * All nodes in the class except that single method would be processed. + */ + boolean enabled() default true; +}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/AutoImplement.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/AutoImplement.java b/src/main/groovy/groovy/transform/AutoImplement.java new file mode 100644 index 0000000..57e8152 --- /dev/null +++ b/src/main/groovy/groovy/transform/AutoImplement.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Class annotation used to provide default dummy methods for a class extending an abstract super class or + * implementing one or more interfaces. + * <p> + * Example usage: + * <pre> + * import groovy.transform.AutoImplement + * + * {@code @AutoImplement} + * class EmptyStringIterator implements Iterator<String> { + * boolean hasNext() { false } + * } + * + * assert !new EmptyStringIterator().hasNext() + * </pre> + * In the above example, since {@code hasNext} returns false, the {@code next} method + * should never be called, so any dummy implementation would do for {@code next}. + * The "empty" implementation provided by default when using {@code @AutoImplement} + * will suffice - which effectively returns {@code null} in Groovy for non-void, + * non-primitive methods. + * + * As a point of interest, the default implementation for methods returning primitive + * types is to return the default value (which incidentally never satisfies Groovy truth). + * For {@code boolean} this means returning {@code false}, so for the above example we + * could have (albeit perhaps less instructive of our intent) by just using: + * <pre> + * {@code @AutoImplement} + * class EmptyStringIterator implements Iterator<String> { } + * </pre> + * If we didn't want to assume that callers of our {@code EmptyStringIterator} correctly followed + * the {@code Iterator} contract, then we might want to guard against inappropriate calls to {@code next}. + * Rather than just returning {@code null}, we might want to throw an exception. This is easily done using + * the {@code exception} annotation attribute as shown below: + * <pre> + * import groovy.transform.AutoImplement + * import static groovy.test.GroovyAssert.shouldFail + * + * {@code @AutoImplement}(exception=UnsupportedOperationException) + * class EmptyStringIterator implements Iterator<String> { + * boolean hasNext() { false } + * } + * + * shouldFail(UnsupportedOperationException) { + * new EmptyStringIterator().next() + * } + * </pre> + * All implemented methods will throw an instance of this exception constructed using its no-arg constructor. + * + * You can also supply a single {@code message} annotation attribute in which case the message will be passed + * as an argument during exception construction as shown in the following example: + * <pre> + * {@code @AutoImplement}(exception=UnsupportedOperationException, message='Not supported for this empty iterator') + * class EmptyStringIterator implements Iterator<String> { + * boolean hasNext() { false } + * } + * + * def ex = shouldFail(UnsupportedOperationException) { + * new EmptyStringIterator().next() + * } + * assert ex.message == 'Not supported for this empty iterator' + * </pre> + * Finally, you can alternatively supply a {@code code} annotation attribute in which case a closure + * block can be supplied which should contain the code to execute for all implemented methods. This can be + * seen in the following example: + * <pre> + * {@code @AutoImplement}(code = { throw new UnsupportedOperationException("Not supported for ${getClass().simpleName}") }) + * class EmptyStringIterator implements Iterator<String> { + * boolean hasNext() { false } + * } + * + * def ex = shouldFail(UnsupportedOperationException) { + * new EmptyStringIterator().next() + * } + * assert ex.message == 'Not supported for EmptyStringIterator' + * </pre> + * + * @since 2.5.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.AutoImplementASTTransformation") +public @interface AutoImplement { + /** + * If defined, all unimplemented methods will throw this exception. + * Will be ignored if {@code code} is defined. + */ + Class<? extends RuntimeException> exception() default Undefined.EXCEPTION.class; + + /** + * If {@code exception} is defined, {@code message} can be used to specify the exception message. + * Will be ignored if {@code code} is defined or {@code exception} isn't defined. + */ + String message() default Undefined.STRING; + + /** + * If defined, all unimplemented methods will execute the code found within the supplied closure. + */ + Class code() default Undefined.CLASS.class; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/BaseScript.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/BaseScript.java b/src/main/groovy/groovy/transform/BaseScript.java new file mode 100644 index 0000000..2720b79 --- /dev/null +++ b/src/main/groovy/groovy/transform/BaseScript.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import groovy.lang.Script; +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Variable annotation used for changing the base script class of the current script. + * <p> + * The type of the variable annotated with {@code @BaseScript} must extend {@link groovy.lang.Script}. + * It will be used as the base script class. + * The annotated variable will become shortcut to <code>this</code> object. + * Using this annotation will override base script set by Groovy compiler or + * {@link org.codehaus.groovy.control.CompilerConfiguration} of {@link groovy.lang.GroovyShell} + * Example usage: + * <pre> + * abstract class CustomScript extends Script { + * int getTheMeaningOfLife() { 42 } + * } + * + * @BaseScript CustomScript baseScript + * + * assert baseScript == this + * assert theMeaningOfLife == 42 + * assert theMeaningOfLife == baseScript.theMeaningOfLife + * </pre> + * In this example, the base script of the current script will be changed to + * <code>CustomScript</code> allowing usage of <code>getTheMeaningOfLife()</code> + * method. <code>baseScript</code> variable will become typed shortcut for + * <code>this</code> object which enables better IDE support. + * </p><p> + * The custom base script may implement the run() method and specify a different + * method name to be used for the script body by declaring a single abstract method. + * For example: + * <pre> + * abstract class CustomScriptBodyMethod extends Script { + * abstract def runScript() + * def preRun() { println "preRunning" } + * def postRun() { println "postRunning" } + * def run() { + * preRun() + * try { + * 3.times { runScript() } + * } finally { + * postRun() + * } + * } + * } + * + * {@code @BaseScript} CustomScriptBodyMethod baseScript + * println "Script body run" + * </pre> + * That will produce the following output: + * <pre> + * preRunning + * Script body run + * Script body run + * Script body run + * postRunning + * </pre> + * + * Note that while you can declare arguments for the script body's method, as + * the AST is currently implemented they are not accessible in the script body code. + * </p> + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * // Simple Car class to save state and distance. + * class Car { + * String state + * Long distance = 0 + * } + * + * // Custom Script with methods that change the Car's state. + * // The Car object is passed via the binding. + * abstract class CarScript extends Script { + * def start() { + * this.binding.car.state = 'started' + * } + * + * def stop() { + * this.binding.car.state = 'stopped' + * } + * + * def drive(distance) { + * this.binding.car.distance += distance + * } + * } + * + * + * // Define Car object here, so we can use it in assertions later on. + * def car = new Car() + * // Add to script binding (CarScript references this.binding.car). + * def binding = new Binding(car: car) + * + * // Configure the GroovyShell. + * def shell = new GroovyShell(this.class.classLoader, binding) + * + * // Simple DSL to start, drive and stop the car. + * // The methods are defined in the CarScript class. + * def carDsl = ''' + * start() + * drive 20 + * stop() + * ''' + * + * + * // Run DSL script. + * shell.evaluate """ + * // Use BaseScript annotation to set script + * // for evaluating the DSL. + * @groovy.transform.BaseScript CarScript carScript + * + * $carDsl + * """ + * + * // Checks to see that Car object has changed. + * assert car.distance == 20 + * assert car.state == 'stopped' + * </pre> + * + * @author Paul King + * @author Vladimir Orany + * @author Jim White + * @since 2.2.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.LOCAL_VARIABLE, ElementType.PACKAGE, ElementType.TYPE /*, ElementType.IMPORT*/}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.BaseScriptASTTransformation") +public @interface BaseScript { + Class value() default Script.class; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/Canonical.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Canonical.groovy b/src/main/groovy/groovy/transform/Canonical.groovy new file mode 100644 index 0000000..6f99c80 --- /dev/null +++ b/src/main/groovy/groovy/transform/Canonical.groovy @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform +/** + * The {@code @Canonical} meta-annotation combines the {@code @EqualsAndHashCode}, + * {@code @ToString} and {@code @TupleConstructor} annotations. It is used to assist in + * the creation of mutable classes. It instructs the compiler to execute AST transformations + * which add positional constructors, equals, hashCode and a pretty print toString to your class. + * <p> + * You can write classes in this shortened form: + * <pre class="groovyTestCase"> + * import groovy.transform.Canonical + * {@code @Canonical} class Customer { + * String first, last + * int age + * Date since + * Collection favItems = ['Food'] + * def object + * } + * def d = new Date() + * def anyObject = new Object() + * def c1 = new Customer(first:'Tom', last:'Jones', age:21, since:d, favItems:['Books', 'Games'], object: anyObject) + * def c2 = new Customer('Tom', 'Jones', 21, d, ['Books', 'Games'], anyObject) + * assert c1 == c2 + * </pre> + * + * You don't need to provide all arguments in constructor calls. If using named parameters, any property names not + * referenced will be given their default value (as per Java's default unless an explicit initialization constant is + * provided when defining the property). If using a tuple constructor, parameters are supplied in the order in which + * the properties are defined. Supplied parameters fill the tuple from the left. Any parameters missing on the right + * are given their default value. + * <pre> + * def c3 = new Customer(last: 'Jones', age: 21) + * def c4 = new Customer('Tom', 'Jones') + * + * assert null == c3.since + * assert 0 == c4.age + * assert c3.favItems == ['Food'] && c4.favItems == ['Food'] + * </pre> + * + * If you don't need all of the functionality of {@code @Canonical}, you can simply directly use one or more of the individual + * annotations which {@code @Canonical} aggregates. + * In addition, you can use {@code @Canonical} in combination with explicit use one or more of the individual annotations in + * cases where you need to further customize the annotation attributes. + * Any applicable annotation attributes from {@code @Canonical} missing from the explicit annotation will be merged + * but any existing annotation attributes within the explicit annotation take precedence. So, for example in this case here: + * <pre> + * {@code @Canonical}(includeNames=true, excludes='c') + * {@code @}{@link ToString}(excludes='a,b') + * class MyClass { ... } + * </pre> + * The generated {@code toString} will include property names and exclude the {@code a} and {@code b} properties. + * <p> + * A class created using {@code @Canonical} has the following characteristics: + * <ul> + * <li>A no-arg constructor is provided which allows you to set properties by name using Groovy's normal bean conventions. + * <li>Tuple-style constructors are provided which allow you to set properties in the same order as they are defined. + * <li>Default {@code equals}, {@code hashCode} and {@code toString} methods are provided based on the property values. + * See the GroovyDoc for the individual annotations for more details. + * <p> + * If you want similar functionality to what this annotation provides but also require immutability, see the + * {@code @}{@link Immutable} annotation. + * + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * import groovy.transform.* + * + * @Canonical + * class Building { + * String name + * int floors + * boolean officeSpace + * } + * + * // Constructors are added. + * def officeSpace = new Building('Initech office', 1, true) + * + * // toString() added. + * assert officeSpace.toString() == 'Building(Initech office, 1, true)' + * + * // Default values are used if constructor + * // arguments are not assigned. + * def theOffice = new Building('Wernham Hogg Paper Company') + * assert theOffice.floors == 0 + * theOffice.officeSpace = true + * + * def anotherOfficeSpace = new Building(name: 'Initech office', floors: 1, officeSpace: true) + * + * // equals() method is added. + * assert anotherOfficeSpace == officeSpace + * + * // equals() and hashCode() are added, so duplicate is not in Set. + * def offices = [officeSpace, anotherOfficeSpace, theOffice] as Set + * assert offices.size() == 2 + * assert offices.name.join(',') == 'Initech office,Wernham Hogg Paper Company' + * + * @Canonical + * @ToString(excludes='age') // Customize one of the transformations. + * class Person { + * String name + * int age + * } + * + * def mrhaki = new Person('mrhaki', 37) + * assert mrhaki.toString() == 'Person(mrhaki)' + * </pre> + * + * @see groovy.transform.EqualsAndHashCode + * @see groovy.transform.ToString + * @see groovy.transform.TupleConstructor + * @see groovy.transform.Immutable + * @since 1.8.0 + */ +@AnnotationCollector(value=[ToString, TupleConstructor, EqualsAndHashCode], mode=AnnotationCollectorMode.PREFER_EXPLICIT_MERGED) +public @interface Canonical { } http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/CompilationUnitAware.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/CompilationUnitAware.java b/src/main/groovy/groovy/transform/CompilationUnitAware.java new file mode 100644 index 0000000..b609f43 --- /dev/null +++ b/src/main/groovy/groovy/transform/CompilationUnitAware.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.control.CompilationUnit; + +/** + * This interface is for AST transformations which must be aware of the compilation unit where they are applied. + * + * @author Cedric Champeau + */ +public interface CompilationUnitAware { + void setCompilationUnit(CompilationUnit unit); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/CompileDynamic.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/CompileDynamic.groovy b/src/main/groovy/groovy/transform/CompileDynamic.groovy new file mode 100644 index 0000000..c5247d4 --- /dev/null +++ b/src/main/groovy/groovy/transform/CompileDynamic.groovy @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform + +import java.lang.annotation.Documented + +/** + * An annotation which is just a shortcut for @CompileStatic(TypeCheckingMode.SKIP). + * This can be used for example if you statically compile a full class but you want to skip + * some methods without having to use the full annotation. + * + * @author Cedric Champeau + * @since 2.1.0 + */ +@Documented +@AnnotationCollector(processor = "org.codehaus.groovy.transform.CompileDynamicProcessor") +public @interface CompileDynamic { +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/CompileStatic.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/CompileStatic.java b/src/main/groovy/groovy/transform/CompileStatic.java new file mode 100644 index 0000000..16dbc33 --- /dev/null +++ b/src/main/groovy/groovy/transform/CompileStatic.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This will let the Groovy compiler use compile time checks in the style of Java + * then perform static compilation, thus bypassing the Groovy meta object protocol. + * <p> + * When a class is annotated, all methods, properties, files, inner classes, etc. + * of the annotated class will be type checked. When a method is annotated, static + * compilation applies only to items (closures and anonymous inner classes) within + * the method. + * <p> + * By using {@link TypeCheckingMode#SKIP}, static compilation can be skipped on an + * element within a class or method otherwise marked with CompileStatic. For example + * a class can be annotated with CompileStatic, and a method within can be marked + * to skip static checking to use dynamic language features. + * + * @author <a href="mailto:[email protected]">Jochen "blackdrag" Theodorou</a> + * @author Cedric Champeau + * + * @see CompileDynamic + */ +@Documented +@Retention(RetentionPolicy.SOURCE) +@Target({ ElementType.METHOD, ElementType.TYPE, + ElementType.CONSTRUCTOR +}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.sc.StaticCompileTransformation") +public @interface CompileStatic { + TypeCheckingMode value() default TypeCheckingMode.PASS; + + /** + * The list of (classpath resources) paths to type checking DSL scripts, also known + * as type checking extensions. + * @return an array of paths to groovy scripts that must be on compile classpath + */ + String[] extensions() default {}; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/ConditionalInterrupt.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/ConditionalInterrupt.groovy b/src/main/groovy/groovy/transform/ConditionalInterrupt.groovy new file mode 100644 index 0000000..66aa64c --- /dev/null +++ b/src/main/groovy/groovy/transform/ConditionalInterrupt.groovy @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform + +import org.codehaus.groovy.transform.GroovyASTTransformationClass + +import java.lang.annotation.ElementType +import java.lang.annotation.Retention +import java.lang.annotation.RetentionPolicy +import java.lang.annotation.Target + +/** + * Allows "interrupt-safe" executions of scripts by adding a custom check for interruption + * into loops (for, while, ...) and at the start of closures and methods. + * <p> + * This is especially useful when executing foreign scripts that you do not have control over. Inject this + * transformation into a script that you need to interrupt based on some custom criteria. + * <p> + * Annotating anything in a script will cause for loops, while loops, methods, and closures to make a + * check against the specified closure. If the closure yields true (according to GroovyTruth), then the script + * will throw an InterruptedException. The annotation by default applies to any classes defined in the script + * as well. Annotated a class will cause (by default) all classes in the entire file ('Compilation Unit') to be + * enhanced. You can fine tune what is enhanced using the annotation parameters. + * <p> + * The following is sample usage of the annotation: + * <pre> + * <code>@ConditionalInterrupt({ counter++> 10})</code> + * import groovy.transform.ConditionalInterrupt + * + * counter = 0 + * def scriptMethod() { + * 4.times { + * println 'executing script method...' + * } + * } + * + * scriptMethod() + * </pre> + * Which results in the following code being generated (XXXXXX will be replaced with some runtime generated hashCode). Notice the checks and exceptions: + * <pre> + * public class script1291741477073 extends groovy.lang.Script { + * Object counter = 0 + * + * public java.lang.Object run() { + * counter = 0 + * } + * + * public java.lang.Object scriptMethod() { + * if (this.conditionalTransformXXXXXX$condition()) { + * throw new java.lang.InterruptedException('Execution interrupted. The following condition failed: { counter++> 10}') + * } + * 4.times({ + * if (this.conditionalTransformXXXXXX$condition()) { + * throw new java.lang.InterruptedException('Execution interrupted. The following condition failed: { counter++> 10}') + * } + * this.println('executing script method...') + * }) + * } + * + * private java.lang.Object conditionalTransformXXXXXX$condition() { + * counter++ > 10 + * } + * } + * </pre> + * + * Note that when you're annotating scripts, the variable scoping semantics are unchanged. Therefore, you must be + * careful about the variable scope you're using. Make sure that variables you reference in the closure parameter + * are in scope during script execution. The following example will throw a MissingPropertyException because + * counter is not in scope for a class: + * <pre> + * import groovy.transform.ConditionalInterrupt + * + * def counter = 0 + * <code>@ConditionalInterrupt({ counter++> 10})</code> + * class MyClass { + * def myMethod() { + * 4.times { + * println 'executing script method...' + * } + * } + * } + * + * new MyClass().myMethod() + * </pre> + * Additional usage examples can be found in the unit test for this class. + * + * @see TimedInterrupt + * @see ThreadInterrupt + * @author Cedric Champeau + * @author Hamlet D'Arcy + * @author Paul King + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target([ElementType.PACKAGE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.LOCAL_VARIABLE]) +@GroovyASTTransformationClass(["org.codehaus.groovy.transform.ConditionalInterruptibleASTTransformation"]) +@interface ConditionalInterrupt { + /** + * Set this to false if you have multiple classes within one source file and only + * want a conditional check on some of the classes. Place annotations on the classes + * you want enhanced. Set to true (the default) for blanket coverage of conditional + * checks on all methods, loops and closures within all classes/script code. + * + * For even finer-grained control see {@code applyToAllMembers}. + * + * @see #applyToAllMembers() + */ + boolean applyToAllClasses() default true + + /** + * Set this to false if you have multiple methods/closures within a class or script and only + * want conditional checks on some of them. Place annotations on the methods/closures that + * you want enhanced. When false, {@code applyToAllClasses} is automatically set to false. + * + * Set to true (the default) for blanket coverage of conditional checks on all methods, loops + * and closures within the class/script. + * + * @since 2.2.0 + * @see #applyToAllClasses() + */ + boolean applyToAllMembers() default true + + /** + * By default a conditional check is added to the start of all user-defined methods. To turn this off simply + * set this parameter to false. + */ + boolean checkOnMethodStart() default true + + /** + * Sets the type of exception which is thrown. + */ + Class thrown() default InterruptedException + + /** + * Conditional check - set as a closure expression. + */ + Class value() +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/EqualsAndHashCode.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/EqualsAndHashCode.java b/src/main/groovy/groovy/transform/EqualsAndHashCode.java new file mode 100644 index 0000000..592c6ba --- /dev/null +++ b/src/main/groovy/groovy/transform/EqualsAndHashCode.java @@ -0,0 +1,276 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Class annotation used to assist in creating appropriate {@code equals()} and {@code hashCode()} methods. + * <p> + * It allows you to write classes in this shortened form: + * <pre class="groovyTestCase"> + * import groovy.transform.EqualsAndHashCode + * {@code @EqualsAndHashCode} + * class Person { + * String first, last + * int age + * } + * def p1 = new Person(first:'John', last:'Smith', age:21) + * def p2 = new Person(first:'John', last:'Smith', age:21) + * assert p1 == p2 + * def map = [:] + * map[p1] = 45 + * assert map[p2] == 45 + * </pre> + * The {@code @EqualsAndHashCode} annotation instructs the compiler to execute an + * AST transformation which adds the necessary equals and hashCode methods to the class. + * <p> + * The {@code hashCode()} method is calculated using Groovy's {@code HashCodeHelper} class + * which implements an algorithm similar to the one outlined in the book <em>Effective Java</em>. + * <p> + * The {@code equals()} method compares the values of the individual properties (and optionally fields) + * of the class. It can also optionally call equals on the super class. Two different equals method + * implementations are supported both of which support the equals contract outlined in the javadoc + * for <code>java.lang.Object</code> + * <p> + * To illustrate the 'canEqual' implementation style (see http://www.artima.com/lejava/articles/equality.html + * for further details), consider this class: + * <pre> + * {@code @EqualsAndHashCode} + * class IntPair { + * int x, y + * } + * </pre> + * The generated <code>equals</code> and <code>canEqual</code> methods will be something like below: + * <pre> + * public boolean equals(java.lang.Object other) + * if (other == null) return false + * if (this.is(other)) return true + * if (!(other instanceof IntPair)) return false + * if (!other.canEqual(this)) return false + * if (x != other.x) return false + * if (y != other.y) return false + * return true + * } + * + * public boolean canEqual(java.lang.Object other) { + * return other instanceof IntPair + * } + * </pre> + * If no further options are specified, this is the default style for {@code @Canonical} and + * {@code @EqualsAndHashCode} annotated classes. The advantage of this style is that it allows inheritance + * to be used in limited cases where its purpose is for overriding implementation details rather than + * creating a derived type with different behavior. This is useful when using JPA Proxies for example or + * as shown in the following examples: + * <pre class="groovyTestCase"> + * import groovy.transform.* + * {@code @Canonical} class IntPair { int x, y } + * def p1 = new IntPair(1, 2) + * + * // overridden getter but deemed an IntPair as far as domain is concerned + * def p2 = new IntPair(1, 1) { int getY() { 2 } } + * + * // additional helper method added through inheritance but + * // deemed an IntPair as far as our domain is concerned + * {@code @InheritConstructors} class IntPairWithSum extends IntPair { + * def sum() { x + y } + * } + * + * def p3 = new IntPairWithSum(1, 2) + * + * assert p1 == p2 && p2 == p1 + * assert p1 == p3 && p3 == p1 + * assert p3 == p2 && p2 == p3 + * </pre> + * Note that if you create any domain classes which don't have exactly the + * same contract as <code>IntPair</code> then you should provide an appropriate + * <code>equals</code> and <code>canEqual</code> method. The easiest way to + * achieve this would be to use the {@code @Canonical} or + * {@code @EqualsAndHashCode} annotations as shown below: + * <pre> + * {@code @EqualsAndHashCode} + * {@code @TupleConstructor(includeSuperProperties=true)} + * class IntTriple extends IntPair { int z } + * def t1 = new IntTriple(1, 2, 3) + * assert p1 != t1 && p2 != t1 && t1 != p3 + * </pre> + * + * The alternative supported style regards any kind of inheritance as creation of + * a new type and is illustrated in the following example: + * <pre> + * {@code @EqualsAndHashCode(useCanEqual=false)} + * class IntPair { + * int x, y + * } + * </pre> + * The generated equals method will be something like below: + * <pre> + * public boolean equals(java.lang.Object other) + * if (other == null) return false + * if (this.is(other)) return true + * if (IntPair != other.getClass()) return false + * if (x != other.x) return false + * if (y != other.y) return false + * return true + * } + * </pre> + * This style is appropriate for final classes (where inheritance is not + * allowed) which have only <code>java.lang.Object</code> as a super class. + * Most {@code @Immutable} classes fall in to this category. For such classes, + * there is no need to introduce the <code>canEqual()</code> method. + * <p> + * Note that if you explicitly set <code>useCanEqual=false</code> for child nodes + * in a class hierarchy but have it <code>true</code> for parent nodes and you + * also have <code>callSuper=true</code> in the child, then your generated + * equals methods will not strictly follow the equals contract. + * <p> + * Note that when used in the recommended fashion, the two implementations supported adhere + * to the equals contract. You can provide your own equivalence relationships if you need, + * e.g. for comparing instances of the <code>IntPair</code> and <code>IntTriple</code> classes + * discussed earlier, you could provide the following method in <code>IntPair</code>: + * <pre> + * boolean hasEqualXY(other) { other.x == getX() && other.y == getY() } + * </pre> + * Then for the objects defined earlier, the following would be true: + * <pre> + * assert p1.hasEqualXY(t1) && t1.hasEqualXY(p1) + * assert p2.hasEqualXY(t1) && t1.hasEqualXY(p2) + * assert p3.hasEqualXY(t1) && t1.hasEqualXY(p3) + * </pre> + * There is also support for including or excluding fields/properties by name when constructing + * the equals and hashCode methods as shown here: + * <pre class="groovyTestCase"> + * import groovy.transform.* + * {@code @EqualsAndHashCode}(excludes="z") + * {@code @TupleConstructor} + * public class Point2D { + * int x, y, z + * } + * + * assert new Point2D(1, 1, 1).equals(new Point2D(1, 1, 2)) + * assert !new Point2D(1, 1, 1).equals(new Point2D(2, 1, 1)) + * + * {@code @EqualsAndHashCode}(excludes=["y", "z"]) + * {@code @TupleConstructor} + * public class Point1D { + * int x, y, z + * } + * + * assert new Point1D(1, 1, 1).equals(new Point1D(1, 1, 2)) + * assert new Point1D(1, 1, 1).equals(new Point1D(1, 2, 1)) + * assert !new Point1D(1, 1, 1).equals(new Point1D(2, 1, 1)) + * </pre> + * <b>Note:</b> {@code @EqualsAndHashCode} is a transform to help reduce boilerplate + * in the common cases. It provides a useful implementation of {@code equals()} + * and {@code hashCode()} but not necessarily the most appropriate or + * efficient one for every use case. You should write custom versions if your + * scenario demands it. In particular, if you have + * mutually-referential classes, the implementations provided may not be suitable + * and may recurse infinitely (leading to a {@code StackOverflowError}). In such cases, + * you need to remove the infinite loop from your data structures or write your own custom methods. + * If your data structures are self-referencing, the code generated by this transform will try to avoid + * infinite recursion but the algorithm used may not suit your scenario, so use with caution if + * you have such structures. + * A future version of this transform may better handle some additional recursive scenarios. + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * import groovy.transform.EqualsAndHashCode + * + * @EqualsAndHashCode(includeFields=true) + * class User { + * String name + * boolean active + * List likes + * private int age = 37 + * } + * + * def user = new User(name: 'mrhaki', active: false, likes: ['Groovy', 'Java']) + * def mrhaki = new User(name: 'mrhaki', likes: ['Groovy', 'Java']) + * def hubert = new User(name: 'Hubert Klein Ikkink', likes: ['Groovy', 'Java']) + * + * assert user == mrhaki + * assert mrhaki != hubert + * + * Set users = new HashSet() + * users.add user + * users.add mrhaki + * users.add hubert + * assert users.size() == 2 + * </pre> + * + * @see org.codehaus.groovy.util.HashCodeHelper + * @author Paul King + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.EqualsAndHashCodeASTTransformation") +public @interface EqualsAndHashCode { + /** + * List of field and/or property names to exclude from the equals and hashCode calculations. + * Must not be used if 'includes' is used. For convenience, a String with comma separated names + * can be used in addition to an array (using Groovy's literal list notation) of String values. + */ + String[] excludes() default {}; + + /** + * List of field and/or property names to include within the equals and hashCode calculations. + * Must not be used if 'excludes' is used. For convenience, a String with comma separated names + * can be used in addition to an array (using Groovy's literal list notation) of String values. + * The default value is a special marker value indicating that no includes are defined; all fields + * and/or properties are included if 'includes' remains undefined and 'excludes' is explicitly or + * implicitly an empty list. + */ + String[] includes() default {Undefined.STRING}; + + /** + * Whether to cache hashCode calculations. You should only set this to true if + * you know the object is immutable (or technically mutable but never changed). + */ + boolean cache() default false; + + /** + * Whether to include super in equals and hashCode calculations. + */ + boolean callSuper() default false; + + /** + * Include fields as well as properties in equals and hashCode calculations. + */ + boolean includeFields() default false; + + /** + * Generate a canEqual method to be used by equals. + */ + boolean useCanEqual() default true; + + /** + * Whether to include all fields and/or properties in equals and hashCode calculations, including those + * with names that are considered internal. + * + * @since 2.5.0 + */ + boolean allNames() default false; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/ExternalizeMethods.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/ExternalizeMethods.java b/src/main/groovy/groovy/transform/ExternalizeMethods.java new file mode 100644 index 0000000..5ef37a0 --- /dev/null +++ b/src/main/groovy/groovy/transform/ExternalizeMethods.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Class annotation used to assist in the creation of {@code Externalizable} classes. + * The {@code @ExternalizeMethods} annotation instructs the compiler to execute an + * AST transformation which adds {@code writeExternal()} and {@code readExternal()} methods + * to a class and adds {@code Externalizable} to the interfaces which the class implements. + * The {@code writeExternal()} method writes each property (and optionally field) of the class + * while the {@code readExternal()} method will read each one back in the same order. + * Properties or fields marked as {@code transient} are ignored. + * This annotation is typically used in conjunction with the {@code @ExternalizeVerifier} annotation but + * most usually not directly but rather via {@code @AutoExternalizable} which is a shortcut for both annotations. + * <p> + * Example usage: + * <pre> + * import groovy.transform.* + * {@code @ExternalizeMethods} + * class Person { + * String first, last + * List favItems + * Date since + * } + * </pre> + * Which will create a class of the following form: + * <pre> + * class Person implements Externalizable { + * ... + * public void writeExternal(ObjectOutput out) throws IOException { + * out.writeObject(first) + * out.writeObject(last) + * out.writeObject(favItems) + * out.writeObject(since) + * } + * + * public void readExternal(ObjectInput oin) { + * first = (String) oin.readObject() + * last = (String) oin.readObject() + * favItems = (List) oin.readObject() + * since = (Date) oin.readObject() + * } + * ... + * } + * </pre> + * + * @author Paul King + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.ExternalizeMethodsASTTransformation") +public @interface ExternalizeMethods { + /** + * Comma separated list of property names to exclude from externalizing. + * For convenience, a String with comma separated names + * can be used in addition to an array (using Groovy's literal list notation) of String values. + */ + String[] excludes() default {}; + + /** + * Include fields as well as properties when externalizing. + */ + boolean includeFields() default false; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/ExternalizeVerifier.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/ExternalizeVerifier.java b/src/main/groovy/groovy/transform/ExternalizeVerifier.java new file mode 100644 index 0000000..185c935 --- /dev/null +++ b/src/main/groovy/groovy/transform/ExternalizeVerifier.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Class annotation used to assist in the creation of {@code Externalizable} classes. + * The {@code @ExternalizeVerifier} annotation instructs the compiler to check + * that a class has {@code writeExternal()} and {@code readExternal()} methods, + * implements the {@code Externalizable} interface and that each property (and optionally field) is not final + * and, optionally for non-primitives, has a type which is either {@code Externalizable} or {@code Serializable}. + * Properties or fields marked as {@code transient} are ignored. + * This annotation is typically used in conjunction with the {@code @ExternalizeMethods} annotation but + * most usually not directly but rather via {@code @AutoExternalizable} which is a shortcut for both annotations. + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.ExternalizeVerifierASTTransformation") +public @interface ExternalizeVerifier { + /** + * Comma separated list of property names to exclude from externalization verification. + * For convenience, a String with comma separated names + * can be used in addition to an array (using Groovy's literal list notation) of String values. + */ + String[] excludes() default {}; + + /** + * Include fields as well as properties when verifying externalization properties. + */ + boolean includeFields() default false; + + /** + * Turns on strict type checking for property (or field) types. In strict mode, such types must also implement Serializable or Externalizable. + * If your properties have interface types that don't implement Serializable but all the concrete implementations do, or the + * type is of a non-Serializable class but the property will be null at runtime, then your instances will still be serializable + * but you can't turn on strict checking. + */ + boolean checkPropertyTypes() default false; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/Field.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Field.java b/src/main/groovy/groovy/transform/Field.java new file mode 100644 index 0000000..bcc64e8 --- /dev/null +++ b/src/main/groovy/groovy/transform/Field.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Variable annotation used for changing the scope of a variable within a script from + * being within the run method of the script to being at the class level for the script. + * <p> + * The annotated variable will become a private field of the script class. + * The type of the field will be the same as the type of the variable. Example usage: + * <pre class="groovyTestCase"> + * import groovy.transform.Field + * {@code @Field} List awe = [1, 2, 3] + * def awesum() { awe.sum() } + * assert awesum() == 6 + * </pre> + * In this example, without the annotation, variable <code>awe</code> would be + * a local script variable (technically speaking it will be a local variable within + * the <code>run</code> method of the script class). Such a local variable would + * not be visible inside the <code>awesum</code> method. With the annotation, + * <code>awe</code> becomes a private List field in the script class and is + * visible within the <code>awesum</code> method. + * + * @author Paul King + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.LOCAL_VARIABLE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.FieldASTTransformation") +public @interface Field { +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/Generated.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Generated.java b/src/main/groovy/groovy/transform/Generated.java new file mode 100644 index 0000000..98eaf5b --- /dev/null +++ b/src/main/groovy/groovy/transform/Generated.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The Generated annotation is used to mark members that have been generated. + */ +@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Generated { +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/Immutable.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Immutable.java b/src/main/groovy/groovy/transform/Immutable.java new file mode 100644 index 0000000..5f6bf90 --- /dev/null +++ b/src/main/groovy/groovy/transform/Immutable.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Class annotation used to assist in the creation of immutable classes. + * <p> + * It allows you to write classes in this shortened form: + * <pre class="groovyTestCase"> + * {@code @groovy.transform.Immutable} class Customer { + * String first, last + * int age + * Date since + * Collection favItems + * } + * def d = new Date() + * def c1 = new Customer(first:'Tom', last:'Jones', age:21, since:d, favItems:['Books', 'Games']) + * def c2 = new Customer('Tom', 'Jones', 21, d, ['Books', 'Games']) + * assert c1 == c2 + * </pre> + * The {@code @Immutable} annotation instructs the compiler to execute an + * AST transformation which adds the necessary getters, constructors, + * equals, hashCode and other helper methods that are typically written + * when creating immutable classes with the defined properties. + * <p> + * A class created in this way has the following characteristics: + * <ul> + * <li>The class is automatically made final. + * <li>Properties must be of an immutable type or a type with a strategy for handling non-immutable + * characteristics. Specifically, the type must be one of the primitive or wrapper types, Strings, enums, + * other {@code @Immutable} classes or known immutables (e.g. java.awt.Color, java.net.URI, java.util.UUID). + * Also handled are Cloneable classes, collections, maps and arrays, other "effectively immutable" + * classes with special handling (e.g. java.util.Date), and usages of java.util.Optional where the + * contained type is immutable (e.g. Optional<String>). + * <li>Properties automatically have private, final backing fields with getters. + * Attempts to update the property will result in a {@code ReadOnlyPropertyException}. + * <li>A map-based constructor is provided which allows you to set properties by name. + * <li>A tuple-style constructor is provided which allows you to set properties in the same order as they are defined. + * <li>Default {@code equals}, {@code hashCode} and {@code toString} methods are provided based on the property values. + * Though not normally required, you may write your own implementations of these methods. For {@code equals} and {@code hashCode}, + * if you do write your own method, it is up to you to obey the general contract for {@code equals} methods and supply + * a corresponding matching {@code hashCode} method. + * If you do provide one of these methods explicitly, the default implementation will be made available in a private + * "underscore" variant which you can call. E.g., you could provide a (not very elegant) multi-line formatted + * {@code toString} method for {@code Customer} above as follows: + * <pre> + * String toString() { + * _toString().replaceAll(/\(/, '(\n\t').replaceAll(/\)/, '\n)').replaceAll(/, /, '\n\t') + * } + * </pre> + * If an "underscore" version of the respective method already exists, then no default implementation is provided. + * <li>{@code Date}s, {@code Cloneable}s and arrays are defensively copied on the way in (constructor) and out (getters). + * Arrays and {@code Cloneable} objects use the {@code clone} method. For your own classes, + * it is up to you to define this method and use deep cloning if appropriate. + * <li>{@code Collection}s and {@code Map}s are wrapped by immutable wrapper classes (but not deeply cloned!). + * Attempts to update them will result in an {@code UnsupportedOperationException}. + * <li>Fields that are enums or other {@code @Immutable} classes are allowed but for an + * otherwise possible mutable property type, an error is thrown. + * <li>You don't have to follow Groovy's normal property conventions, e.g. you can create an explicit private field and + * then you can write explicit get and set methods. Such an approach, isn't currently prohibited (to give you some + * wiggle room to get around these conventions) but any fields created in this way are deemed not to be part of the + * significant state of the object and aren't factored into the {@code equals} or {@code hashCode} methods. + * Similarly, you may use static properties (though usually this is discouraged) and these too will be ignored + * as far as significant state is concerned. If you do break standard conventions, you do so at your own risk and + * your objects may no longer be immutable. It is up to you to ensure that your objects remain immutable at least + * to the extent expected in other parts of your program! + * </ul> + * Immutable classes are particularly useful for functional and concurrent styles of programming + * and for use as key values within maps. If you want similar functionality to what this annotation + * provides but don't need immutability then consider using {@code @Canonical}. + * <p> + * Customising behaviour: + * <p> + * You can customise the toString() method provided for you by {@code @Immutable} + * by also adding the {@code @ToString} annotation to your class definition. + * <p> + * Limitations: + * <ul> + * <li> + * As outlined above, Arrays and {@code Cloneable} objects use the {@code clone} method. For your own classes, + * it is up to you to define this method and use deep cloning if appropriate. + * </li> + * <li> + * As outlined above, {@code Collection}s and {@code Map}s are wrapped by immutable wrapper classes (but not deeply cloned!). + * </li> + * <li> + * Currently {@code BigInteger} and {@code BigDecimal} are deemed immutable but see: + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6348370 + * </li> + * <li> + * {@code java.awt.Color} is treated as "effectively immutable" but is not final so while not normally used with child + * classes, it isn't strictly immutable. Use at your own risk. + * </li> + * <li> + * {@code java.util.Date} is treated as "effectively immutable" but is not final so it isn't strictly immutable. + * Use at your own risk. + * </li> + * <li> + * Groovy's normal map-style naming conventions will not be available if the first property + * has type {@code LinkedHashMap} or if there is a single Map, AbstractMap or HashMap property. + * </li> + * </ul> + * <p>More examples:</p> + -------------------------------------------------------------------------------- + * <pre class="groovyTestCase"> + * import groovy.transform.* + * + * @Canonical + * class Building { + * String name + * int floors + * boolean officeSpace + * } + * + * // Constructors are added. + * def officeSpace = new Building('Initech office', 1, true) + * + * // toString() added. + * assert officeSpace.toString() == 'Building(Initech office, 1, true)' + * + * // Default values are used if constructor + * // arguments are not assigned. + * def theOffice = new Building('Wernham Hogg Paper Company') + * assert theOffice.floors == 0 + * theOffice.officeSpace = true + * + * def anotherOfficeSpace = new Building(name: 'Initech office', floors: 1, officeSpace: true) + * + * // equals() method is added. + * assert anotherOfficeSpace == officeSpace + * + * // equals() and hashCode() are added, so duplicate is not in Set. + * def offices = [officeSpace, anotherOfficeSpace, theOffice] as Set + * assert offices.size() == 2 + * assert offices.name.join(',') == 'Initech office,Wernham Hogg Paper Company' + * + * @Canonical + * @ToString(excludes='age') // Customize one of the transformations. + * class Person { + * String name + * int age + * } + * + * def mrhaki = new Person('mrhaki', 37) + * assert mrhaki.toString() == 'Person(mrhaki)' + * </pre> + * + * @author Paul King + * @author Andre Steingress + * @see groovy.transform.ToString + * @see groovy.transform.Canonical + * @since 1.7 + */ [email protected] +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.ImmutableASTTransformation") +public @interface Immutable { + /** + * Allows you to provide {@code @Immutable} with a list of classes which + * are deemed immutable. By supplying a class in this list, you are vouching + * for its immutability and {@code @Immutable} will do no further checks. + * Example: + * <pre> + * import groovy.transform.* + * {@code @Immutable}(knownImmutableClasses = [Address]) + * class Person { + * String first, last + * Address address + * } + * + * {@code @TupleConstructor} + * class Address { + * final String street + * } + * </pre> + * + * @since 1.8.7 + */ + Class[] knownImmutableClasses() default {}; + + /** + * Allows you to provide {@code @Immutable} with a list of property names which + * are deemed immutable. By supplying a property's name in this list, you are vouching + * for its immutability and {@code @Immutable} will do no further checks. + * Example: + * <pre> + * {@code @groovy.transform.Immutable}(knownImmutables = ['address']) + * class Person { + * String first, last + * Address address + * } + * ... + * </pre> + * + * @since 2.1.0 + */ + String[] knownImmutables() default {}; + + /** + * If {@code true}, this adds a method {@code copyWith} which takes a Map of + * new property values and returns a new instance of the Immutable class with + * these values set. + * Example: + * <pre class="groovyTestCase"> + * {@code @groovy.transform.Immutable}(copyWith = true) + * class Person { + * String first, last + * } + * + * def tim = new Person( 'tim', 'yates' ) + * def alice = tim.copyWith( first:'alice' ) + * + * assert tim.first == 'tim' + * assert alice.first == 'alice' + * </pre> + * Unknown keys in the map are ignored, and if the values would not change + * the object, then the original object is returned. + * + * If a method called {@code copyWith} that takes a single parameter already + * exists in the class, then this setting is ignored, and no method is generated. + * + * @since 2.2.0 + */ + boolean copyWith() default false; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/IndexedProperty.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/IndexedProperty.java b/src/main/groovy/groovy/transform/IndexedProperty.java new file mode 100644 index 0000000..b6d47dc --- /dev/null +++ b/src/main/groovy/groovy/transform/IndexedProperty.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Field annotation used with properties to provide an indexed getter and setter for the property. + * Groovy provides nice GPath syntax support for accessing indexed properties but Java tools + * or frameworks may expect the JavaBean style setters and getters. + * <p> + * <em>Example usage:</em> suppose you have a class with the following properties: + * <pre> + * {@code @IndexedProperty} FieldType[] someField + * {@code @IndexedProperty} List<FieldType> otherField + * {@code @IndexedProperty} List furtherField + * </pre> + * will add the following methods to the class containing the properties: + * <pre> + * FieldType getSomeField(int index) { + * someField[index] + * } + * FieldType getOtherField(int index) { + * otherField[index] + * } + * Object getFurtherField(int index) { + * furtherField[index] + * } + * void setSomeField(int index, FieldType val) { + * someField[index] = val + * } + * void setOtherField(int index, FieldType val) { + * otherField[index] = val + * } + * void setFurtherField(int index, Object val) { + * furtherField[index] = val + * } + * </pre> + * Normal Groovy visibility rules for properties apply + * (i.e. no <code>public</code>, <code>private</code> or <code>package</code> + * visibility can be specified) or you will receive a compile-time error message. + * The normal Groovy property getters and setters will also be created. + * <p> + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * import groovy.transform.IndexedProperty + * + * class Group { + * String name + * List members = [] + * } + * + * class IndexedGroup { + * String name + * @IndexedProperty List members = [] + * } + * + * def group = new Group(name: 'Groovy') + * group.members[0] = 'mrhaki' + * group.members[1] = 'Hubert' + * assert 2 == group.members.size() + * assert ['mrhaki', 'Hubert'] == group.members + * + * try { + * group.setMembers(0, 'hubert') // Not index property + * } catch (MissingMethodException e) { + * assert e + * } + * + * def indexedGroup = new IndexedGroup(name: 'Grails') + * indexedGroup.members[0] = 'mrhaki' + * indexedGroup.setMembers 1, 'Hubert' + * assert 2 == indexedGroup.members.size() + * assert 'mrhaki' == indexedGroup.getMembers(0) + * assert 'Hubert' == indexedGroup.members[1] + * </pre> + * + * @author Paul King + * @since 1.7.3 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.FIELD}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.IndexedPropertyASTTransformation") +public @interface IndexedProperty { +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/InheritConstructors.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/InheritConstructors.java b/src/main/groovy/groovy/transform/InheritConstructors.java new file mode 100644 index 0000000..ba7758a --- /dev/null +++ b/src/main/groovy/groovy/transform/InheritConstructors.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Class annotation to make constructors from a super class available in a sub class. + * Should be used with care with other annotations which create constructors - see "Known + * Limitations" for more details. + * <p> + * {@code @InheritConstructors} saves you typing some boilerplate code. + * <p> + * <em>Example usage:</em> + * <pre class="groovyTestCase"> + * class Person { + * String first, last + * Person(String first, String last) { + * this.first = first + * this.last = last.toUpperCase() + * } + * } + * + * {@code @groovy.transform.InheritConstructors} + * class PersonAge extends Person { + * int age + * } + * + * def js = new PersonAge('John', 'Smith') + * js.age = 25 + * + * assert "$js.last, $js.first is $js.age years old" == 'SMITH, John is 25 years old' + * </pre> + * for this case, the <code>PersonAge</code> class will be + * equivalent to the following code: + * <pre> + * class PersonAge extends Person { + * PersonAge(String first, String last) { + * super(first, last) + * } + * int age + * } + * </pre> + * You may add additional constructors in addition to inherited ones. + * If the argument types of a supplied constructor exactly match those + * of a parent constructor, then that constructor won't be inherited. + * <p> + * <em>Style note:</em> Don't go overboard using this annotation. + * Typical Groovy style is to use named-arg constructors when possible. + * This is easy to do for Groovy objects or any objects following JavaBean + * conventions. In other cases, inheriting the constructors may be useful. + * However, sub-classes often introduce new properties and these are often best + * set in a constructor; especially if that matches the style adopted + * in parent classes. So, even for the example above, it may have been + * better style to define an explicit constructor for <code>PersonAge</code> + * that also set the <code>age</code> property. Sometimes, consistent + * style is much more important than saving a few keystrokes. + * <p> + * As another example, this: + * <pre> + * {@code @InheritConstructors} class CustomException extends RuntimeException { } + * </pre> + * is equivalent to this: + * <pre> + * class CustomException extends RuntimeException { + * CustomException() { + * super() + * } + * CustomException(String message) { + * super(message) + * } + * CustomException(String message, Throwable cause) { + * super(message, cause) + * } + * CustomException(Throwable cause) { + * super(cause) + * } + * } + * </pre> + * Known Limitations: + * <ul> + * <li>This AST transform creates (potentially) numerous constructors. + * You should take care to avoid constructors with duplicate signatures if you are defining your own constructors or + * combining with other AST transforms which create constructors (e.g. {@code @TupleConstructor}); + * the order in which the particular transforms are processed becomes important in that case.</li> + * <li>If you create Groovy constructors with optional + * arguments this leads to multiple constructors created in the byte code. + * The expansion to multiple constructors occurs in a later phase to + * this AST transformation. This means that you can't override (i.e. not + * inherit) the constructors with signatures that Groovy adds later. + * If you get it wrong you will get a compile-time error about the duplication.</li> + * </ul> + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * import groovy.transform.InheritConstructors + * + * @InheritConstructors + * class MyException extends Exception { + * } + * + * def e = new MyException() + * def e1 = new MyException('message') // Other constructors are available. + * assert 'message' == e1.message + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * import groovy.transform.InheritConstructors + + * class Person { + * String name + * + * Person(String name) { + * this.name = name + * } + * } + * + * @InheritConstructors + * class Child extends Person {} + * + * def child = new Child('Liam') + * assert 'Liam' == child.name + * </pre> + * + * @author Paul King + * @since 1.7.3 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.InheritConstructorsASTTransformation") +public @interface InheritConstructors { + /** + * Whether to carry over annotations on the copied constructors. + * Currently Closure annotation members are not supported. + * + * @return true if copied constructor should keep constructor annotations + */ + boolean constructorAnnotations() default false; + + /** + * Whether to carry over parameter annotations on the copied constructors. + * Currently Closure annotation members are not supported. + * + * @return true if copied constructor should keep parameter annotations + */ + boolean parameterAnnotations() default false; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/MapConstructor.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/MapConstructor.java b/src/main/groovy/groovy/transform/MapConstructor.java new file mode 100644 index 0000000..5608001 --- /dev/null +++ b/src/main/groovy/groovy/transform/MapConstructor.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.transform; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Class annotation used to assist in the creation of map constructors in classes. + * <p> + * It allows you to write classes in this shortened form: + * <pre class="groovyTestCase"> + * import groovy.transform.* + * + * {@code @TupleConstructor} + * class Person { + * String first, last + * } + * + * {@code @CompileStatic} // optional + * {@code @ToString(includeSuperProperties=true)} + * {@code @MapConstructor}(pre={ super(args?.first, args?.last); args = args ?: [:] }, post = { first = first?.toUpperCase() }) + * class Author extends Person { + * String bookName + * } + * + * assert new Author(first: 'Dierk', last: 'Koenig', bookName: 'ReGinA').toString() == 'Author(ReGinA, DIERK, Koenig)' + * assert new Author().toString() == 'Author(null, null, null)' + * </pre> + * The {@code @MapConstructor} annotation instructs the compiler to execute an + * AST transformation which adds the necessary constructor method to your class. + * <p> + * A map constructor is created which sets properties, and optionally fields and + * super properties if the property/field name is a key within the map. + * <p> + * For the above example, the generated constructor will be something like: + * <pre> + * public Author(java.util.Map args) { + * super(args?.first, args?.last) + * args = args ? args : [:] + * if (args.containsKey('bookName')) { + * this.bookName = args['bookName'] + * } + * first = first?.toUpperCase() + * } + * </pre> + * + * @since 2.5.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.MapConstructorASTTransformation") +public @interface MapConstructor { + /** + * List of field and/or property names to exclude from the constructor. + * Must not be used if 'includes' is used. For convenience, a String with comma separated names + * can be used in addition to an array (using Groovy's literal list notation) of String values. + */ + String[] excludes() default {}; + + /** + * List of field and/or property names to include within the constructor. + * Must not be used if 'excludes' is used. For convenience, a String with comma separated names + * can be used in addition to an array (using Groovy's literal list notation) of String values. + * The default value is a special marker value indicating that no includes are defined; all fields and/or properties + * are included if 'includes' remains undefined and 'excludes' is explicitly or implicitly an empty list. + */ + String[] includes() default {Undefined.STRING}; + + /** + * Include fields in the constructor. + */ + boolean includeFields() default false; + + /** + * Include properties in the constructor. + */ + boolean includeProperties() default true; + + /** + * Include properties from super classes in the constructor. + */ + boolean includeSuperProperties() default false; + + /** + * By default, properties are set directly using their respective field. + * By setting {@code useSetters=true} then a writable property will be set using its setter. + * If turning on this flag we recommend that setters that might be called are + * made null-safe wrt the parameter. + */ + boolean useSetters() default false; + + /** + * A Closure containing statements which will be prepended to the generated constructor. The first statement within the Closure may be "super(someArgs)" in which case the no-arg super constructor won't be called. + */ + Class pre() default Undefined.CLASS.class; + + /** + * A Closure containing statements which will be appended to the end of the generated constructor. Useful for validation steps or tweaking the populated fields/properties. + */ + Class post() default Undefined.CLASS.class; + + /** + * Whether to include all fields and/or properties within the constructor, including those with names that are considered internal. + */ + boolean allNames() default false; +}
