package origo.helpers;
import org.apache.commons.lang.StringUtils;
import play.modules.origo.core.CachedAnnotation;
import play.modules.origo.core.Listeners;
import play.modules.origo.core.Node;
import play.modules.origo.core.annotations.Provides;
import java.util.*;
/**
* Helper to trigger \@Provides origo.listeners. Should not be used directly except in core and admin, use NodeHelper
* instead when creating a new module.
*
* @see NodeHelper
* @see Provides
*/
public class ProvidesHelper {
public static <T> T triggerListener(String providesType, String withType, Node node) {
//noinspection unchecked
return (T) triggerListener(providesType, withType, node, Collections.<Class, Object>emptyMap());
}
public static <T> T triggerListener(String providesType, String withType, Node node, Class argType, Object arg) {
//noinspection unchecked
return (T) triggerListener(providesType, withType, node, Collections.<Class, Object>singletonMap(argType, arg));
}
public static <T> T triggerListener(String providesType, String withType, Node node, Map<Class, Object> args) {
CachedAnnotation cachedAnnotation = findListener(providesType, withType);
Map<Class, Object> parameters = new HashMap<Class, Object>();
parameters.put(Node.class, node);
parameters.putAll(args);
//noinspection unchecked
return (T) ReflectionHelper.invokeMethod(cachedAnnotation.method, parameters);
}
/**
* Collects all \@Provides.with for the specified providesType. To be used when choosing a type for a new item for
* instance or to find all DASHBOARD_ITEM's for the admin module.
*
* @param providesType a type to look for (NODE, NAVIGATION, NAVIGATION_ITEM, DASHBOARD_ITEM, etc).
* @return a list of all "with" added to the system.
*/
public static Set<String> getAllProvidesWithForType(String providesType) {
Set<String> providedTypes = new HashSet<String>();
List<CachedAnnotation> cachedAnnotations = getAllProvidersForType(providesType);
for (CachedAnnotation cachedAnnotation : cachedAnnotations) {
providedTypes.add(((Provides) cachedAnnotation.annotation).with());
}
return providedTypes;
}
private static List<CachedAnnotation> getAllProvidersForType(final String providesType) {
return Listeners.getListenersForAnnotationType(Provides.class, new CachedAnnotation.ListenerSelector() {
@Override
public boolean isCorrectListener(CachedAnnotation listener) {
return ((Provides) listener.annotation).type().equals(providesType);
}
});
}
private static CachedAnnotation findListener(String providesType, String withType) {
CachedAnnotation listener = findProvidersForType(providesType, withType);
if (listener == null) {
throw new RuntimeException("Every type (specified by using attribute 'with') must have a class annotated with @Provides to instantiate an instance. Unable to find a provider for type \'" + withType + "\'");
}
return listener;
}
private static CachedAnnotation findProvidersForType(final String type, final String withType) {
List<CachedAnnotation> providers = Listeners.getListenersForAnnotationType(Provides.class, new CachedAnnotation.ListenerSelector() {
@Override
public boolean isCorrectListener(CachedAnnotation listener) {
Provides annotation = (Provides) listener.annotation;
return annotation.type().equals(type) && (annotation.with().equals(withType) || StringUtils.isBlank(withType));
}
});
if (!providers.isEmpty()) {
if (providers.size() > 1) {
throw new RuntimeException("Only one @Provides per type (attribute 'with') is allowed");
}
return providers.iterator().next();
} else {
return null;
}
}
}