http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/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/10110145/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/10110145/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/10110145/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/10110145/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/10110145/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/10110145/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/10110145/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/10110145/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/10110145/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/10110145/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..25fccab --- /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.0 + */ + 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; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/Trait.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Trait.java b/src/main/groovy/groovy/transform/Trait.java new file mode 100644 index 0000000..de88180 --- /dev/null +++ b/src/main/groovy/groovy/transform/Trait.java @@ -0,0 +1,38 @@ +/* + * 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; + +/** + * Used to mark a class as being a trait. + * + * @since 2.3.0 + */ [email protected] +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.trait.TraitASTTransformation") +public @interface Trait { +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/TupleConstructor.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/TupleConstructor.java b/src/main/groovy/groovy/transform/TupleConstructor.java new file mode 100644 index 0000000..2cd2be7 --- /dev/null +++ b/src/main/groovy/groovy/transform/TupleConstructor.java @@ -0,0 +1,278 @@ +/* + * 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 tuple constructors in classes. + * Should be used with care with other annotations which create constructors - see "Known + * Limitations" for more details. + * <p> + * It allows you to write classes in this shortened form: + * <pre class="groovyTestCase"> + * {@code @groovy.transform.TupleConstructor} class Customer { + * String first, last + * int age + * Date since + * Collection favItems + * } + * def c1 = new Customer(first:'Tom', last:'Jones', age:21, since:new Date(), favItems:['Books', 'Games']) + * def c2 = new Customer('Tom', 'Jones', 21, new Date(), ['Books', 'Games']) + * def c3 = new Customer('Tom', 'Jones') + * </pre> + * The {@code @TupleConstructor} annotation instructs the compiler to execute an + * AST transformation which adds the necessary constructor method to your class. + * <p> + * A tuple constructor is created with a parameter for each property (and optionally field and + * super properties). + * A default value is provided (using Java's default values) for all parameters in the constructor. + * Groovy's normal conventions then allows any number of parameters to be left off the end of the parameter list + * including all of the parameters - giving a no-arg constructor which can be used with the map-style naming conventions. + * <p> + * The order of parameters is given by the properties of any super classes with most super first + * (if {@code includeSuperProperties} is set) followed by the properties of the class followed + * by the fields of the class (if {@code includeFields} is set). Within each grouping the order + * is as attributes appear within the respective class. + * <p>More examples:</p> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * import groovy.transform.TupleConstructor + * + * @TupleConstructor() + * class Person { + * String name + * List likes + * private boolean active = false + * } + * + * def person = new Person('mrhaki', ['Groovy', 'Java']) + * + * assert person.name == 'mrhaki' + * assert person.likes == ['Groovy', 'Java'] + * + * person = new Person('mrhaki') + * + * assert person.name == 'mrhaki' + * assert !person.likes + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // includeFields in the constructor creation. + * import groovy.transform.TupleConstructor + * + * @TupleConstructor(includeFields=true) + * class Person { + * String name + * List likes + * private boolean active = false + * + * boolean isActivated() { active } + * } + * + * def person = new Person('mrhaki', ['Groovy', 'Java'], true) + * + * assert person.name == 'mrhaki' + * assert person.likes == ['Groovy', 'Java'] + * assert person.activated + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // use force attribute to force creation of constructor + * // even if we define our own constructors. + * import groovy.transform.TupleConstructor + * + * @TupleConstructor(force=true) + * class Person { + * String name + * List likes + * private boolean active = false + * + * Person(boolean active) { + * this.active = active + * } + * + * boolean isActivated() { active } + * } + * + * def person = new Person('mrhaki', ['Groovy', 'Java']) + * + * assert person.name == 'mrhaki' + * assert person.likes == ['Groovy', 'Java'] + * assert !person.activated + * + * person = new Person(true) + * + * assert person.activated + * </pre> + * <pre class="groovyTestCase"> + * //-------------------------------------------------------------------------- + * // include properties and fields from super class. + * import groovy.transform.TupleConstructor + * + * @TupleConstructor(includeFields=true) + * class Person { + * String name + * List likes + * private boolean active = false + * + * boolean isActivated() { active } + * } + * + * @TupleConstructor(callSuper=true, includeSuperProperties=true, includeSuperFields=true) + * class Student extends Person { + * List courses + * } + * + * def student = new Student('mrhaki', ['Groovy', 'Java'], true, ['IT']) + * + * assert student.name == 'mrhaki' + * assert student.likes == ['Groovy', 'Java'] + * assert student.activated + * assert student.courses == ['IT'] + * </pre> + * <p> + * Known Limitations: + * <ul> + * <li>This AST transform might become a no-op if you are defining your own constructors or + * combining with other AST transforms which create constructors (e.g. {@code @InheritConstructors}); + * the order in which the particular transforms are processed becomes important in that case. + * See the {@code force} attribute for further details about customizing this behavior.</li> + * <li>This AST transform normally uses default parameter values which creates multiple constructors under + * the covers. You should use with care if you are defining your own constructors or + * combining with other AST transforms which create constructors (e.g. {@code @InheritConstructors}); + * the order in which the particular transforms are processed becomes important in that case. + * See the {@code defaults} attribute for further details about customizing this behavior.</li> + * <li>Groovy's normal map-style naming conventions will not be available if the first property (or field) + * has type {@code LinkedHashMap} or if there is a single Map, AbstractMap or HashMap property (or field)</li> + * </ul> + * + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.TYPE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.TupleConstructorASTTransformation") +public @interface TupleConstructor { + /** + * 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 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 fields from super classes in the constructor. + */ + boolean includeSuperFields() default false; + + /** + * Include properties from super classes in the constructor. + */ + boolean includeSuperProperties() default false; + + /** + * Should super properties be called within a call to the parent constructor + * rather than set as properties. Typically used in combination with {@code includeSuperProperties}. + * Can't be true if using {@code pre} with a {@code super} first statement. + */ + boolean callSuper() default false; + + /** + * By default, this annotation becomes a no-op if you provide your own constructor. + * By setting {@code force=true} then the tuple constructor(s) will be added regardless of + * whether existing constructors exist. It is up to you to avoid creating duplicate constructors. + */ + boolean force() default false; + + /** + * Used to set whether default value processing is enabled (the default) or disabled. + * + * By default, every constructor parameter is given a default value. This value will + * be Java's default for primitive types (zero or false) and null for Objects, unless + * an initial value is given when declaring the property or field. A consequence of + * this design is that you can leave off parameters from the right if the default + * value will suffice. As far as Java interoperability is concerned, Groovy will + * create additional constructors under the covers representing the constructors + * with parameters left off, all the way from the constructor with all arguments + * to the no-arg constructor. + * + * However, when set to false, default values are not allowed for properties and fields. + * Only the constructor containing all arguments will be provided. + * In particular, a no-arg constructor won't be provided and since this is currently + * used by Groovy when using named-arguments, the named-argument style won't be available. + */ + boolean defaults() default true; + + /** + * 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; + + /** + * Whether to include all fields and/or properties within the constructor, including those with names that are + * considered internal. + * + * @since 2.5.0 + */ + boolean allNames() default false; + + /** + * A Closure containing statements which will be prepended to the generated constructor. The first statement + * within the Closure may be {@code super(someArgs)} in which case the no-arg super constructor won't be called. + * + * @since 2.5.0 + */ + 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. + * + * @since 2.5.0 + */ + Class post() default Undefined.CLASS.class; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/TypeChecked.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/TypeChecked.java b/src/main/groovy/groovy/transform/TypeChecked.java new file mode 100644 index 0000000..b902f3f --- /dev/null +++ b/src/main/groovy/groovy/transform/TypeChecked.java @@ -0,0 +1,70 @@ +/* + * 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; + +/** + * This will let the Groovy compiler use compile time checks in the style of Java. + * @author <a href="mailto:[email protected]">Jochen "blackdrag" Theodorou</a> + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ ElementType.METHOD, ElementType.TYPE, + ElementType.CONSTRUCTOR +}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.StaticTypesTransformation") +public @interface TypeChecked { + 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 {}; + + /** + * This annotation is added by @TypeChecked on methods which have type checking turned on. + * It is used to embed type information into binary, so that the type checker can use this information, + * if available, for precompiled classes. + */ + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface TypeCheckingInfo { + /** + * Returns the type checker information protocol number. This is used if the format of the + * string used in {@link #inferredType()} changes. + * @return the protocol version + */ + int version() default 0; + + /** + * An encoded type information. + * @return the inferred type + */ + String inferredType(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/TypeCheckingMode.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/TypeCheckingMode.java b/src/main/groovy/groovy/transform/TypeCheckingMode.java new file mode 100644 index 0000000..075bd71 --- /dev/null +++ b/src/main/groovy/groovy/transform/TypeCheckingMode.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * This enumeration can be used whenever it is preferred to annotate a class as + * {@link TypeChecked} in general, but where only one or more methods are "dynamic". This allows the user + * to annotate the class itself then annotate only the methods which require exclusion. + * + * @author Cedric Champeau + */ +public enum TypeCheckingMode { + PASS, + SKIP +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/Undefined.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/Undefined.java b/src/main/groovy/groovy/transform/Undefined.java new file mode 100644 index 0000000..35b360d --- /dev/null +++ b/src/main/groovy/groovy/transform/Undefined.java @@ -0,0 +1,37 @@ +/* + * 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.ast.ClassNode; + +/** + * Java doesn't allow you to have null as an attribute value. It wants you to indicate what you really + * mean by null, so that is what we do here - as ugly as it is. + */ +public final class Undefined { + private Undefined() {} + public static final String STRING = "<DummyUndefinedMarkerString-DoNotUse>"; + public static final class CLASS {} + public static final class EXCEPTION extends RuntimeException { + private static final long serialVersionUID = -3960500360386581172L; + } + public static boolean isUndefined(String other) { return STRING.equals(other); } + public static boolean isUndefined(ClassNode other) { return CLASS.class.getName().equals(other.getName()); } + public static boolean isUndefinedException(ClassNode other) { return EXCEPTION.class.getName().equals(other.getName()); } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/WithReadLock.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/WithReadLock.java b/src/main/groovy/groovy/transform/WithReadLock.java new file mode 100644 index 0000000..475786a --- /dev/null +++ b/src/main/groovy/groovy/transform/WithReadLock.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; + +/** + * This annotation is used in conjunction with {@link WithWriteLock} to support read and write synchronization on a method. + * <p> + * To use this annotation, declare {@code @WithReadLock} on your method. The method may be either an instance method or + * a static method. The resulting method will allow multiple threads to read the information at the same time. + * However, if some other method obtains a write lock, then this method will force callers to wait until the write is complete. + * <p> + * This annotation is a declarative wrapper around the JDK's <code>java.util.concurrent.locks.ReentrantReadWriteLock</code>. + * Objects containing this annotation will have a ReentrantReadWriteLock field named <code>$reentrantLock</code> added to the class, + * and method access is protected by the lock. If the method is static then the field is static and named <code>$REENTRANTLOCK</code>. + * <p> + * The annotation takes an optional parameter for the name of the field. This field must exist on the class and must be + * of type ReentrantReadWriteLock. + * <p> + * To understand how this annotation works, it is convenient to think in terms of the source code it replaces. The following + * is a typical usage of this annotation from Groovy: + * <pre> + * import groovy.transform.*; + * + * public class ResourceProvider { + * + * private final Map<String, String> data = new HashMap<String, String>(); + * + * {@code @WithReadLock} + * public String getResource(String key) throws Exception { + * return data.get(key); + * } + * + * {@code @WithWriteLock} + * public void refresh() throws Exception { + * //reload the resources into memory + * } + * } + * </pre> + * As part of the Groovy compiler, code resembling this is produced: + * <pre> + * import java.util.concurrent.locks.ReentrantReadWriteLock; + * import java.util.concurrent.locks.ReadWriteLock; + * + * public class ResourceProvider { + * + * private final ReadWriteLock $reentrantlock = new ReentrantReadWriteLock(); + * private final Map<String, String> data = new HashMap<String, String>(); + * + * public String getResource(String key) throws Exception { + * $reentrantlock.readLock().lock(); + * try { + * return data.get(key); + * } finally { + * $reentrantlock.readLock().unlock(); + * } + * } + * + * public void refresh() throws Exception { + * $reentrantlock.writeLock().lock(); + * try { + * //reload the resources into memory + * } finally { + * $reentrantlock.writeLock().unlock(); + * } + * } + * } + * </pre> + * + * @author Hamlet D'Arcy + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.ReadWriteLockASTTransformation") +public @interface WithReadLock { + /** + * @return if a user specified lock object with the given name should be used + * the lock object must exist. If the annotated method is static then the + * lock object must be static. If the annotated method is not static then + * the lock object must not be static. + */ + String value () default ""; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/transform/WithWriteLock.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/transform/WithWriteLock.java b/src/main/groovy/groovy/transform/WithWriteLock.java new file mode 100644 index 0000000..1eeb7f0 --- /dev/null +++ b/src/main/groovy/groovy/transform/WithWriteLock.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; + +/** + * This annotation is used in conjunction with {@link WithReadLock} to support read and write synchronization on a method. + * <p> + * To use this annotation, declare {@code @WithWriteLock} on your method. The method may be either an instance method or + * a static method. The resulting method will allow only one thread access to the method at a time, and will wait to access + * the method until any other read locks have been released. + * <p> + * This annotation is a declarative wrapper around the JDK's <code>java.util.concurrent.locks.ReentrantReadWriteLock</code>. + * Objects containing this annotation will have a ReentrantReadWriteLock field named <code>$reentrantLock</code> added to the class, + * and method access is protected by the lock. If the method is static then the field is static and named <code>$REENTRANTLOCK</code>. + * <p> + * The annotation takes an optional parameter for the name of the field. This field must exist on the class and must be + * of type ReentrantReadWriteLock. + * <p> + * To understand how this annotation works, it is convenient to think in terms of the source code it replaces. The following + * is a typical usage of this annotation from Groovy: + * <pre> + * import groovy.transform.*; + * + * public class ResourceProvider { + * + * private final Map<String, String> data = new HashMap<String, String>(); + * + * {@code @WithReadLock} + * public String getResource(String key) throws Exception { + * return data.get(key); + * } + * + * {@code @WithWriteLock} + * public void refresh() throws Exception { + * //reload the resources into memory + * } + * } + * </pre> + * As part of the Groovy compiler, code resembling this is produced: + * <pre> + * import java.util.concurrent.locks.ReentrantReadWriteLock; + * import java.util.concurrent.locks.ReadWriteLock; + * + * public class ResourceProvider { + * + * private final ReadWriteLock $reentrantlock = new ReentrantReadWriteLock(); + * private final Map<String, String> data = new HashMap<String, String>(); + * + * public String getResource(String key) throws Exception { + * $reentrantlock.readLock().lock(); + * try { + * return data.get(key); + * } finally { + * $reentrantlock.readLock().unlock(); + * } + * } + * + * public void refresh() throws Exception { + * $reentrantlock.writeLock().lock(); + * try { + * //reload the resources into memory + * } finally { + * $reentrantlock.writeLock().unlock(); + * } + * } + * } + * </pre> + * + * @author Hamlet D'Arcy + * @since 1.8.0 + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.METHOD}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.ReadWriteLockASTTransformation") +public @interface WithWriteLock { + /** + * @return if a user specified lock object with the given name should be used + * the lock object must exist. If the annotated method is static then the + * lock object must be static. If the annotated method is not static then + * the lock object must not be static. + */ + String value () default ""; +}
