
com.crabshue.commons.aggregator.Processor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-aggregator Show documentation
Show all versions of commons-aggregator Show documentation
Library aggregating fields using annotations in complex objects structures
The newest version!
package com.crabshue.commons.aggregator;
import com.crabshue.commons.aggregator.model.ProcessTrace;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Array;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class Processor {
private static final Pattern THIS_FIELD_PATTERN = Pattern.compile("this\\.(\\w+)");
/**
* Analyse recursively an object, collecting all the @Collects to aggregators,
* then executes the @Executes Nulls will not be collected You cannot assume
* wich order will be executed for the @Executes
*
* @param o the object to process
* @return an Aggregator context to be used for programmatically accessing the
* aggregators
*/
public static AggregatorContext process(Object o) {
return process(o, o.getClass().getSimpleName(), AggregatorContext.builder().build());
}
/**
* same as process. Use this method if you want to register custom methods in a
* namespace to your context.
*
* @param o the object to be processed
* @param prefix the prefix to use as a reference for o in the context
* @param aggregatorContext a context to help processing the object
* @return updated aggregator context
*/
public static AggregatorContext process(Object o, String prefix, AggregatorContext aggregatorContext) {
Map> executors = new HashMap<>();
aggregatorContext.set(prefix, o);
if (aggregatorContext.getPackageStarts() == null) {
aggregatorContext.setPackageStarts(
Arrays.asList(o.getClass().getPackage().getName()));
}
aggregatorContext.preProcess(o);
process(prefix, o, aggregatorContext, executors);
executors.values().stream()
.flatMap(List::stream)
.filter(Processor::notExecuted)
.forEach(e -> execute(e, aggregatorContext));
aggregatorContext.postProcess(o);
return aggregatorContext;
}
private static void process(String prefix, Object o, AggregatorContext localContext,
Map> executeContexts) {
if (o == null)
return;
@SuppressWarnings("rawtypes")
Class objectClass = o.getClass();
Analysed analysed = localContext.getAnalysed(objectClass);
ProcessTrace current = localContext.getProcessTrace();
try {
if (!StringFunctions.isEmpty(analysed.getClassContext())) {
localContext.setProcessTrace(localContext.getProcessTrace().traceContext(analysed.getClassContext()));
executeContexts.values().stream()
.flatMap(Collection::stream)
.filter(Processor::notExecuted)
.filter(e -> e.formula.contains(analysed.getClassContext() + "."))
.forEach(e -> execute(e, localContext));
localContext.cleanContext(analysed.getClassContext());
}
int i = 0;
String key;
switch (analysed.getClassType()) {
case MAP:
Map m = (Map) o;
key = prefix.replaceAll("\\.", "_") + "_";
for (Object mO : m.values()) {
String setKey = key + (i++);
localContext.set(setKey, mO);
process(setKey, mO, localContext, executeContexts);
}
return;
case ARRAY:
int length = Array.getLength(o);
for (i = 0; i < length; i++) {
process(prefix + "[" + i + "]", Array.get(o, i), localContext, executeContexts);
}
return;
case LIST:
@SuppressWarnings("rawtypes")
List l = (List) o;
for (i = 0; i < l.size(); i++) {
process(prefix + "[" + i + "]", l.get(i), localContext, executeContexts);
}
return;
case ITERABLE:
@SuppressWarnings("rawtypes")
Iterator it = ((Iterable) o).iterator();
key = prefix.replaceAll("\\.", "_") + "_";
while (it.hasNext()) {
String setKey = key + (i++);
Object iO = it.next();
localContext.set(setKey, iO);
process(setKey, iO, localContext, executeContexts);
}
return;
case IGNORABLE:
return;
}
//Prepare the field execution
analysed.getExecutes()
.forEach((field, executeList) -> executeList.forEach(execute -> {
if (applicable(o, execute.getWhen(), localContext)) {
addExecuteContext(executeContexts, prefix, field, execute.getJexl(), localContext);
}
}));
for (String field : analysed.getVariables().keySet()) {
localContext.addVariable(analysed.getVariables().get(field), get(o, field, localContext));
}
//First potentially seek deeper.
for (String field : analysed.getOtherFields()) {
process(prefix + "." + field, get(o, field, localContext), localContext,
executeContexts);
}
//Collect the fields
for (String field : analysed.getCollects().keySet()) {
String add = prefix + "." + field;
Optional.ofNullable(executeContexts.get(add))
.orElse(Collections.emptyList()).stream().filter(Processor::notExecuted)
.forEach(e -> execute(e, localContext));
if (!isNull(o, field, localContext)) {
for (com.crabshue.commons.aggregator.model.Collect collect : analysed.getCollects().get(field)) {
if (applicable(o, collect.getWhen(), localContext)) {
localContext.collect(evaluate(o, collect.getTo(), localContext), add);
}
}
}
}
if (analysed.getClassCollects() != null) {
for (com.crabshue.commons.aggregator.model.Collect collect : analysed.getClassCollects()) {
String formula = "(" + executeFieldsFromFormula(prefix, collect.getWhat(), executeContexts, localContext) + ")";
if (applicable(o, collect.getWhen(), localContext) && !isNull(formula, localContext)) {
localContext.collect(evaluate(o, collect.getTo(), localContext),
formula);
}
}
}
} finally {
localContext.setProcessTrace(current);
}
}
private static boolean notExecuted(ExecuteContext executeContext) {
return !executeContext.executed;
}
private static String executeFieldsFromFormula(String prefix, String formula, Map> executeContexts, AggregatorContext localContext) {
StringBuilder sb = new StringBuilder();
Matcher m = THIS_FIELD_PATTERN.matcher(formula);
int lastIndex = 0;
while (m.find()) {
sb.append(formula, lastIndex, m.start());
String field = prefix + '.' + m.group(1);
sb.append(field);
Optional.ofNullable(executeContexts.get(field))
.orElse(new ArrayList<>()).stream().filter(Processor::notExecuted)
.forEach(e -> execute(e, localContext));
lastIndex = m.end();
}
sb.append(formula.substring(lastIndex));
return sb.toString();
}
private static void addExecuteContext(Map> executeContexts, String prefix, String field, String jexl, AggregatorContext localContext) {
String key = prefix + "." + field;
List list = executeContexts.getOrDefault(key, new ArrayList<>());
String formula = executeFieldsFromFormula(prefix, jexl, executeContexts, localContext);
list.add(new ExecuteContext(key, formula));
executeContexts.put(prefix + "." + field, list);
}
private static void execute(ExecuteContext e, AggregatorContext localContext) {
localContext.execute(e.field, e.formula);
e.executed = true;
}
private static boolean isNull(String formula, AggregatorContext localContext) {
return localContext.evaluate(formula) == null;
}
private static Object get(Object o, String fieldName, AggregatorContext localContext) {
if (StringFunctions.isEmpty(fieldName) || fieldName.contains("$"))
return null;
localContext.set("this", o);
if (localContext.isDebug())
logger.debug("Get " + o.getClass() + " " + fieldName);
return localContext.evaluate("this." + fieldName);
}
private static String evaluate(Object o, String value, AggregatorContext localContext) {
if (StringFunctions.isEmpty(value))
return null;
if (!value.startsWith("eval:")) {
return value;
}
localContext.set("this", o);
Object evaluated = localContext.evaluate(value.substring(5));
return evaluated == null ? "null" : evaluated.toString();
}
private static boolean isNull(Object o, String fieldName, AggregatorContext localContext) {
return get(o, fieldName, localContext) == null;
}
private static boolean applicable(Object o, String when, AggregatorContext localContext) {
if (StringFunctions.isEmpty(when))
return true;
localContext.set("this", o);
Object evaluated = localContext.evaluate(when);
Boolean ret = evaluated == null ? Boolean.FALSE : Boolean.valueOf(evaluated.toString());
if (localContext.isDebug())
logger.debug("applicable :" + when + " is " + ret);
return ret;
}
static class ExecuteContext {
String field;
String formula;
boolean executed = false;
ExecuteContext(String field, String formula) {
this.field = field;
this.formula = formula;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy