http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/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/0ad8c07c/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/0ad8c07c/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/0ad8c07c/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/0ad8c07c/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; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/Memoized.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Memoized.java b/src/main/groovy/groovy/transform/Memoized.java new file mode 100644 index 0000000..efecb17 --- /dev/null +++ b/src/main/groovy/groovy/transform/Memoized.java @@ -0,0 +1,145 @@ +/* + * 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; + +/** + * Method annotation that creates a cache for the results of the execution of the annotated method. Whenever the method + * is called, the mapping between the parameters and the return value is preserved in a cache making subsequent calls with + * the same arguments fast. + * + * <p> + * Example usage: + * + * <pre> + * class MemoizedExample { + * + * {@code @Memoized} + * int sum(int n1, int n2) { + * println "$n1 + $n2 = ${n1 + n2}" + * n1 + n2 + * } + * } + * </pre> + * + * which becomes (approximately): + * + * <pre> + * class MemoizedExample { + * + * private final Closure memoizedSum = { int n1, int n2 -> + * private$method$memoizedSum(n1,n2) + * }.memoize() + * + * int sum(int n1, int n2) { + * memoizedSum(n1, n2) + * } + * + * private private$method$memoizedSum(int n1, int n2) { + * println "$n1 + $n2 = ${n1 + n2}" + * n1 + n2 + * } + * } + * </pre> + * + * <p> + * Upon execution of this code: + * + * <pre> + * def instance = new MemoizedExample() + * println instance.sum(1, 2) + * println instance.sum(1, 2) + * println instance.sum(2, 3) + * println instance.sum(2, 3) + * </pre> + * + * The following will be output: + * + * <pre> + * 1 + 2 = 3 + * 3 + * 3 + * 2 + 3 = 5 + * 5 + * 5 + * </pre> + * + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * import groovy.transform.* + * + * // Script variable which is changed when increment() + * // method is invoked. + * // If cached method call is invoked then the value + * // of this variable will not change. + * @Field boolean incrementChange = false + * + * @Memoized + * int increment(int value) { + * incrementChange = true + * value + 1 + * } + * + * // Invoke increment with argument 10. + * increment(10) + * + * // increment is invoked so incrementChange is true. + * assert incrementChange + * + * // Set incrementChange back to false. + * incrementChange = false + * + * // Invoke increment with argument 10. + * increment(10) + * + * // Now the cached method is used and + * // incrementChange is not changed. + * assert !incrementChange + * + * // Invoke increment with other argument value. + * increment(11) + * + * // increment is invoked so incrementChange is true. + * assert incrementChange + * </pre> + * + * @author Andrey Bloschetsov + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ ElementType.METHOD }) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.MemoizedASTTransformation") +public @interface Memoized { + + /** + * Number of cached return values to protect from garbage collection. + */ + int protectedCacheSize() default 0; + + /** + * The maximum size the cache can grow to. + */ + int maxCacheSize() default 0; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/PackageScope.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/PackageScope.java b/src/main/groovy/groovy/transform/PackageScope.java new file mode 100644 index 0000000..caa77a7 --- /dev/null +++ b/src/main/groovy/groovy/transform/PackageScope.java @@ -0,0 +1,71 @@ +/* + * 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; + +/** + * Annotation used for turning off Groovy's auto visibility conventions. + * By default, Groovy automatically turns package protected fields into properties + * and makes package protected methods, constructors and classes public. + * This annotation allows this feature to be turned + * off and revert back to Java behavior if needed. + * + * Place it on classes, fields, constructors or methods of interest as follows: + * <pre> + * {@code @}PackageScope class Bar { // package protected + * {@code @}PackageScope int field // package protected; not a property + * {@code @}PackageScope method(){} // package protected + * } + * </pre> + * or for greater control, at the class level with one or more + * <code>PackageScopeTarget</code> values: + * <pre> + * import static groovy.transform.PackageScopeTarget.* + * {@code @}PackageScope([CLASS, FIELDS]) + * class Foo { // class will have package protected scope + * int field1, field2 // both package protected + * def method(){} // public + * } + * {@code @}PackageScope(METHODS) + * class Bar { // public + * int field // treated as a property + * def method1(){} // package protected + * def method2(){} // package protected + * } + * </pre> + * + * This transformation is not frequently needed but can be useful in certain testing scenarios + * or when using a third-party library or framework which relies upon package scoping. + * + * @author Paul King + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.PackageScopeASTTransformation") +public @interface PackageScope { + groovy.transform.PackageScopeTarget[] value() default {PackageScopeTarget.CLASS}; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/PackageScopeTarget.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/PackageScopeTarget.java b/src/main/groovy/groovy/transform/PackageScopeTarget.java new file mode 100644 index 0000000..726d47a --- /dev/null +++ b/src/main/groovy/groovy/transform/PackageScopeTarget.java @@ -0,0 +1,47 @@ +/* + * 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; + +/** + * Intended target when {@code @}PackageScope is placed at the class level. + * + * @author Paul King + * @since 1.8.0 + */ +public enum PackageScopeTarget { + /** + * Make the Class have package protected visibility. + */ + CLASS, + + /** + * Make the Class methods have package protected visibility. + */ + METHODS, + + /** + * Make the Class fields have package protected visibility. + */ + FIELDS, + + /** + * Make the Class constructors have package protected visibility. + */ + CONSTRUCTORS +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/SelfType.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/SelfType.java b/src/main/groovy/groovy/transform/SelfType.java new file mode 100644 index 0000000..5b2ccb3 --- /dev/null +++ b/src/main/groovy/groovy/transform/SelfType.java @@ -0,0 +1,82 @@ +/* + * 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; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be added on a trait to declare the list of types that a class + * implementing that trait is supposed to extend. This is useful when you want to be + * able to call methods from the class implementing the trait without having to declare + * all of them as members of the trait. + * + * Self types are particularly useful in combination with {@link groovy.transform.CompileStatic}, + * if you know that a trait can only be applied to a specific type but that the trait cannot extend + * that type itself. For example, imagine the following code: + * <pre><code> + * class Component { void methodInComponent() } + * trait ComponentDecorator { + * void logAndCall() { + * println "Calling method in component" + * methodInComponent() + * } + * // other useful methods + * } + * class DecoratedComponent extends Component implements ComponentDecorator {} + * </code></pre> + * + * This will work because the trait uses the dynamic backend, so there is no check at + * compile time that the <i>methodInComponent</i> call in <i>logAndCall</i> is actually + * defined. If you annotate the trait with {@link groovy.transform.CompileStatic}, compilation + * will fail because the trait does not define the method. To declare that the trait can be + * applied on something that will extend <i>Component</i>, you need to add the <i>SelfType</i> + * annotation like this: + * <pre><code> + * class Component { void methodInComponent() } + * + * {@literal @}CompileStatic + * {@literal @}SelfType(Component) + * trait ComponentDecorator { + * void logAndCall() { + * println "Calling method in component" + * methodInComponent() + * } + * // other useful methods + * } + * class DecoratedComponent extends Component implements ComponentDecorator {} + * </code></pre> + * + * This pattern can therefore be used to avoid explicit casts everywhere you need to call a method + * that you know is defined in the class that will implement the trait but normally don't have access + * to, which is often the case where a trait needs to be applied on a class provided by a third-party + * library. + * + * @author Cédric Champeau + * @since 2.4.0 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface SelfType { + Class[] value(); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/Sortable.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Sortable.java b/src/main/groovy/groovy/transform/Sortable.java new file mode 100644 index 0000000..b235bfa --- /dev/null +++ b/src/main/groovy/groovy/transform/Sortable.java @@ -0,0 +1,179 @@ +/* + * 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; + +/** + * A class annotation used to make a class Comparable by multiple Comparators. + * + * As an example, given this class: + * <pre> + * {@code @Sortable} class Person { + * String first + * String last + * Integer born + * } + * </pre> + * The generated Groovy class will: + * <ul> + * <li>implement the {@code Comparable} interface</li> + * <li>have a {@code compareTo} method based on the {@code first}, + * {@code last} and {@code born} properties (priority ordering will be according + * to the ordering of property definition, highest first, unless 'includes' is used; in which case, + * priority will be according to the order given in the includes list)</li> + * <li>have three {@code Comparator} methods named {@code comparatorByFirst}, + * {@code comparatorByLast} and {@code comparatorByBorn}</li> + * <li>sort by natural order by default, reversed natural order can be specified</li> + * </ul> + * The properties within the class must themselves be {@code Comparable} or {@code @Sortable}. + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * import groovy.transform.Sortable + * import groovy.transform.ToString + * + * @Sortable + * @ToString + * class Course { + * // Order of properties determines priority when sorting + * String title + * Date beginDate + * Integer maxAttendees // int doesn't implement Comparable, so use Integer + * } + * + * + * final Course groovy = new Course( + * title: 'Groovy', beginDate: new Date() + 7, maxAttendees: 40) + * final Course groovy2 = new Course( + * title: 'Groovy', beginDate: new Date() + 2, maxAttendees: 50) + * final Course grails = new Course( + * title: 'Grails', beginDate: new Date() + 1, maxAttendees: 20) + * + * + * final List<Course> courses = [groovy, groovy2, grails] + * assert courses.last().title == 'Grails' + * + * // Use toSorted() method to sort + * final List<Course> sorted = courses.toSorted() + * + * assert sorted.first().title == 'Grails' + * assert sorted.last().title == 'Groovy' + * assert sorted.maxAttendees == [20, 50, 40] + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // Order of fields for includes determines priority when sorting + * import groovy.transform.Sortable + * import groovy.transform.ToString + * + * @Sortable(includes = ['title', 'maxAttendees']) + * // Or @Sortable(excludes = ['beginDate']) + * @ToString + * class Course { + * String title + * Date beginDate + * Integer maxAttendees + * } + * + * final Course groovy = new Course( + * title: 'Groovy', beginDate: new Date() + 7, maxAttendees: 40) + * final Course groovy2 = new Course( + * title: 'Groovy', beginDate: new Date() + 2, maxAttendees: 50) + * final Course grails = new Course( + * title: 'Grails', beginDate: new Date() + 1, maxAttendees: 20) + * + * + * final List<Course> courses = [groovy, groovy2, grails] + * + * // Use toSorted() method to sort + * final List<Course> sorted = courses.toSorted() + * + * assert sorted.first().title == 'Grails' + * assert sorted.last().title == 'Groovy' + * assert sorted.maxAttendees == [20, 40, 50] + * + * //-------------------------------------------------------------------------- + * // Static methods to create comparators. + * final Comparator byMaxAttendees = Course.comparatorByMaxAttendees() + * final List<Course> sortedByMaxAttendees = courses.sort(false, byMaxAttendees) + * + * assert sortedByMaxAttendees.maxAttendees == [20, 40, 50] + * // beginDate is not used for sorting + * assert sortedByMaxAttendees[2].beginDate < sortedByMaxAttendees[1].beginDate + * + * assert Course.declaredMethods.name.findAll { it.startsWith('comparatorBy') }.toSorted() == ['comparatorByMaxAttendees', 'comparatorByTitle'] + * + * //-------------------------------------------------------------------------- + * // Sorting by max attendees using reversed order + * import groovy.transform.Sortable + * import groovy.transform.ToString + * + * @Sortable(includes = ['points'], reversed = true) + * @ToString + * class LeaderBoardEntry { + * String team + * int points + * } + * + * + * final LeaderBoardEntry teamA = new LeaderBoardEntry(team: "Team A", points: 30) + * final LeaderBoardEntry teamB = new LeaderBoardEntry(team: "Team B", points: 80) + * final LeaderBoardEntry teamC = new LeaderBoardEntry(team: "Team C", points: 50) + * + * final List<LeaderBoardEntry> leaderBoard = [teamA, teamB, teamC].toSorted() + * + * assert leaderBoard.first().team == 'Team B' + * assert leaderBoard.last().team == 'Team A' + * assert leaderBoard.points == [80, 50, 30] + * + * </pre> + * + * @author Andres Almiray + * @author Paul King + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.SortableASTTransformation") +public @interface Sortable { + /** + * Property names to include in the comparison algorithm. + * Must not be used if 'excludes' is used. + * The default value is a special marker value indicating that no includes are defined; all properties + * are included if 'includes' remains undefined and 'excludes' is explicitly or implicitly an empty list. + */ + String[] includes() default {Undefined.STRING}; + + /** + * Property names to exclude in the comparison algorithm. + * Must not be used if 'includes' is used. + */ + String[] excludes() default {}; + + /** + * Set to true so that comparator uses reversed natural order. + * @since 2.5.0 + */ + boolean reversed() default false; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/SourceURI.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/SourceURI.java b/src/main/groovy/groovy/transform/SourceURI.java new file mode 100644 index 0000000..4104369 --- /dev/null +++ b/src/main/groovy/groovy/transform/SourceURI.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 getting the URI of the current script. + * <p> + * The type of the variable annotated with {@code @SourceURI} must be assignment compatible with {@link java.net.URI}. + * It will be used to hold a URI object that references the source for the current script. + * </p><p>By default the URI + * will be made absolute (which is to say it will have an authority) in the case where a relative path was used + * for the source of the script. If you want to leave relative URIs as relative, then set <code>allowRelative</code> + * to <code>true</code>. + * </p> + * + * Example usage: + * <pre class="groovyTestCase"> + * {@code @groovy.transform.SourceURI} def sourceURI + * + * assert sourceURI instanceof java.net.URI + * </pre> + * + * @author Jim White + * @since 2.3.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.LOCAL_VARIABLE, ElementType.FIELD}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.SourceURIASTTransformation") +public @interface SourceURI { + boolean allowRelative() default false; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/Synchronized.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Synchronized.java b/src/main/groovy/groovy/transform/Synchronized.java new file mode 100644 index 0000000..8282270 --- /dev/null +++ b/src/main/groovy/groovy/transform/Synchronized.java @@ -0,0 +1,170 @@ +/* + * 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; + +/** + * Method annotation to make a method call synchronized for concurrency handling + * with some useful baked-in conventions. + * <p> + * {@code @Synchronized} is a safer variant of the <code>synchronized</code> method modifier. + * The annotation can only be used on static and instance methods. It operates similarly to + * the <code>synchronized</code> keyword, but it locks on different objects. When used with + * an instance method, the <code>synchronized</code> keyword locks on <code>this</code>, but the annotation + * locks on a (by default automatically generated) field named <code>$lock</code>. + * If the field does not exist, it is created for you. If you annotate a static method, + * the annotation locks on a static field named <code>$LOCK</code> instead. + * <p> + * If you want, you can create these locks yourself. + * The <code>$lock</code> and <code>$LOCK</code> fields will not be generated if you create + * them yourself. You can also choose to lock on another field, by specifying its name as + * parameter to the {@code @Synchronized} annotation. In this usage variant, the lock field + * will not be created automatically, and you must explicitly create it yourself. + * <p> + * <em>Rationale:</em> Locking on <code>this</code> or your own class object can have unfortunate side-effects, + * as other code not under your control can lock on these objects as well, which can + * cause race conditions and other nasty threading-related bugs. + * <p> + * <em>Example usage:</em> + * <pre> + * class SynchronizedExample { + * private final myLock = new Object() + * + * {@code @}Synchronized + * static void greet() { + * println "world" + * } + * + * {@code @}Synchronized + * int answerToEverything() { + * return 42 + * } + * + * {@code @}Synchronized("myLock") + * void foo() { + * println "bar" + * } + * } + * </pre> + * which becomes: + * <pre> + * class SynchronizedExample { + * private static final $LOCK = new Object[0] + * private final $lock = new Object[0] + * private final myLock = new Object() + * + * static void greet() { + * synchronized($LOCK) { + * println "world" + * } + * } + * + * int answerToEverything() { + * synchronized($lock) { + * return 42 + * } + * } + * + * void foo() { + * synchronized(myLock) { + * println "bar" + * } + * } + * } + * </pre> + * + * <em>Credits:</em> this annotation is inspired by the Project Lombok annotation of the + * same name. The functionality has been kept similar to ease the learning + * curve when swapping between these two tools. + * <p> + * <em>Details:</em> If <code>$lock</code> and/or <code>$LOCK</code> are auto-generated, the fields are initialized + * with an empty <code>Object[]</code> array, and not just a new <code>Object()</code> as many snippets using + * this pattern tend to use. This is because a new <code>Object</code> is NOT serializable, but + * a 0-size array is. Therefore, using {@code @Synchronized} will not prevent your + * object from being serialized. + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * import groovy.transform.Synchronized + * + * class Util { + * private counter = 0 + * + * private def list = ['Groovy'] + * + * private Object listLock = new Object[0] + * + * @Synchronized + * void workOnCounter() { + * assert 0 == counter + * counter++ + * assert 1 == counter + * counter -- + * assert 0 == counter + * } + * + * @Synchronized('listLock') + * void workOnList() { + * assert 'Groovy' == list[0] + * list << 'Grails' + * assert 2 == list.size() + * list = list - 'Grails' + * assert 'Groovy' == list[0] + * } + * } + * + * def util = new Util() + * def tc1 = Thread.start { + * 100.times { + * util.workOnCounter() + * sleep 20 + * util.workOnList() + * sleep 10 + * } + * } + * def tc2 = Thread.start { + * 100.times { + * util.workOnCounter() + * sleep 10 + * util.workOnList() + * sleep 15 + * } + * } + * tc1.join() + * tc2.join() + * </pre> + * + * @author Paul King + * @since 1.7.3 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.SynchronizedASTTransformation") +public @interface Synchronized { + /** + * @return if a user specified lock object with the given name should be used + */ + String value () default ""; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/TailRecursive.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/TailRecursive.groovy b/src/main/groovy/groovy/transform/TailRecursive.groovy new file mode 100644 index 0000000..afbee50 --- /dev/null +++ b/src/main/groovy/groovy/transform/TailRecursive.groovy @@ -0,0 +1,87 @@ +/* + * 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 + +/** + * Method annotation used to transform methods with tail recursive calls into iterative methods automagically + * since the JVM cannot do this itself. This works for both static and non-static methods. + * <p/> + * It allows you to write a method like this: + * <pre class="groovyTestCase"> + * import groovy.transform.TailRecursive + * class Target { + * {@code @TailRecursive} + * long sumUp(long number, long sum = 0) { + * if (number == 0) + * return sum; + * sumUp(number - 1, sum + number) + * } + * } + * def target = new Target() + * assert target.sumUp(100) == 5050 + * assert target.sumUp(1000000) == 500000500000 //will blow the stack on most machines when used without {@code @TailRecursive} + * </pre> + * + * {@code @TailRecursive} is supposed to work in combination with {@code @CompileStatic} + * + * Known shortcomings: + * <ul> + * <li>Only non-void methods are currently being handled. Void methods will fail compilation. + * <li>Only direct recursion (calling the exact same method again) is supported. + * <li>Mixing of tail calls and non-tail calls is not possible. The compiler will complain if some recursive calls cannot be handled. + * <li>Checking if a recursive call is really tail-recursive is not very strict. You might run into cases where non-tail calls will be considered tail calls. + * <li>In the presence of method overloading and method overriding you might run into situations where a call is considered recursive although it really is not. + * <li>Catching {@code Throwable} around a recursive might lead to problems + * <li>Non trivial continuation passing style examples do not work. + * <li>Probably many unrecognized edge cases. + * </ul> + * + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * import groovy.transform.TailRecursive + * + * @TailRecursive + * long sizeOfList(list, counter = 0) { + * if (list.size() == 0) { + * counter + * } else { + * sizeOfList(list.tail(), counter + 1) + * } + * } + * + * // Without @TailRecursive a StackOverFlowError + * // is thrown. + * assert sizeOfList(1..10000) == 10000 + * </pre> + * + * @author Johannes Link + * @since 2.3 + */ +@Retention(RetentionPolicy.SOURCE) +@Target([ElementType.METHOD]) +@GroovyASTTransformationClass(["org.codehaus.groovy.transform.tailrec.TailRecursiveASTTransformation"]) +public @interface TailRecursive { +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/ThreadInterrupt.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/ThreadInterrupt.groovy b/src/main/groovy/groovy/transform/ThreadInterrupt.groovy new file mode 100644 index 0000000..817507a --- /dev/null +++ b/src/main/groovy/groovy/transform/ThreadInterrupt.groovy @@ -0,0 +1,148 @@ +/* + * 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 + +/** + * Allows "interrupt-safe" executions of scripts by adding Thread.currentThread().isInterrupted() + * checks 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. + * <p> + * Annotating anything in a script will cause for loops, while loops, methods, and closures to make an + * isInterruptedCheck and throw a InterruptedException if the check yields true. The annotation by default + * will apply 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>@groovy.transform.ThreadInterrupt</code> + * def scriptMethod() { + * 4.times { + * println 'executing script method...' + * } + * } + * + * class MyClass { + * def myMethod() { + * for (i in (1..10)) { + * println 'executing method...' + * } + * } + * } + * + * scriptMethod() + * new MyClass().myMethod() + * </pre> + * + * Which results in the following code being generated. Notice the checks and exceptions: + * + * <pre> + * public class script1290627909406 extends groovy.lang.Script { + * + * public java.lang.Object scriptMethod() { + * if (java.lang.Thread.currentThread().isInterrupted()) { + * throw new java.lang.InterruptedException('Execution Interrupted') + * } + * 4.times({ + * if (java.lang.Thread.currentThread().isInterrupted()) { + * throw new java.lang.InterruptedException('Execution Interrupted') + * } + * this.println('executing script method...') + * }) + * } + * } + * public class MyClass extends java.lang.Object { + * + * public java.lang.Object myMethod() { + * if (java.lang.Thread.currentThread().isInterrupted()) { + * throw new java.lang.InterruptedException('Execution Interrupted') + * } + * for (java.lang.Object i : (1..10)) { + * if (java.lang.Thread.currentThread().isInterrupted()) { + * throw new java.lang.InterruptedException('Execution Interrupted') + * } + * this.println('executing method...') + * } + * } + * } + * + * this.scriptMethod() + * new MyClass().myMethod() + * </pre> + * Additional usage examples can be found in the unit test for this class. + * + * @see TimedInterrupt + * @see ConditionalInterrupt + * @author Cedric Champeau + * @author Hamlet D'Arcy + * @author Paul King + * @since 1.8.0 + */ +@Documented +@Retention(RetentionPolicy.SOURCE) +@Target([ElementType.PACKAGE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.LOCAL_VARIABLE]) +@GroovyASTTransformationClass(["org.codehaus.groovy.transform.ThreadInterruptibleASTTransformation"]) +@interface ThreadInterrupt { + /** + * Set this to false if you have multiple classes within one source file and only + * want isInterrupted checks on some of the classes. Place annotations on the classes + * you want enhanced. Set to true (the default) for blanket coverage of isInterrupted + * 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 isInterrupted 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 isInterrupted checks on all methods, loops + * and closures within the class/script. + * + * @since 2.2.0 + * @see #applyToAllClasses() + */ + boolean applyToAllMembers() default true + + /** + * By default an isInterrupted 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 +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/TimedInterrupt.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/TimedInterrupt.groovy b/src/main/groovy/groovy/transform/TimedInterrupt.groovy new file mode 100644 index 0000000..0a0c193 --- /dev/null +++ b/src/main/groovy/groovy/transform/TimedInterrupt.groovy @@ -0,0 +1,142 @@ +/* + * 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 +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + +/** + * Allows safe timed executions of scripts by adding elapsed time checks into loops (for, while) + * and at the start of closures and methods and throwing an exception if a timeout occurs. + * <p> + * This is especially useful when executing foreign scripts that you do not have control over. + * Inject this transformation into a script that you want to timeout after a specified amount of time. + * <p> + * Annotating anything in a script will cause for loops, while loops, methods, and closures to make an + * elapsed time check and throw a TimeoutException if the check yields true. The annotation by default + * will apply to any classes defined in the script as well. Annotating 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. Static methods and static fields are ignored. + * <p> + * The following is sample usage of the annotation forcing the script to timeout after 5 minutes (300 seconds): + * + * <pre> + * import groovy.transform.TimedInterrupt + * import java.util.concurrent.TimeUnit + * + * {@code @TimedInterrupt}(value = 300L, unit = TimeUnit.SECONDS) + * class MyClass { + * def method() { + * println '...' + * } + * } + * </pre> + * This sample script will be transformed at compile time to something that resembles this: + * <pre> + * import java.util.concurrent.TimeUnit + * import java.util.concurrent.TimeoutException + * + * public class MyClass { + * // XXXXXX below is a placeholder for a hashCode value at runtime + * final private long timedInterruptXXXXXX$expireTime + * final private java.util.Date timedInterruptXXXXXX$startTime + * + * public MyClass() { + * timedInterruptXXXXXX$expireTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(300, TimeUnit.SECONDS) + * timedInterruptXXXXXX$startTime = new java.util.Date() + * } + * + * public java.lang.Object method() { + * if (timedInterruptXXXXXX$expireTime < System.nanoTime()) { + * throw new TimeoutException('Execution timed out after 300 units. Start time: ' + timedInterruptXXXXXX$startTime) + * } + * return this.println('...') + * } + * } + * </pre> + * See the unit test for this class for additional examples. + * + * @author Hamlet D'Arcy + * @author Cedric Champeau + * @author Paul King + * @see ThreadInterrupt + * @see ConditionalInterrupt + * @since 1.8.0 + */ +@Documented +@Retention(RetentionPolicy.SOURCE) +@Target([ ElementType.PACKAGE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.LOCAL_VARIABLE]) +@GroovyASTTransformationClass(["org.codehaus.groovy.transform.TimedInterruptibleASTTransformation"]) +@interface TimedInterrupt { + /** + * Set this to false if you have multiple classes within one source file and only want + * timeout checks on some of the classes (or you want different time constraints on different classes). + * Place an annotation with appropriate parameters on each class you want enhanced. + * Set to true (the default) for blanket coverage of timeout 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 timeout checks on some of them (or you want different time constraints on different methods/closures). + * Place annotations with appropriate parameters 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 timeout checks on all methods, loops + * and closures within the class/script. + * + * @since 2.2.0 + * @see #applyToAllClasses() + */ + boolean applyToAllMembers() default true + + /** + * By default a time 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 + + /** + * The maximum elapsed time the script will be allowed to run for. By default it is measure in seconds + */ + long value() + + /** + * The TimeUnit of the value parameter. By default it is TimeUnit.SECONDS. + */ + TimeUnit unit() default TimeUnit.SECONDS + + /** + * The type of exception thrown when timeout is reached. + */ + Class thrown() default TimeoutException +} + http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/ToString.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/ToString.java b/src/main/groovy/groovy/transform/ToString.java new file mode 100644 index 0000000..968e7f0 --- /dev/null +++ b/src/main/groovy/groovy/transform/ToString.java @@ -0,0 +1,339 @@ +/* + * 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 toString()} methods in classes. + * The {@code @ToString} annotation instructs the compiler to execute an + * AST transformation which adds the necessary toString() method. + * <p> + * It allows you to write classes in this shortened form: + * <pre> + * {@code @ToString} + * class Customer { + * String first, last + * int age + * Date since = new Date() + * Collection favItems + * private answer = 42 + * } + * println new Customer(first:'Tom', last:'Jones', age:21, favItems:['Books', 'Games']) + * </pre> + * Which will have this output: + * <pre> + * Customer(Tom, Jones, 21, Wed Jul 14 23:57:14 EST 2010, [Books, Games]) + * </pre> + * There are numerous options to customize the format of the generated output. + * E.g. if you change the first annotation to: + * <pre> + * {@code @ToString(includeNames=true)} + * </pre> + * Then the output will be: + * <pre> + * Customer(first:Tom, last:Jones, age:21, since:Wed Jul 14 23:57:50 EST 2010, favItems:[Books, Games]) + * </pre> + * Or if you change the first annotation to: + * <pre> + * {@code @ToString(includeNames=true,includeFields=true,excludes="since,favItems")} + * </pre> + * Then the output will be: + * <pre> + * Customer(first:Tom, last:Jones, age:21, answer:42) + * </pre> + * If you have this example: + * <pre class="groovyTestCase"> + * import groovy.transform.ToString + * {@code @ToString} class NamedThing { + * String name + * } + * {@code @ToString}(includeNames=true,includeSuper=true) + * class AgedThing extends NamedThing { + * int age + * } + * String agedThingAsString = new AgedThing(name:'Lassie', age:5).toString() + * assert agedThingAsString == 'AgedThing(age:5, super:NamedThing(Lassie))' + * </pre> + * {@code @ToString} can also be used in conjunction with {@code @Canonical} and {@code @Immutable}. + * <p> + * If you want to omit fields or properties referring to <tt>null</tt>, you can use the <tt>ignoreNulls</tt> flag: + * <pre class="groovyTestCase"> + * import groovy.transform.ToString + * {@code @ToString(ignoreNulls = true)} class NamedThing { + * String name + * } + * assert new NamedThing(name: null).toString() == 'NamedThing()' + * </pre> + * <p> + * By default the fully-qualified class name is used as part of the generated toString. + * If you want to exclude the package, you can set the includePackage flag to false, e.g.: + * <pre> + * package my.company + * import groovy.transform.ToString + * {@code @ToString(includePackage = false)} class NamedThing { + * String name + * } + * println new NamedThing(name: "Lassie") + * </pre> + * Which results in: + * <pre> + * NamedThing(name: Lassie) + * </pre> + * If the includePackage flag is {@code true} (the default), then the output will be: + * <pre> + * my.company.NamedThing(name: Lassie) + * </pre> + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // Most simple implementation of toString. + * import groovy.transform.ToString + * + * {@code @ToString} + * class Person { + * String name + * List likes + * private boolean active = false + * } + * + * def person = new Person(name: 'mrhaki', likes: ['Groovy', 'Java']) + * + * assert person.toString() == 'Person(mrhaki, [Groovy, Java])' + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // includeNames to output the names of the properties. + * import groovy.transform.ToString + * + * @ToString(includeNames=true) + * class Person { + * String name + * List likes + * private boolean active = false + * } + * + * def person = new Person(name: 'mrhaki', likes: ['Groovy', 'Java']) + * + * assert person.toString() == 'Person(name:mrhaki, likes:[Groovy, Java])' + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // includeFields to not only output properties, but also field values. + * import groovy.transform.ToString + * + * @ToString(includeNames=true, includeFields=true) + * class Person { + * String name + * List likes + * private boolean active = false + * } + * + * def person = new Person(name: 'mrhaki', likes: ['Groovy', 'Java']) + * + * assert person.toString() == 'Person(name:mrhaki, likes:[Groovy, Java], active:false)' + * </pre> + * <pre> + * //-------------------------------------------------------------------------- + * // Use includeSuper to include properties from super class in output. + * import groovy.transform.ToString + * + * @ToString(includeNames=true) + * class Person { + * String name + * List likes + * private boolean active = false + * } + * + * @ToString(includeSuper=true, includeNames=true) + * class Student extends Person { + * List courses + * } + * + * def student = new Student(name: 'mrhaki', likes: ['Groovy', 'Java'], courses: ['IT', 'Business']) + * + * assert student.toString() == 'Student(courses:[IT, Business], super:Person(name:mrhaki, likes:[Groovy, Java]))' + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // excludes active field and likes property from output + * import groovy.transform.ToString + * + * @ToString(includeNames=true, includeFields=true, excludes='active,likes') + * class Person { + * String name + * List likes + * private boolean active = false + * } + * + * def person = new Person(name: 'mrhaki', likes: ['Groovy', 'Java']) + * + * assert person.toString() == 'Person(name:mrhaki)' + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // Don't include the package name in the output + * package com.mrhaki.blog.groovy + * + * import groovy.transform.* + * + * @ToString(includePackage=false) + * class Course { + * String title + * Integer maxAttendees + * } + * + * final Course course = new Course(title: 'Groovy 101', maxAttendees: 200) + * + * assert course.toString() == 'Course(Groovy 101, 200)' + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // Don't use properties with null value. + * package com.mrhaki.blog.groovy + * + * import groovy.transform.* + * + * @ToString(ignoreNulls=true) + * class Course { + * String title + * Integer maxAttendees + * } + * + * final Course course = new Course(title: 'Groovy 101') + * + * assert course.toString() == 'com.mrhaki.blog.groovy.Course(Groovy 101)' + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // Cache toString() result. + * package com.mrhaki.blog.groovy + * + * import groovy.transform.* + * + * @ToString(cache=true) + * class Course { + * String title + * Integer maxAttendees + * } + * + * Course course = new Course(title: 'Groovy 101', maxAttendees: 200) + * + * assert course.toString() == 'com.mrhaki.blog.groovy.Course(Groovy 101, 200)' + * + * // Value change will not be reflected in toString(). + * course.title = 'Grails with REST' + * + * assert course.toString() == 'com.mrhaki.blog.groovy.Course(Groovy 101, 200)' + * assert course.title == 'Grails with REST' + * </pre> + * + * @author Paul King + * @author Andre Steingress + * @see groovy.transform.Immutable + * @see groovy.transform.Canonical + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.ToStringASTTransformation") +public @interface ToString { + /** + * List of field and/or property names to exclude from generated toString. + * 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 generated toString. + * 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. + * The special name 'super' can be used instead of using the 'includeSuper' flag. + */ + String[] includes() default {Undefined.STRING}; + + /** + * Whether to include the toString() of super in the generated toString. + */ + boolean includeSuper() default false; + + /** + * Whether to include super properties in the generated toString. + * @since 2.4.0 + */ + boolean includeSuperProperties() default false; + + /** + * Whether to include names of properties/fields in the generated toString. + */ + boolean includeNames() default false; + + /** + * Include fields as well as properties in the generated toString. + */ + boolean includeFields() default false; + + /** + * Don't display any fields or properties with value <tt>null</tt>. + */ + boolean ignoreNulls() default false; + + /** + * Whether to include the fully-qualified class name (i.e. including + * the package) or just the simple class name in the generated toString. + * @since 2.0.6 + */ + boolean includePackage() default true; + + /** + * Whether to include all properties (as per the JavaBean spec) in the generated toString. + * Groovy recognizes any field-like definitions with no explicit visibility as property definitions + * and always includes them in the {@code @ToString} generated toString (as well as auto-generating the + * appropriate getters and setters). Groovy also treats any explicitly created getXxx() or isYyy() + * methods as property getters as per the JavaBean specification. Old versions of Groovy did not. + * So set this flag to false for the old behavior or if you want to explicitly exclude such properties. + * + * @since 2.5 + */ + boolean allProperties() default true; + + /** + * Whether to cache toString() calculations. You should only set this to true if + * you know the object is immutable (or technically mutable but never changed). + * @since 2.1.0 + */ + boolean cache() default false; + + /** + * Whether to include all fields and/or properties in the generated toString, including those with names that + * are considered internal. + * + * @since 2.5.0 + */ + boolean allNames() default false; +}
