diff --git a/settings.gradle b/settings.gradle index 4ba7a1e1ec..2ef516f3cb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,6 +18,10 @@ def subprojects = ['groovy-ant', if(JavaVersion.current().isJava7Compatible()) { subprojects << 'groovy-nio' } + +if(JavaVersion.current().isJava8Compatible()) { + subprojects << 'groovy-optional' +} include(subprojects as String[]) diff --git a/subprojects/groovy-optional/build.gradle b/subprojects/groovy-optional/build.gradle new file mode 100644 index 0000000000..e417faaecb --- /dev/null +++ b/subprojects/groovy-optional/build.gradle @@ -0,0 +1,22 @@ +dependencies { + compile rootProject + testCompile project(':groovy-test') + testCompile ('org.spockframework:spock-core:0.7-groovy-2.0') { + exclude module: 'groovy-all' + } +} + +task moduleDescriptor(type: org.codehaus.groovy.gradle.WriteExtensionDescriptorTask) { + extensionClasses = 'org.codehaus.groovy.runtime.OptionalGroovyMethods' +} + +compileJava.dependsOn moduleDescriptor + +tasks.withType(Compile) { + sourceCompatibility = 1.7 + targetCompatibility = 1.7 +} +tasks.withType(Javadoc) { + options.source = "1.7" +} + diff --git a/subprojects/groovy-optional/src/main/java/org/codehaus/groovy/runtime/OptionalGroovyMethods.java b/subprojects/groovy-optional/src/main/java/org/codehaus/groovy/runtime/OptionalGroovyMethods.java new file mode 100644 index 0000000000..65c74b86db --- /dev/null +++ b/subprojects/groovy-optional/src/main/java/org/codehaus/groovy/runtime/OptionalGroovyMethods.java @@ -0,0 +1,51 @@ +/* + * Copyright 2003-2014 the original author or authors. + * + * Licensed 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 org.codehaus.groovy.runtime; +import groovy.transform.CompileStatic; +import org.codehaus.groovy.runtime.InvokerHelper; +import java.util.Optional; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import org.codehaus.groovy.runtime.InvokerHelper; + +/** + * @author Uehara Junji(@uehaj) + */ +public class OptionalGroovyMethods { + + public static Object methodMissing(Optional self, String name, Object args0) { + Object[] args = (Object[])args0; + for (int i=0; ipublic static T eachLine(InputStream self, Closure c) + * provides a eachLine(Closure c) method for InputStream. + *

+ * NOTE: While this class contains many 'public' static methods, it is + * primarily regarded as an internal class (its internal package name + * suggests this also). We value backwards compatibility of these + * methods when used within Groovy but value less backwards compatibility + * at the Java method call level. I.e. future versions of Groovy may + * remove or move a method call in this file but would normally + * aim to keep the method available from within Groovy. + * + * @author James Strachan + * @author Jeremy Rayner + * @author Sam Pullara + * @author Rod Cope + * @author Guillaume Laforge + * @author John Wilson + * @author Hein Meling + * @author Dierk Koenig + * @author Pilho Kim + * @author Marc Guillemot + * @author Russel Winder + * @author bing ran + * @author Jochen Theodorou + * @author Paul King + * @author Michael Baehr + * @author Joachim Baumann + * @author Alex Tkachman + * @author Ted Naleid + * @author Brad Long + * @author Jim Jagielski + * @author Rodolfo Velasco + * @author jeremi Joslin + * @author Hamlet D'Arcy + * @author Cedric Champeau + * @author Tim Yates + * @author Dinko Srkoc + * @author Paolo Di Tommaso + */ + +public class OptionalGroovyMethods extends DefaultGroovyMethodsSupport { + public static void hello(String self) { + System.out.println("hello"+self); + } +} diff --git a/subprojects/groovy-optional/src/test/groovy/org/codehaus/groovy/runtime/OptionalGroovyMethodsTest.groovy b/subprojects/groovy-optional/src/test/groovy/org/codehaus/groovy/runtime/OptionalGroovyMethodsTest.groovy new file mode 100644 index 0000000000..b077503e70 --- /dev/null +++ b/subprojects/groovy-optional/src/test/groovy/org/codehaus/groovy/runtime/OptionalGroovyMethodsTest.groovy @@ -0,0 +1,125 @@ +package org.codehaus.groovy.runtime; + +import java.util.Optional; +import groovy.io.FileType +import spock.lang.Specification + +class OptionalGroovyMethodsTest extends Specification { + + Optional s1 = Optional.of("Abc") + Optional s2 = Optional.of("Def") + Optional i1 = Optional.of(3) + Optional i2 = Optional.of(4) + Optional e = Optional.empty() + + void testStringPlus1() { + expect: + s1+s2 == Optional.of("AbcDef") + s1+e == Optional.empty() + e+s2 == Optional.empty() + e+e == Optional.empty() + } + + void testIntegerPlus1() { + expect: + i1+i2 == Optional.of(7) + i1+e == Optional.empty() + e+i2 == Optional.empty() + e+e == Optional.empty() + } + + def toUpperAndToLower1(a,b) { + a.toUpperCase()+b.toLowerCase() + } + + void testToUpperAndToLower() { + expect: + toUpperAndToLower1(s1, s2) == Optional.of("ABCdef") + toUpperAndToLower1(s1, e) == e + toUpperAndToLower1(e, s2) == e + toUpperAndToLower1(e, e) == e + } + + def mult1(a, b) { + a * 3 + b * 2 + } + + void testMult1() { + expect: + mult1(s1, s2) == Optional.of("AbcAbcAbcDefDef") + mult1(e, s2) == e + mult1(s1, e) == e + mult1(s1, e) == e + mult1(s1, Optional.of(3)) == Optional.of("AbcAbcAbc6") + } + + // @groovy.transform.TypeChecked + Optional mult2(Optionala, Optionalb) { + a * 3 + b * 2 + } + + @groovy.transform.TypeChecked + void testMult2() { + expect: + mult2(i1, i2) == Optional.of(9+8) + mult2(e, i2) == e + mult2(i1, e) == e + mult2(e, e) == e + } + + // Optional.methodMissing() never worked with static method. + // Because static method is not the method of Optional. To apply + // static method to Optional-embedded-value, you can embed a + // closure into Opional and call the Opitonal like + // closure. Optional.call is defined to delagate to Closure.call() + // on the embedded Closure, so you can apply static method to + // Optional embedded value transparently. + void testStatic() { + expect: + Optional.of({fmt,...args->String.format(fmt,*args)})("%02d", i1) == Optional.of("03") + Optional.of(String.&format)("%02d", i1) == Optional.of("03") + Optional.of(String.&format)(Optional.of("%02d"), i1) == Optional.of("03") + } + + // Optional.toString() is not delegated to the toString() on the + // embeded value. Because of compatibility to Java's originaln + // Original behavior. + void testToString() { + def a = Optional.of(3.3) + expect: + a.toString() == "Optional[3.3]" // != "3.3" + a.get().toString() == "3.3" + a.map{"<"+it.toString()+">"}.get() == "<3.3>" + } + + // Optional.toString() is not delegated to the toString() on the + // embeded value. Because of compatibility to Java's originaln + // Original behavior. + void testToString() { + def a = Optional.of(3.3) + expect: + a.toString() == "Optional[3.3]" // != "3.3" + a.get().toString() == "3.3" + a.map{"<"+it.toString()+">"}.get() == "<3.3>" + } + + void testMap() { + expect: + Optional.of("abc").map{it.toUpperCase()} == Optional.of("ABC") + } + + void testFlatMap() { + def divide = {it == 0 ? Optional.empty() : Optional.of(3.0/it) } + expect: + Optional.of(3.0).flatMap(divide) != Optional.of(1.0) // equals i not dispatched to target. + Optional.of(3.0).flatMap(divide).get() == Optional.of(1.0).get() + Optional.of(0).flatMap(divide) == Optional.empty() + } + + void testFilter() { + expect: + Optional.of(3).filter { it % 2 == 0 } == Optional.empty() + Optional.of(3).filter { it % 2 == 1 } == Optional.of(3) + } + +}