/*
* 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;
}
}