ToCriteriaPartTreeTransformer.java
package org.codefilarete.stalactite.spring.repository.query.derivation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.codefilarete.reflection.AccessorChain;
import org.codefilarete.stalactite.engine.EntityCriteria.LimitAware;
import org.codefilarete.stalactite.engine.EntityCriteria.OrderByChain;
import org.codefilarete.stalactite.engine.EntityCriteria.OrderByChain.Order;
import org.codefilarete.stalactite.engine.runtime.projection.ProjectionQueryCriteriaSupport;
import org.codefilarete.stalactite.engine.runtime.query.EntityCriteriaSupport;
import org.codefilarete.stalactite.engine.runtime.query.EntityQueryCriteriaSupport;
import org.codefilarete.stalactite.query.model.LogicalOperator;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.data.repository.query.parser.PartTree.OrPart;
/**
* Applies a {@link PartTree} to a {@link EntityCriteriaSupport} which is expected to come from either a
* {@link ProjectionQueryCriteriaSupport} or a
* {@link EntityQueryCriteriaSupport}
*
* @param <C>
* @author Guillaume Mary
*/
public class ToCriteriaPartTreeTransformer<C> extends AbstractDerivedQuery<C> {
private final PartTree partTree;
public ToCriteriaPartTreeTransformer(PartTree partTree, Class<C> entityType) {
super(entityType);
this.partTree = partTree;
}
public Condition applyTo(EntityCriteriaSupport<C> entityCriteriaSupport, OrderByChain<C, ?> orderByChain, LimitAware<?> limitAware) {
Processor processor = new Processor(entityCriteriaSupport, orderByChain);
partTree.forEach(processor::append);
if (partTree.getSort().isSorted()) {
processor.appendSort(partTree);
}
if (partTree.getMaxResults() != null) {
limitAware.limit(partTree.getMaxResults());
}
return new Condition(processor.conditions);
}
public static class Condition {
private final List<Criterion> condition;
public Condition(List<Criterion> condition) {
this.condition = condition;
}
public void consume(Object[] arguments) {
int argumentIndex = 0;
for (Criterion criterion : condition) {
criterion.setValue(arguments, argumentIndex);
argumentIndex += criterion.argumentCount;
}
}
}
private class Processor {
private EntityCriteriaSupport<C> currentSupport;
private final OrderByChain<C, ?> orderByChain;
private final List<Criterion> conditions = new ArrayList<>();
public Processor(EntityCriteriaSupport<C> currentSupport, OrderByChain<C, ?> orderByChain) {
this.orderByChain = orderByChain;
this.currentSupport = currentSupport;
}
private void appendSort(PartTree tree) {
tree.getSort().iterator().forEachRemaining(order -> {
AccessorChain<C, Object> orderProperty = convertToAccessorChain(order);
orderByChain.orderBy(orderProperty, order.getDirection() == Direction.ASC ? Order.ASC : Order.DESC, order.isIgnoreCase());
});
}
private void append(OrPart part) {
boolean nested = false;
if (part.stream().count() > 1) { // "if" made to avoid extra parenthesis (can be considered superfluous)
nested = true;
this.currentSupport = currentSupport.beginNested();
}
Iterator<Part> iterator = part.iterator();
if (iterator.hasNext()) {
append(iterator.next(), LogicalOperator.OR);
}
iterator.forEachRemaining(p -> this.append(p, LogicalOperator.AND));
if (nested) { // "if" made to avoid extra parenthesis (can be considered superfluous)
this.currentSupport = currentSupport.endNested();
}
}
private void append(Part part, LogicalOperator orOrAnd) {
AccessorChain<C, Object> getter = convertToAccessorChain(part.getProperty());
Criterion criterion = convertToCriterion(part);
this.currentSupport.add(orOrAnd, getter.getAccessors(), criterion.condition);
this.conditions.add(criterion);
}
}
}