/* | |
* 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; | |
import groovy.lang.Closure; | |
import groovy.lang.Reference; | |
import junit.framework.TestCase; | |
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; | |
import java.math.BigDecimal; | |
import java.math.BigInteger; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.LinkedHashMap; | |
import java.util.List; | |
import java.util.Map; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.any; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.collect; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.each; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.every; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.findAll; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.inject; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.join; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.max; | |
import static org.codehaus.groovy.runtime.DefaultGroovyMethods.sort; | |
/** | |
* Groovy's Closure class isn't specifically designed with Java integration in | |
* mind, but these tests illustrate some of the possible ways to use them from Java. | |
*/ | |
public class ClosureJavaIntegrationTest extends TestCase { | |
Map<String, Integer> zoo = new LinkedHashMap<String, Integer>(); | |
List<String> animals = Arrays.asList("ant", "bear", "camel"); | |
@Override | |
protected void setUp() throws Exception { | |
super.setUp(); | |
zoo.put("Monkeys", 3); | |
zoo.put("Giraffe", 2); | |
zoo.put("Lions", 5); | |
} | |
public void testJoinListNonClosureCase() { | |
assertEquals(join(animals, ", "), "ant, bear, camel"); | |
} | |
public void testEachList() { | |
final List<Integer> result = new ArrayList<Integer>(); | |
each(animals, new Closure(null) { | |
public void doCall(String arg) { | |
result.add(arg.length()); | |
} | |
}); | |
assertEquals(Arrays.asList(3, 4, 5), result); | |
} | |
public void testEachMap() { | |
final List<String> result = new ArrayList<String>(); | |
each(zoo, new Closure(null) { | |
public void doCall(String k, Integer v) { | |
result.add("k=" + k + ",v=" + v); | |
} | |
}); | |
assertEquals(Arrays.asList("k=Monkeys,v=3", "k=Giraffe,v=2", "k=Lions,v=5" ), result); | |
} | |
public void testCollectList() { | |
assertEquals(Arrays.asList(3, 4, 5), collect(animals, new Closure<Integer>(null) { | |
public Integer doCall(String it) { | |
return it.length(); | |
} | |
})); | |
} | |
public void testMaxMap() { | |
Map.Entry<String, Integer> lionEntry = null; | |
for (Map.Entry<String, Integer> entry : zoo.entrySet()) { | |
if (entry.getKey().equals("Lions")) lionEntry = entry; | |
} | |
assertEquals(lionEntry, max(zoo.entrySet(), new Closure<Integer>(null) { | |
public Integer doCall(Map.Entry<String, Integer> e) { | |
return e.getKey().length() * e.getValue(); | |
} | |
})); | |
} | |
public void testSortMapKeys() { | |
assertEquals(Arrays.asList("Monkeys", "Lions", "Giraffe"), sort(zoo.keySet(), new Closure<Integer>(null) { | |
public Integer doCall(String a, String b) { | |
return -a.compareTo(b); | |
} | |
})); | |
assertEquals(Arrays.asList("Giraffe", "Lions", "Monkeys"), sort(zoo.keySet(), new Closure<Integer>(null) { | |
public Integer doCall(String a, String b) { | |
return a.compareTo(b); | |
} | |
})); | |
} | |
public void testAnyMap() { | |
assertTrue(any(zoo, new Closure<Boolean>(null) { | |
public Boolean doCall(String k, Integer v) { | |
return k.equals("Lions") && v == 5; | |
} | |
})); | |
} | |
public void testFindAllAndCurry() { | |
Map<String, Integer> expected = new HashMap<String, Integer>(zoo); | |
expected.remove("Lions"); | |
Closure<Boolean> keyBiggerThan = new Closure<Boolean>(null) { | |
public Boolean doCall(Map.Entry<String, Integer> e, Integer size) { | |
return e.getKey().length() > size; | |
} | |
}; | |
Closure<Boolean> keyBiggerThan6 = keyBiggerThan.rcurry(6); | |
assertEquals(expected, findAll(zoo, keyBiggerThan6)); | |
} | |
public void testListArithmetic() { | |
List<List> numLists = new ArrayList<List>(); | |
numLists.add(Arrays.asList(1, 2, 3)); | |
numLists.add(Arrays.asList(10, 20, 30)); | |
assertEquals(Arrays.asList(6, 60), collect(numLists, new Closure<Integer>(null) { | |
public Integer doCall(Integer a, Integer b, Integer c) { | |
return a + b + c; | |
} | |
})); | |
Closure<Integer> arithmeticClosure = new Closure<Integer>(null) { | |
public Integer doCall(Integer a, Integer b, Integer c) { | |
return a * b + c; | |
} | |
}; | |
Closure<Integer> tensAndUnits = arithmeticClosure.curry(10); | |
assertEquals(35, (int) tensAndUnits.call(3, 5)); | |
tensAndUnits = arithmeticClosure.ncurry(0, 10); | |
assertEquals(35, (int) tensAndUnits.call(3, 5)); | |
tensAndUnits = arithmeticClosure.ncurry(1, 10); | |
assertEquals(35, (int) tensAndUnits.call(3, 5)); | |
Closure<Integer> timesPlus5 = arithmeticClosure.rcurry(5); | |
assertEquals(35, (int) timesPlus5.call(15, 2)); | |
timesPlus5 = arithmeticClosure.ncurry(2, 5); | |
assertEquals(35, (int) timesPlus5.call(15, 2)); | |
} | |
public void testComposition() { | |
Closure<String> toUpperCase = new Closure<String>(null) { | |
public String doCall(String s) { | |
return s.toUpperCase(); | |
} | |
}; | |
Closure<Boolean> hasCapitalA = new Closure<Boolean>(null) { | |
public Boolean doCall(String s) { | |
return s.contains("A"); | |
} | |
}; | |
Closure<Boolean> hasA = toUpperCase.rightShift(hasCapitalA); | |
assertTrue(every(animals, hasA)); | |
Closure<Boolean> alsoHasA = hasCapitalA.leftShift(toUpperCase); | |
assertTrue(every(animals, alsoHasA)); | |
} | |
public void testTrampoline() { | |
final Reference<Closure<BigInteger>> ref = new Reference<Closure<BigInteger>>(); | |
ref.set(new Closure<BigInteger>(null) { | |
public Object doCall(Integer n, BigInteger total) { | |
return n > 1 ? ref.get().trampoline(n - 1, total.multiply(BigInteger.valueOf(n))) : total; | |
} | |
}.trampoline()); | |
Closure<BigInteger> factorial = new Closure<BigInteger>(null) { | |
public BigInteger doCall(Integer n) { | |
return ref.get().call(n, BigInteger.ONE); | |
} | |
}; | |
assertEquals(BigInteger.valueOf(479001600), factorial.call(12)); | |
} | |
public void testInject() { | |
Collection<Integer> c = Arrays.asList(2, 4, 5, 20); | |
Number initial = BigDecimal.ZERO; | |
Closure<? extends Number> closure = new Closure<BigDecimal>(c) { | |
BigDecimal doCall(BigDecimal total, Integer next) { | |
return total.add(BigDecimal.ONE.divide(new BigDecimal(next))); | |
} | |
}; | |
assertTrue(DefaultTypeTransformation.compareEqual(BigDecimal.ONE, inject(c, initial, closure))); | |
} | |
} |