Reimplement GroovyJavaMethods in Java
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/0d0214cc Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/0d0214cc Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/0d0214cc Branch: refs/heads/master Commit: 0d0214ccecc6222ffa7c712b1b9077c9356f6f46 Parents: 18c28d2 Author: Svetoslav Neykov <[email protected]> Authored: Thu Oct 22 09:49:22 2015 +0300 Committer: Svetoslav Neykov <[email protected]> Committed: Thu Oct 22 16:33:44 2015 +0300 ---------------------------------------------------------------------- .../util/groovy/FromCallableClosure.java | 38 ++++ .../util/groovy/FromFunctionClosure.java | 39 ++++ .../util/groovy/FromRunnableClosure.java | 46 +++++ .../util/groovy/GroovyJavaMethods.groovy | 146 -------------- .../brooklyn/util/groovy/GroovyJavaMethods.java | 200 +++++++++++++++++++ .../brooklyn/util/groovy/JavadocDummy.java | 30 --- 6 files changed, 323 insertions(+), 176 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d0214cc/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromCallableClosure.java ---------------------------------------------------------------------- diff --git a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromCallableClosure.java b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromCallableClosure.java new file mode 100644 index 0000000..0ad414f --- /dev/null +++ b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromCallableClosure.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 org.apache.brooklyn.util.groovy; + +import java.util.concurrent.Callable; + +import groovy.lang.Closure; + +public class FromCallableClosure<T> extends Closure<T> { + private static final long serialVersionUID = 1L; + private Callable<T> job; + + public FromCallableClosure(Class<GroovyJavaMethods> owner, Callable<T> job) { + super(owner, owner); + this.job = job; + } + + public T doCall() throws Exception { + return job.call(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d0214cc/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromFunctionClosure.java ---------------------------------------------------------------------- diff --git a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromFunctionClosure.java b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromFunctionClosure.java new file mode 100644 index 0000000..80203fb --- /dev/null +++ b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromFunctionClosure.java @@ -0,0 +1,39 @@ +/* + * 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 org.apache.brooklyn.util.groovy; + +import groovy.lang.Closure; + +import com.google.common.base.Function; + +public class FromFunctionClosure<T> extends Closure<T> { + private static final long serialVersionUID = 1L; + private Function<Object, T> job; + + @SuppressWarnings("unchecked") + public FromFunctionClosure(Class<GroovyJavaMethods> owner, Function<?, T> job) { + super(owner, owner); + this.job = (Function<Object, T>) job; + } + + public T doCall(Object it) throws Exception { + return job.apply(it); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d0214cc/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromRunnableClosure.java ---------------------------------------------------------------------- diff --git a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromRunnableClosure.java b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromRunnableClosure.java new file mode 100644 index 0000000..37fad87 --- /dev/null +++ b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/FromRunnableClosure.java @@ -0,0 +1,46 @@ +/* + * 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 org.apache.brooklyn.util.groovy; + +import groovy.lang.Closure; + +import java.util.concurrent.Callable; + +import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; + +public class FromRunnableClosure<T> extends Closure<T> { + private static final long serialVersionUID = 1L; + private Runnable job; + + public FromRunnableClosure(Class<GroovyJavaMethods> owner, Runnable job) { + super(owner, owner); + this.job = job; + } + + @SuppressWarnings("unchecked") + public T doCall() throws Throwable { + if (ScriptBytecodeAdapter.isCase(job, Callable.class)) { + return ((Callable<T>)job).call(); + } else { + job.run(); + return null; + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d0214cc/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.groovy ---------------------------------------------------------------------- diff --git a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.groovy b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.groovy deleted file mode 100644 index ff0def2..0000000 --- a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.groovy +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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 org.apache.brooklyn.util.groovy; - -import static org.apache.brooklyn.util.groovy.GroovyJavaMethods.truth; - -import java.util.concurrent.Callable - -import org.apache.brooklyn.util.concurrent.CallableFromRunnable; - -import com.google.common.base.Function -import com.google.common.base.Predicate - -/** handy methods available in groovy packaged so they can be consumed from java, - * and other conversion/conveniences; but see JavaGroovyEquivalents for faster alternatives */ -public class GroovyJavaMethods { - - //TODO use named subclasses, would that be more efficient? - - // TODO xFromY methods not in correct class: they are not "handy method available in groovy"? - public static Closure closureFromRunnable(final Runnable job) { - return { - if (job in Callable) { return job.call() } - else { job.run(); null; } - }; - } - - public static Closure closureFromCallable(final Callable job) { - return { job.call(); }; - } - - public static <T> Closure<T> closureFromFunction(final Function<?,T> job) { - return { it -> return job.apply(it); }; - } - - public static <T> Callable<T> callableFromClosure(final Closure<T> job) { - return job as Callable; - } - - public static <T> Callable<T> callableFromRunnable(final Runnable job) { - return (job in Callable) ? callableFromClosure(job) : CallableFromRunnable.newInstance(job, null); - } - - public static <T> Predicate<T> predicateFromClosure(final Closure<Boolean> job) { - // TODO using `Predicate<T>` on the line below gives "unable to resolve class T" - return new Predicate<Object>() { - public boolean apply(Object input) { - return job.call(input); - } - }; - } - - public static <F,T> Function<F,T> functionFromClosure(final Closure<T> job) { - // TODO using `Function<F,T>` on the line below gives "unable to resolve class T" - return new Function<Object,Object>() { - public Object apply(Object input) { - return job.call(input); - } - }; - } - - public static <T> Predicate<T> castToPredicate(Object o) { - if (o in Closure) { - return predicateFromClosure(o); - } else { - return (Predicate<T>) o; - } - } - - public static <T> Closure castToClosure(Object o) { - if (o == null) { - return o; - } else if (o in Closure) { - return o; - } else if (o instanceof Runnable) { - return closureFromRunnable((Runnable)o); - } else if (o instanceof Callable) { - return closureFromCallable((Callable)o); - } else if (o instanceof Function) { - return closureFromFunction((Function)o); - } else { - throw new IllegalArgumentException("Cannot convert to closure: o="+o+"; type="+(o != null ? o.getClass() : null)); - } - } - -/* alternatives to above; but I think the above is more efficient? (even more efficient if moved from java to groovy) --alex jun 2012 - public static <K,T> Function<K,T> functionFromClosure(final Closure<T> job) { - return job as Function; - } - - public static <T> Predicate<T> predicateFromClosure(final Closure<Boolean> job) { - return job as Predicate; - } -*/ - - public static Predicate<Object> truthPredicate() { - return new Predicate<Object>() { - @Override public boolean apply(Object input) { - return truth(input); - } - }; - } - - public static boolean truth(Object o) { - if (o) return true; - return false; - } - - public static <T> T elvis(Object preferred, Object fallback) { - return fix(preferred ?: fallback); - } - - public static <T> T elvis(Object... preferences) { - if (preferences.length == 0) throw new IllegalArgumentException("preferences must not be empty for elvis"); - for (Object contender : preferences) { - if (contender) return fix(contender); - } - return fix(preferences[preferences.size()-1]); - } - - public static <T> T fix(Object o) { - if (o in GString) return (o as String); - return o; - } - - // args is expected to be an array, but for groovy compilation reasons it's not declared as such in the signature :-( - public static <T> T invokeMethodOnMetaClass(Object target, String methodName, Object args) { - return target.metaClass.invokeMethod(target, methodName, args); - } -} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d0214cc/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.java ---------------------------------------------------------------------- diff --git a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.java b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.java new file mode 100644 index 0000000..7f019a9 --- /dev/null +++ b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/GroovyJavaMethods.java @@ -0,0 +1,200 @@ +/* + * 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 org.apache.brooklyn.util.groovy; + +import java.util.concurrent.Callable; + +import org.apache.brooklyn.util.concurrent.CallableFromRunnable; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.codehaus.groovy.runtime.ScriptBytecodeAdapter; +import org.codehaus.groovy.runtime.callsite.CallSite; +import org.codehaus.groovy.runtime.callsite.CallSiteArray; +import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; + +import groovy.lang.Closure; +import groovy.lang.GString; + +/** handy methods available in groovy packaged so they can be consumed from java, + * and other conversion/conveniences; but see JavaGroovyEquivalents for faster alternatives */ +public class GroovyJavaMethods { + private static final CallSiteArray CALL_SITE_ARRAY = new CallSiteArray(GroovyJavaMethods.class, new String[] {"metaClass", "invokeMethod"}); + + //TODO use named subclasses, would that be more efficient? + + // TODO xFromY methods not in correct class: they are not "handy method available in groovy"? + public static <T> Closure<T> closureFromRunnable(final Runnable job) { + return new FromRunnableClosure<T>(GroovyJavaMethods.class, job); + } + + public static <T> Closure<T> closureFromCallable(final Callable<T> job) { + return new FromCallableClosure<T>(GroovyJavaMethods.class, job); + } + + public static <T> Closure<T> closureFromFunction(final Function<?,T> job) { + return new FromFunctionClosure<T>(GroovyJavaMethods.class, job); + } + + @SuppressWarnings("unchecked") + public static <T> Callable<T> callableFromClosure(final Closure<T> job) { + try { + return (Callable<T>)ScriptBytecodeAdapter.asType(job, Callable.class); + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + + @SuppressWarnings("unchecked") + public static <T> Callable<T> callableFromRunnable(final Runnable job) { + try { + if (ScriptBytecodeAdapter.isCase(job, Callable.class)) { + return (Callable<T>)ScriptBytecodeAdapter.asType(job, Callable.class); + } else { + return CallableFromRunnable.newInstance(job, null); + } + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + + public static <T> Predicate<T> predicateFromClosure(final Closure<Boolean> job) { + return new Predicate<T>() { + @Override + public boolean apply(Object input) { + return job.call(input); + } + }; + } + + public static <F,T> Function<F,T> functionFromClosure(final Closure<T> job) { + return new Function<F,T>() { + @Override + public T apply(F input) { + return job.call(input); + } + }; + } + + @SuppressWarnings("unchecked") + public static <T> Predicate<T> castToPredicate(Object o) { + try { + if (ScriptBytecodeAdapter.isCase(o, Closure.class)) { + return predicateFromClosure((Closure<Boolean>)o); + } else { + return (Predicate<T>) o; + } + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + + @SuppressWarnings("unchecked") + public static <T> Closure<T> castToClosure(Object o) { + try { + if (ScriptBytecodeAdapter.compareEqual(o, null)) { + return (Closure<T>)ScriptBytecodeAdapter.castToType(o, Closure.class); + } else if (ScriptBytecodeAdapter.isCase(o, Closure.class)) { + return (Closure<T>)ScriptBytecodeAdapter.castToType(o, Closure.class); + } else if (o instanceof Runnable) { + return closureFromRunnable((Runnable)ScriptBytecodeAdapter.createPojoWrapper(ScriptBytecodeAdapter.castToType(o, Runnable.class), Runnable.class)); + } else if (o instanceof Callable) { + return closureFromCallable((Callable<T>)ScriptBytecodeAdapter.createPojoWrapper(ScriptBytecodeAdapter.castToType(o, Callable.class), Callable.class)); + } else if (o instanceof Function) { + return closureFromFunction((Function<Object, T>)ScriptBytecodeAdapter.createPojoWrapper(ScriptBytecodeAdapter.castToType(o, Function.class), Function.class)); + } else { + throw new IllegalArgumentException("Cannot convert to closure: o="+o+"; type="+(o != null ? o.getClass() : null)); + } + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + +/* alternatives to above; but I think the above is more efficient? (even more efficient if moved from java to groovy) --alex jun 2012 + public static <K,T> Function<K,T> functionFromClosure(final Closure<T> job) { + return job as Function; + } + + public static <T> Predicate<T> predicateFromClosure(final Closure<Boolean> job) { + return job as Predicate; + } +*/ + + public static Predicate<Object> truthPredicate() { + return new Predicate<Object>() { + @Override public boolean apply(Object input) { + return truth(input); + } + }; + } + + public static boolean truth(Object o) { + if (DefaultTypeTransformation.booleanUnbox(o)) return true; + return false; + } + + public static <T> T elvis(Object preferred, Object fallback) { + try { + return fix(truth(preferred) ? preferred : fallback); + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + + public static <T> T elvis(Object... preferences) { + try { + if (preferences.length == 0) throw new IllegalArgumentException("preferences must not be empty for elvis"); + for (Object contender : preferences) { + if (truth(contender)) return fix(contender); + } + return fix(preferences[preferences.length-1]); + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + + @SuppressWarnings("unchecked") + public static <T> T fix(Object o) { + try { + if (ScriptBytecodeAdapter.isCase(o, GString.class)) { + return (T)ScriptBytecodeAdapter.asType(o, String.class); + } else { + return (T)o; + } + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + + @SuppressWarnings("unchecked") + public static <T> T invokeMethodOnMetaClass(Object target, String methodName, Object args) { + try { + CallSite[] callSiteArray = getCallSiteArray(); + Object metaClass = callSiteArray[0].callGetProperty(target); + return (T) callSiteArray[1].call(metaClass, target, methodName, args); + } catch (Throwable e) { + throw Exceptions.propagate(e); + } + } + + private static CallSite[] getCallSiteArray() { + return CALL_SITE_ARRAY.array; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/0d0214cc/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/JavadocDummy.java ---------------------------------------------------------------------- diff --git a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/JavadocDummy.java b/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/JavadocDummy.java deleted file mode 100644 index c9a51b5..0000000 --- a/utils/groovy/src/main/java/org/apache/brooklyn/util/groovy/JavadocDummy.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 org.apache.brooklyn.util.groovy; - -/** Maven Central requires javadoc to promote as a release. This seemed to happen when this was built by maven as a bundle, - * but now that it is built as a jar it does not. This class exists only to provide that javadoc. - * <p> - * Note the groovy code does javadoc but the maven build is not picking it up. It *is* generated as part of the site build. - */ -public class JavadocDummy { - - private JavadocDummy() {} - -}
