package org.finra.datagenerator.scaffolding.transformer.utils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.finra.datagenerator.scaffolding.transformer.function.FunctionTransformation;
import org.finra.datagenerator.scaffolding.transformer.service.transformations.OrderImpl;
import org.finra.datagenerator.scaffolding.transformer.service.transformations.TransformationImpl;
import org.finra.datagenerator.scaffolding.transformer.service.transformations.TransformationsImpl;
import org.finra.datagenerator.scaffolding.transformer.support.Order;
import org.finra.datagenerator.scaffolding.transformer.support.Transformation;
import org.finra.datagenerator.scaffolding.transformer.support.Transformations;
import org.finra.datagenerator.scaffolding.utils.ClassPathScanner;
import org.finra.datagenerator.scaffolding.utils.ReflectionUtils.AnnotationAssociation;
import org.finra.datagenerator.scaffolding.utils.SimpleClassPathScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.finra.datagenerator.scaffolding.utils.ReflectionUtils.findAnnotationsWithAssociation;
/**
* Created by dkopel on 9/30/16.
*/
/*
Input always represents the object that
*/
public class TransformerUtils {
private final static Logger logger = LoggerFactory.getLogger(TransformerUtils.class);
private final static Set<Class<FunctionTransformation>> functionTransformerClasses = new HashSet();
private final static ExpressionParser parser = new SpelExpressionParser();
public static <T extends java.lang.annotation.Annotation> Field getField(AnnotationAssociation<T> target, Class<T> clazz) {
List<Field> fs = target.getAssociations()
.stream()
.filter(as -> as instanceof Field)
.map(a -> ((Field) a))
.collect(Collectors.toList());
if(fs.size() == 1) {
return fs.get(0);
} else {
throw new IllegalArgumentException();
}
}
public static Set<Class<FunctionTransformation>> findFunctionTransformers(String basePackage) {
ClassPathScanner classPathScanner = new SimpleClassPathScanner(basePackage);
classPathScanner.addIncludeFilter(new AssignableTypeFilter(FunctionTransformation.class));
for(BeanDefinition bd : classPathScanner.findComponents()) {
try {
Class cl = Class.forName(bd.getBeanClassName());
if(!Modifier.isAbstract(cl.getModifiers())) {
logger.debug("Adding transformer function {}", cl);
functionTransformerClasses.add(cl);
} else {
logger.debug("Ignoring transformer function {}", cl);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return functionTransformerClasses;
}
public static Object parseExpression(String value, StandardEvaluationContext context) {
logger.debug("TransformField: {}", value);
return parser.parseExpression(value).getValue(context);
}
public static List<Map.Entry<Field, Transformation[]>> processTransformations(Class clazz) {
// Find all `Transformation` annotations for the class
return Lists.newArrayList(findAnnotationsWithAssociation(clazz, Transformations.class)
.stream().collect(
Collectors.toMap(
t -> TransformerUtils.getField(t, Transformations.class),
t -> t.getAnnotation().value()
)
).entrySet())
.stream()
.collect(Collectors.toList());
}
public static boolean evaluateCondition(String condition, StandardEvaluationContext context) {
return (condition != null && condition.length() > 0 && (boolean) TransformerUtils.parseExpression(condition, context))
|| condition == null || condition.isEmpty();
}
public static Map<Field, Set<OrderImpl>> getFields(Class clazz) {
// Find all `Transformation` annotations for the class
Set<Field> fields = Sets.newHashSet(clazz.getDeclaredFields());
Map<Field, Set<OrderImpl>> tos = findAnnotationsWithAssociation(clazz, Order.class)
.stream()
.map(t -> {
Order o = t.getAnnotation();
return new AbstractMap.SimpleEntry<>(
TransformerUtils.getField(t, Order.class),
new OrderImpl(o.value(), o.condition())
);
}
)
.collect(
Collectors.groupingBy(
Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toSet()))
);
for(Field f : fields) {
if(!tos.containsKey(f)) {
tos.put(f, Sets.newHashSet());
}
}
return tos;
}
public static Map<Field, Set<TransformationImpl>> getTransformations(Class clazz) {
Set<Field> fields = Sets.newHashSet(clazz.getDeclaredFields());
Map<Field, Set<TransformationImpl>> tos = findAnnotationsWithAssociation(clazz, Transformation.class)
.stream()
.map(t -> new AbstractMap.SimpleEntry<>(
TransformerUtils.getField(t, Transformation.class),
new TransformationImpl(t.getAnnotation())
))
.collect(
Collectors.groupingBy(
Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toSet()))
);
for(Field f : fields) {
if(!tos.containsKey(f)) {
tos.put(f, Sets.newHashSet());
}
}
return tos;
}
public static Set<OrderImpl> getFieldOrders(Field field) {
return getFields(field.getDeclaringClass()).get(field);
}
public static Set<TransformationImpl> getFieldTransformations(Field field) {
return getTransformations(field.getDeclaringClass()).get(field);
}
public static Set<TransformationsImpl> getAllTransformations(Class clazz) {
Map<Field, Set<OrderImpl>> os = getFields(clazz);
Map<Field, Set<TransformationImpl>> ts = getTransformations(clazz);
return os.keySet().stream().map(
f -> new TransformationsImpl(f, os.get(f), ts.get(f))
).collect(Collectors.toSet());
}
public static List<Field> getFields(Class clazz, StandardEvaluationContext context) {
return getFields(getFields(clazz), context);
}
public static List<Field> getFields(Map<Field, Set<OrderImpl>> fields, StandardEvaluationContext context) {
return fields.entrySet()
.stream()
.collect(
Collectors.toMap(
o1 -> o1.getKey(),
o1 -> o1.getValue()
.stream()
.filter(o -> TransformerUtils.evaluateCondition(o.getCondition(), context))
.map(o -> o.getOrder())
.min(Long::compare).orElseGet(()->Long.MAX_VALUE)
)
).entrySet()
.stream()
.sorted((o1, o2) -> o1.getValue().compareTo(o2.getValue()))
.map(o -> o.getKey())
.collect(Collectors.toList());
}
public static Optional<TransformationImpl> getTransformation(Field field, StandardEvaluationContext context) {
return Stream.of(field.getAnnotationsByType(Transformation.class))
.map(TransformationImpl::new)
.sorted((o1, o2) -> o1.compareTo(o2))
.filter(o -> TransformerUtils.evaluateCondition(o.getCondition(), context))
.findFirst();
}
}