Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sap.cds.util.PathExpressionResolver Maven / Gradle / Ivy
package com.sap.cds.util;
import static com.sap.cds.util.CdsModelUtils.isReverseAssociation;
import static java.util.stream.Collectors.toMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import com.sap.cds.CdsException;
import com.sap.cds.SessionContext;
import com.sap.cds.impl.builder.model.Conjunction;
import com.sap.cds.ql.Delete;
import com.sap.cds.ql.Insert;
import com.sap.cds.ql.Update;
import com.sap.cds.ql.Upsert;
import com.sap.cds.ql.cqn.CqnDelete;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnStructuredTypeRef;
import com.sap.cds.ql.cqn.CqnUpdate;
import com.sap.cds.ql.cqn.CqnXsert;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsModel;
public class PathExpressionResolver {
private final CdsModel cdsModel;
private final CqnStructuredTypeRef ref;
private final SessionContext session;
private PathExpressionResolver(CdsModel cdsModel, SessionContext session, CqnStructuredTypeRef ref) {
this.cdsModel = cdsModel;
this.session = session;
this.ref = ref;
}
@SuppressWarnings("unchecked")
public static S resolvePath(CdsModel cdsModel, SessionContext session, S statement) {
PathExpressionResolver per = new PathExpressionResolver(cdsModel, session, statement.ref());
LinkedList path = per.unfoldPathExpression();
if (!isPathExpression(path)) {
if (path.getFirst().expr.isPresent()) {
throw new UnsupportedOperationException(
"Infix filters are not supported in Insert/Upsert statements: " + statement);
}
return statement;
}
List> entries = addValues(statement.entries(), per.getTargetFkValues(parentEntry(path)));
CdsEntity target = targetEntity(path);
if (statement.isInsert()) {
return (S) Insert.into(target).entries(entries);
}
if (statement.isUpsert()) {
return (S) Upsert.into(target).entries(entries);
}
throw new IllegalStateException();
}
public static CqnUpdate resolvePath(CdsModel cdsModel, SessionContext session, CqnUpdate update) {
PathExpressionResolver per = new PathExpressionResolver(cdsModel, session, update.ref());
LinkedList path = per.unfoldPathExpression();
CqnPredicate wherePredicate;
if (isPathExpression(path)) {
wherePredicate = conjunctPredicates(per.getOnConditionPredicate(parentEntry(path)), update.where(),
path.getLast().expr);
} else {
wherePredicate = conjunctPredicates(update.where(), path.getLast().expr);
}
return Update.entity(targetEntity(path)).entries(update.entries()).where(wherePredicate);
}
// TODO resolvePath adds FK elements not part of CDS model (CDSJAVA-1837)
public static CqnDelete resolvePath(CdsModel cdsModel, SessionContext session, CqnDelete delete) {
PathExpressionResolver per = new PathExpressionResolver(cdsModel, session, delete.ref());
LinkedList path = per.unfoldPathExpression();
CqnPredicate wherePredicate;
if (isPathExpression(path)) {
wherePredicate = conjunctPredicates(per.getOnConditionPredicate(parentEntry(path)), delete.where(),
path.getLast().expr);
} else {
wherePredicate = conjunctPredicates(delete.where(), path.getLast().expr);
}
return Delete.from(targetEntity(path)).where(wherePredicate);
}
public static List resolvePathCascadeDelete(CdsModel cdsModel, SessionContext session,
CqnStructuredTypeRef ref, List> entries) {
PathExpressionResolver per = new PathExpressionResolver(cdsModel, session, ref);
LinkedList path = per.unfoldPathExpression();
Map fkValues = isPathExpression(path) ? per.getTargetFkValues(parentEntry(path))
: Collections.emptyMap();
return entries.stream()
.map(e -> createDeleteStatement(cdsModel, targetEntity(path).getQualifiedName(), e, fkValues))
.collect(Collectors.toList());
}
private static CqnPredicate conjunctPredicates(Optional where, Optional filter) {
return Conjunction.and(where, filter).orElse(null);
}
@SafeVarargs
private static CqnPredicate conjunctPredicates(CqnPredicate predicate, Optional... otherPredicates) {
CqnPredicate conjunction = predicate;
for (Optional pred : otherPredicates) {
if (pred.isPresent()) {
conjunction = new Conjunction(pred.get(), conjunction);
}
}
return conjunction;
}
private static CqnDelete createDeleteStatement(CdsModel cdsModel, String entityName, Map values,
Map fkValues) {
// Exclude associations, as they are already contained in fkValues
Map keys = cdsModel.getEntity(entityName).keyElements()
.filter(k -> !k.getType().isAssociation())
.collect(toMap(CdsElement::getName, e -> getValue(e, values)));
keys.putAll(fkValues);
return Delete.from(entityName).matching(keys);
}
private static Object getValue(CdsElement key, Map values) {
Object val = values.get(key.getName());
if (val == null) {
throw new CdsException("Provided data is missing value for key '" + key + "'");
}
return val;
}
private Map getTargetFkValues(Entry parentEntry) {
String association = ref.lastSegment();
CqnPredicate filter = parentEntry.expr.orElseThrow(() -> new UnsupportedOperationException(
"Statements with path expression are only supported with filter by keys. Structured type reference: "
+ ref));
CdsEntity entity = parentEntry.entity;
Map parentFilterValues = new OnConditionAnalyzer(association, filter, true).getParentPkValues();
return calculateFkValues(entity, association, parentFilterValues);
}
private CqnPredicate getOnConditionPredicate(Entry parentEntry) {
String association = ref.lastSegment();
CqnPredicate filter = parentEntry.expr.orElseThrow(() -> new UnsupportedOperationException(
"Statements with path expression are only supported with filter by keys. Structured type reference: "
+ ref));
return new OnConditionAnalyzer(parentEntry.entity.getAssociation(association), true, session)
.getTargetPredicate(filter);
}
private static Entry parentEntry(LinkedList path) {
Iterator iterator = path.descendingIterator();
iterator.next();
return iterator.next();
}
private static CdsEntity targetEntity(LinkedList path) {
Iterator iterator = path.descendingIterator();
return iterator.next().entity;
}
private static List> addValues(List> entries, Map values) {
List> copy = entries.stream().map(HashMap::new).collect(Collectors.toList());
copy.forEach(e -> e.putAll(values));
return copy;
}
private LinkedList unfoldPathExpression() {
LinkedList path = new LinkedList<>();
path.add(new Entry(cdsModel.getEntity(ref.firstSegment()), ref.segments().get(0).filter()));
if (ref.segments().size() > 1) {
ref.segments().stream().skip(1).forEach(s -> {
CdsEntity e = path.getLast().entity;
path.add(new Entry(e.getTargetOf(s.id()), s.filter()));
});
}
return path;
}
private Map calculateFkValues(CdsEntity entity, String associationName,
Map filterValues) {
CdsElement association = entity.getAssociation(associationName);
if (!isReverseAssociation(association)) {
throw new UnsupportedOperationException(
"Path expressions in insert on forward mapped associations are not supported");
}
return new OnConditionAnalyzer(association, true, session).getFkValues(filterValues);
}
private static boolean isPathExpression(List path) {
return path.size() > 1;
}
private static class Entry {
CdsEntity entity;
Optional expr;
public Entry(CdsEntity entity, Optional expr) {
this.entity = entity;
this.expr = expr;
}
}
}