/*
* Copyright (C) 2014 GG-Net GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package eu.ggnet.saft.core.ops;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.ggnet.saft.api.ui.*;
import eu.ggnet.saft.core.all.*;
/**
* Operation Central. The point there Actions and Factories are registered.
* <p>
* @author oliver.guenther
*/
public class Ops {
private final static Logger L = LoggerFactory.getLogger(Ops.class);
private final static Map<Class, List<DescriptiveConsumer>> REGISTERED_ACTIONS = new HashMap<>();
private final static Map<Class, DescriptiveConsumer> REGISTERED_DEFAULT_ACTIONS = new HashMap<>();
private final static Map<Class, List<DescriptiveConsumerFactory>> REGISTERED_ACTION_FACTORIES = new HashMap<>();
private final static Map<Class, List<Consumer>> REGISTERED_SELECTION_LISTENERS = new HashMap<>();
private static Class extractSingleType(Class<?> target, Object instance) {
for (Type genericInterface : instance.getClass().getGenericInterfaces()) {
if ( !genericInterface.toString().contains(target.getName()) ) continue;
// This, we know !
ParameterizedType pt = (ParameterizedType)genericInterface;
Class clazz = (Class)pt.getActualTypeArguments()[0];
return clazz;
}
throw new IllegalArgumentException("Instance does not implement an Interface of type A<B> " + instance);
}
/**
* Register a Consumer, to be informed on a specific selection.
* <p>
* @param <T> the type to be listening too.
* @param listener the listener to register
*/
public static <T> void registerSelectListener(Consumer<T> listener) {
Class clazz = extractSingleType(Consumer.class, listener);
L.info("Registering key {} with {}", clazz, listener);
if ( !REGISTERED_SELECTION_LISTENERS.containsKey(clazz) ) REGISTERED_SELECTION_LISTENERS.put(clazz, new ArrayList<>());
REGISTERED_SELECTION_LISTENERS.get(clazz).add(listener);
}
/**
* Register a Consumer, to be informed on a specific selection.
* <p>
* @param <T> the type to be listening too.
* @param listener the listener to register
*/
public static <T> void unregisterSelectListener(Consumer<T> listener) {
L.info("Unregistering select listener {}", listener);
Class clazz = extractSingleType(Consumer.class, listener);
if ( REGISTERED_SELECTION_LISTENERS.containsKey(clazz) ) REGISTERED_SELECTION_LISTENERS.get(clazz).remove(listener);
}
/**
* Returns a selector used for the source, to start selections.
* Any calls on the selector will go through the registered listeners.
* <p>
* @param <T>
* @param clazz the clazz as key
* @return a selector bound to Ops.
*/
public static <T> Selector<T> seletor(Class<T> clazz) {
return seletor(clazz, null);
}
/**
* Returns a selector used for the source, to start selections.
* Any calls on the selector will go through the registered listeners.
* <p>
* @param <T>
* @param clazz the clazz as key
* @param enhancer optional enhancer.
* @return a selector bound to Ops.
*/
public static <T> Selector<T> seletor(Class<T> clazz, SelectionEnhancer<T> enhancer) {
return new Selector<>(clazz, REGISTERED_SELECTION_LISTENERS, enhancer);
}
/**
* Register a new Action Factory.
* <p>
* @param <T> the type is used as key.
* @param factory the factory to register.
*/
public static <T> void registerActionFactory(DescriptiveConsumerFactory<T> factory) {
Class clazz = extractSingleType(DescriptiveConsumerFactory.class, factory);
L.info("Registering factory, key {} with {}", clazz, factory);
if ( !REGISTERED_ACTION_FACTORIES.containsKey(clazz) ) REGISTERED_ACTION_FACTORIES.put(clazz, new ArrayList<>());
REGISTERED_ACTION_FACTORIES.get(clazz).add(factory);
}
/**
* Register a Consumer as global action, possibly annotated for title and more.
* <p>
* @param <T> the type as key, what this action is for.
* @param consumer the consumer as action.
*/
public static <T> void registerAction(Consumer<T> consumer) {
Class clazz = extractSingleType(Consumer.class, consumer);
L.info("Registering action, key {} with {}", clazz, consumer);
DescriptiveConsumer descriptiveConsumer = new DescriptiveConsumer(consumer);
if ( consumer.getClass().getAnnotation(DefaultAction.class) != null ) REGISTERED_DEFAULT_ACTIONS.put(clazz, descriptiveConsumer);
if ( !REGISTERED_ACTIONS.containsKey(clazz) ) REGISTERED_ACTIONS.put(clazz, new ArrayList<>());
REGISTERED_ACTIONS.get(clazz).add(descriptiveConsumer);
}
/**
* Returns the default DependendAction wrapped in a runner.
* <p>
* @param <T> typo of action relevant instance
* @param t action relevant instance
* @return the default DependendAction wrapped in a runner.
*/
public static <T> Optional<DescriptiveConsumerRunner<T>> defaultOf(T t) {
if ( t == null || !REGISTERED_DEFAULT_ACTIONS.containsKey(t.getClass()) ) return Optional.empty();
return Optional.of(new DescriptiveConsumerRunner<>(REGISTERED_DEFAULT_ACTIONS.get(t.getClass()), t));
}
/**
* Returns all registered dependent actions for this instance as runners, or an empty list never null.
* <p>
* @param <T>
* @param t this instance as dependent reference
* @param enhancer an optional enhancer
* @return all registered dependent actions for this instance as runners, or an empty list never null.
*/
public static <T> List<DescriptiveConsumerRunner<?>> staticOf(T t, SelectionEnhancer<T> enhancer) {
Stream<DescriptiveConsumerRunner<T>> map = safeNull(REGISTERED_ACTIONS.get(t.getClass())).stream().map(d -> new DescriptiveConsumerRunner<T>(d, t));
List<DescriptiveConsumerRunner<?>> result = map.collect(Collectors.toList());
if ( enhancer == null ) return result;
for (Object other : enhancer.enhance(t)) {
for (DescriptiveConsumer otherAction : safeNull(REGISTERED_ACTIONS.get(other.getClass()))) {
result.add(new DescriptiveConsumerRunner<>(otherAction, other));
}
}
return result;
}
public static <T> List<DescriptiveConsumerRunner<?>> dynamicOf(T t, SelectionEnhancer<T> enhancer) {
List<DescriptiveConsumerRunner<?>> result = new ArrayList<>();
for (DescriptiveConsumerFactory<T> factory : safeNull(REGISTERED_ACTION_FACTORIES.get(t.getClass()))) {
for (DescriptiveConsumer<T> action : factory.of(t)) {
result.add(new DescriptiveConsumerRunner<>(action, t));
}
}
L.debug("Result before enhance for {} is {}", t, result);
if ( enhancer != null ) {
for (Object other : enhancer.enhance(t)) {
for (DescriptiveConsumerFactory<Object> factory : safeNull(REGISTERED_ACTION_FACTORIES.get(other.getClass()))) {
for (DescriptiveConsumer<Object> action : factory.of(other)) {
result.add(new DescriptiveConsumerRunner<>(action, other));
}
}
}
}
L.debug("Result after enhance for {} is {}", t, result);
return result;
}
private static <T> List<T> safeNull(List<T> in) {
if ( in != null ) return in;
return new ArrayList<>();
}
}