| /* |
| * 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. |
| */ |
| import groovy.test.GroovyTestCase |
| |
| class ClassDesignASTTransformsTest extends GroovyTestCase { |
| |
| void testDelegateTransformation() { |
| assertScript ''' |
| // tag::delegating_class[] |
| class Event { |
| @Delegate Date when |
| String title |
| } |
| // end::delegating_class[] |
| |
| /* |
| // tag::delegating_class_generated[] |
| class Event { |
| Date when |
| String title |
| boolean before(Date other) { |
| when.before(other) |
| } |
| // ... |
| } |
| // end::delegating_class_generated[] |
| */ |
| |
| // tag::delegation_assert[] |
| def ev = new Event(title:'Groovy keynote', when: Date.parse('yyyy/MM/dd', '2013/09/10')) |
| def now = new Date() |
| assert ev.before(now) |
| // end::delegation_assert[] |
| ''' |
| assertScript ''' |
| // tag::delegate_example_interfaces[] |
| interface Greeter { void sayHello() } |
| class MyGreeter implements Greeter { void sayHello() { println 'Hello!'} } |
| |
| class DelegatingGreeter { // no explicit interface |
| @Delegate MyGreeter greeter = new MyGreeter() |
| } |
| def greeter = new DelegatingGreeter() |
| assert greeter instanceof Greeter // interface was added transparently |
| // end::delegate_example_interfaces[] |
| ''' |
| assertScript ''' |
| // tag::delegate_deprecated_header[] |
| class WithDeprecation { |
| @Deprecated |
| void foo() {} |
| } |
| class WithoutDeprecation { |
| @Deprecated |
| void bar() {} |
| } |
| class Delegating { |
| @Delegate(deprecated=true) WithDeprecation with = new WithDeprecation() |
| @Delegate WithoutDeprecation without = new WithoutDeprecation() |
| } |
| def d = new Delegating() |
| d.foo() // passes thanks to deprecated=true |
| // end::delegate_deprecated_header[] |
| try { |
| // tag::delegate_deprecated_footer[] |
| d.bar() // fails because of @Deprecated |
| // end::delegate_deprecated_footer[] |
| } catch (e ) {} |
| ''' |
| |
| assertScript ''' |
| // tag::delegate_method[] |
| class Test { |
| private int robinCount = 0 |
| private List<List> items = [[0], [1], [2]] |
| |
| @Delegate |
| List getRoundRobinList() { |
| items[robinCount++ % items.size()] |
| } |
| |
| void checkItems(List<List> testValue) { |
| assert items == testValue |
| } |
| } |
| // end::delegate_method[] |
| |
| // tag::delegate_method_usage[] |
| def t = new Test() |
| t << 'fee' |
| t << 'fi' |
| t << 'fo' |
| t << 'fum' |
| t.checkItems([[0, 'fee', 'fum'], [1, 'fi'], [2, 'fo']]) |
| // end::delegate_method_usage[] |
| ''' |
| |
| assertScript ''' |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target([ElementType.METHOD]) |
| @interface Transactional {} |
| // tag::delegate_example_annotations[] |
| class WithAnnotations { |
| @Transactional |
| void method() { |
| } |
| } |
| class DelegatingWithoutAnnotations { |
| @Delegate WithAnnotations delegate |
| } |
| class DelegatingWithAnnotations { |
| @Delegate(methodAnnotations = true) WithAnnotations delegate |
| } |
| def d1 = new DelegatingWithoutAnnotations() |
| def d2 = new DelegatingWithAnnotations() |
| assert d1.class.getDeclaredMethod('method').annotations.length==1 |
| assert d2.class.getDeclaredMethod('method').annotations.length==2 |
| // end::delegate_example_annotations[] |
| ''' |
| |
| assertScript ''' |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target([ElementType.PARAMETER]) |
| @interface NotNull {} |
| // tag::delegate_example_parameter_annotations[] |
| class WithAnnotations { |
| void method(@NotNull String str) { |
| } |
| } |
| class DelegatingWithoutAnnotations { |
| @Delegate WithAnnotations delegate |
| } |
| class DelegatingWithAnnotations { |
| @Delegate(parameterAnnotations = true) WithAnnotations delegate |
| } |
| def d1 = new DelegatingWithoutAnnotations() |
| def d2 = new DelegatingWithAnnotations() |
| assert d1.class.getDeclaredMethod('method',String).parameterAnnotations[0].length==0 |
| assert d2.class.getDeclaredMethod('method',String).parameterAnnotations[0].length==1 |
| // end::delegate_example_parameter_annotations[] |
| ''' |
| |
| assertScript ''' |
| // tag::delegate_example_excludes_header[] |
| class Worker { |
| void task1() {} |
| void task2() {} |
| } |
| class Delegating { |
| @Delegate(excludes=['task2']) Worker worker = new Worker() |
| } |
| def d = new Delegating() |
| d.task1() // passes |
| // end::delegate_example_excludes_header[] |
| groovy.test.GroovyAssert.shouldFail { |
| // tag::delegate_example_excludes_footer[] |
| d.task2() // fails because method is excluded |
| // end::delegate_example_excludes_footer[] |
| } |
| |
| ''' |
| |
| assertScript ''' |
| // tag::delegate_example_includes_header[] |
| class Worker { |
| void task1() {} |
| void task2() {} |
| } |
| class Delegating { |
| @Delegate(includes=['task1']) Worker worker = new Worker() |
| } |
| def d = new Delegating() |
| d.task1() // passes |
| // end::delegate_example_includes_header[] |
| groovy.test.GroovyAssert.shouldFail { |
| // tag::delegate_example_includes_footer[] |
| d.task2() // fails because method is not included |
| // end::delegate_example_includes_footer[] |
| } |
| |
| ''' |
| assertScript ''' |
| // tag::delegate_example_includeTypes_header[] |
| interface AppendBooleanSelector { |
| StringBuilder append(boolean b) |
| } |
| interface AppendFloatSelector { |
| StringBuilder append(float b) |
| } |
| class NumberBooleanBuilder { |
| @Delegate(includeTypes=AppendBooleanSelector, interfaces=false) |
| StringBuilder nums = new StringBuilder() |
| @Delegate(includeTypes=[AppendFloatSelector], interfaces=false) |
| StringBuilder bools = new StringBuilder() |
| String result() { "${nums.toString()} ~ ${bools.toString()}" } |
| } |
| def b = new NumberBooleanBuilder() |
| b.append(true) |
| b.append(3.14f) |
| b.append(false) |
| b.append(0.0f) |
| assert b.result() == "truefalse ~ 3.140.0" |
| // end::delegate_example_includeTypes_header[] |
| groovy.test.GroovyAssert.shouldFail { |
| // tag::delegate_example_includeTypes_footer[] |
| b.append(3.5d) // would fail because we didn't include append(double) |
| // end::delegate_example_includeTypes_footer[] |
| } |
| ''' |
| assertScript ''' |
| // tag::delegate_example_excludeTypes[] |
| interface AppendStringSelector { |
| StringBuilder append(String str) |
| } |
| class UpperStringBuilder { |
| @Delegate(excludeTypes=AppendStringSelector) |
| StringBuilder sb1 = new StringBuilder() |
| |
| @Delegate(includeTypes=AppendStringSelector) |
| StringBuilder sb2 = new StringBuilder() |
| |
| String toString() { sb1.toString() + sb2.toString().toUpperCase() } |
| } |
| def usb = new UpperStringBuilder() |
| usb.append(3.5d) |
| usb.append('hello') |
| usb.append(true) |
| assert usb.toString() == '3.5trueHELLO' |
| // end::delegate_example_excludeTypes[] |
| ''' |
| |
| assertScript ''' |
| // tag::delegate_example_allNames[] |
| class Worker { |
| void task$() {} |
| } |
| class Delegating { |
| @Delegate(allNames=true) Worker worker = new Worker() |
| } |
| def d = new Delegating() |
| d.task$() //passes |
| // end::delegate_example_allNames[] |
| ''' |
| } |
| |
| void testImmutable() { |
| assertScript ''' |
| // tag::immutable_simple[] |
| import groovy.transform.Immutable |
| |
| @Immutable |
| class Point { |
| int x |
| int y |
| } |
| // end::immutable_simple[] |
| |
| def p = new Point(x:1,y:2) |
| assert p.toString() == 'Point(1, 2)' // @ToString equivalent |
| try { |
| p.x = 2 |
| } catch (ReadOnlyPropertyException rope) { |
| println 'ReadOnly property' |
| } |
| ''' |
| |
| assertScript ''' |
| // tag::immutable_example_knownimmutableclasses[] |
| import groovy.transform.Immutable |
| import groovy.transform.TupleConstructor |
| |
| @TupleConstructor |
| final class Point { |
| final int x |
| final int y |
| public String toString() { "($x,$y)" } |
| } |
| |
| @Immutable(knownImmutableClasses=[Point]) |
| class Triangle { |
| Point a,b,c |
| } |
| // end::immutable_example_knownimmutableclasses[] |
| |
| def p = new Triangle(a:[47,22], b:[12,12],c:[88,17]) |
| assert p.toString() == 'Triangle((47,22), (12,12), (88,17))' // @ToString equivalent |
| try { |
| p.a = [0,0] |
| } catch (ReadOnlyPropertyException rope) { |
| println 'ReadOnly property' |
| } |
| ''' |
| |
| |
| assertScript ''' |
| // tag::immutable_example_knownimmutables[] |
| import groovy.transform.Immutable |
| import groovy.transform.TupleConstructor |
| |
| @TupleConstructor |
| final class Point { |
| final int x |
| final int y |
| public String toString() { "($x,$y)" } |
| } |
| |
| @Immutable(knownImmutables=['a','b','c']) |
| class Triangle { |
| Point a,b,c |
| } |
| // end::immutable_example_knownimmutables[] |
| |
| def p = new Triangle(a:[47,22], b:[12,12],c:[88,17]) |
| assert p.toString() == 'Triangle((47,22), (12,12), (88,17))' // @ToString equivalent |
| try { |
| p.a = [0,0] |
| } catch (ReadOnlyPropertyException rope) { |
| println 'ReadOnly property' |
| } |
| ''' |
| |
| assertScript ''' |
| // tag::immutable_example_copyWith[] |
| import groovy.transform.Immutable |
| |
| @Immutable( copyWith=true ) |
| class User { |
| String name |
| Integer age |
| } |
| |
| def bob = new User( 'bob', 43 ) |
| def alice = bob.copyWith( name:'alice' ) |
| assert alice.name == 'alice' |
| assert alice.age == 43 |
| // end::immutable_example_copyWith[] |
| |
| try { |
| bob.name = 'alice' |
| } catch (ReadOnlyPropertyException rope) { |
| println 'ReadOnly property' |
| } |
| ''' |
| } |
| |
| void testMemoized() { |
| assertScript ''' |
| import groovy.transform.Memoized |
| |
| // tag::memoized_long_computation[] |
| long longComputation(int seed) { |
| // slow computation |
| Thread.sleep(100*seed) |
| System.nanoTime() |
| } |
| // end::memoized_long_computation[] |
| // tag::memoized_long_computation_asserts[] |
| def x = longComputation(1) |
| def y = longComputation(1) |
| assert x!=y |
| // end::memoized_long_computation_asserts[] |
| ''' |
| |
| assertScript ''' |
| import groovy.transform.Memoized |
| |
| // tag::memoized_long_computation_cached[] |
| @Memoized |
| long longComputation(int seed) { |
| // slow computation |
| Thread.sleep(100*seed) |
| System.nanoTime() |
| } |
| |
| def x = longComputation(1) // returns after 100 milliseconds |
| def y = longComputation(1) // returns immediatly |
| def z = longComputation(2) // returns after 200 milliseconds |
| assert x==y |
| assert x!=z |
| // end::memoized_long_computation_cached[] |
| ''' |
| } |
| |
| void testSingleton() { |
| assertScript ''' |
| // tag::singleton_simple[] |
| @Singleton |
| class GreetingService { |
| String greeting(String name) { "Hello, $name!" } |
| } |
| assert GreetingService.instance.greeting('Bob') == 'Hello, Bob!' |
| // end::singleton_simple[] |
| ''' |
| |
| assertScript ''' |
| // tag::singleton_example_property[] |
| @Singleton(property='theOne') |
| class GreetingService { |
| String greeting(String name) { "Hello, $name!" } |
| } |
| |
| assert GreetingService.theOne.greeting('Bob') == 'Hello, Bob!' |
| // end::singleton_example_property[] |
| ''' |
| |
| assertScript ''' |
| // tag::singleton_example_lazy[] |
| class Collaborator { |
| public static boolean init = false |
| } |
| @Singleton(lazy=true,strict=false) |
| class GreetingService { |
| static void init() {} |
| GreetingService() { |
| Collaborator.init = true |
| } |
| String greeting(String name) { "Hello, $name!" } |
| } |
| GreetingService.init() // make sure class is initialized |
| assert Collaborator.init == false |
| GreetingService.instance |
| assert Collaborator.init == true |
| assert GreetingService.instance.greeting('Bob') == 'Hello, Bob!' |
| // end::singleton_example_lazy[] |
| ''' |
| } |
| |
| void testTailRecursive() { |
| assertScript ''' |
| // tag::tailrecursive[] |
| import groovy.transform.CompileStatic |
| import groovy.transform.TailRecursive |
| |
| @CompileStatic |
| class Factorial { |
| |
| @TailRecursive |
| static BigInteger factorial( BigInteger i, BigInteger product = 1) { |
| if( i == 1) { |
| return product |
| } |
| return factorial(i-1, product*i) |
| } |
| } |
| |
| assert Factorial.factorial(1) == 1 |
| assert Factorial.factorial(3) == 6 |
| assert Factorial.factorial(5) == 120 |
| assert Factorial.factorial(50000).toString().size() == 213237 // Big number and no Stack Overflow |
| // end::tailrecursive[] |
| ''' |
| } |
| |
| } |