blob: 46de3281ed6d0b076fc847247ca66e0b362cb7e3 [file] [log] [blame]
/*
* 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[]
'''
}
}