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
/************************************************************************
* © 2019-2022 SAP SE or an SAP affiliate company. All rights reserved. *
************************************************************************/
package com.sap.cds.util;
import static com.sap.cds.util.CdsModelUtils.isReverseAssociation;
import static com.sap.cds.util.CqnStatementUtils.linkKeysToOuterQuery;
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.function.Consumer;
import java.util.stream.Collectors;
import com.sap.cds.SessionContext;
import com.sap.cds.impl.builder.model.Conjunction;
import com.sap.cds.impl.builder.model.ExistsSubquery;
import com.sap.cds.ql.Delete;
import com.sap.cds.ql.Insert;
import com.sap.cds.ql.Select;
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.CqnSelect;
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;
import com.sap.cds.reflect.CdsStructuredType;
public class PathExpressionResolver {
private final CdsModel cdsModel;
private final CqnStructuredTypeRef ref;
private final SessionContext sessionContext;
private PathExpressionResolver(CdsModel cdsModel, CqnStructuredTypeRef ref, SessionContext sessionContext) {
this.cdsModel = cdsModel;
this.ref = ref;
this.sessionContext = sessionContext;
}
@SuppressWarnings("unchecked")
public static S resolvePath(CdsModel cdsModel, S statement, SessionContext sessionContext) {
PathExpressionResolver per = new PathExpressionResolver(cdsModel, statement.ref(), sessionContext);
LinkedList path = per.unfoldPathExpression();
if (!isPathExpression(path)) {
if (path.getFirst().filter != null) {
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, CqnUpdate update) {
CqnStructuredTypeRef ref = update.ref();
CdsEntity target = CdsModelUtils.entity(cdsModel, ref);
Update> resolved = Update.entity(target).entries(update.entries());
addWhere(ref, target, update.where(), resolved::where);
return resolved;
}
public static CqnSelect resolvePath(CdsModel cdsModel, CqnSelect select) {
if (select.from().isRef()) {
CqnStructuredTypeRef ref = select.ref();
CdsEntity target = CdsModelUtils.entity(cdsModel, ref);
Select> resolved = Select.from(target).columns(select.items()).orderBy(select.orderBy());
addWhere(ref, target, select.where(), resolved::where);
return resolved;
}
return select;
}
public static CqnDelete resolvePath(CdsModel cdsModel, CqnDelete delete) {
CqnStructuredTypeRef ref = delete.ref();
CdsEntity target = CdsModelUtils.entity(cdsModel, ref);
Delete> resolved = Delete.from(target);
addWhere(ref, target, delete.where(), resolved::where);
return resolved;
}
private static void addWhere(CqnStructuredTypeRef ref, CdsEntity target, Optional where,
Consumer filterable) {
Optional refFilter;
if (ref.size() == 1) {
refFilter = ref.rootSegment().filter();
} else {
refFilter = Optional.of(resolveUsingSubquery(target, ref));
}
Conjunction.and(refFilter, where).ifPresent(filterable::accept);
}
private static CqnPredicate resolveUsingSubquery(CdsStructuredType target, CqnStructuredTypeRef ref) {
return new ExistsSubquery(Select.from(ref).where(linkKeysToOuterQuery(target)));
}
private Map getTargetFkValues(Entry parentEntry) {
if (null == parentEntry.filter) {
throw new UnsupportedOperationException(
"Statements with path expression are only supported with filter by keys. Structured type reference: "
+ ref);
}
String association = ref.lastSegment();
CdsEntity entity = parentEntry.entity;
Map parentFilterValues = new OnConditionAnalyzer(association, parentEntry.filter, true)
.getParentPkValues();
return calculateFkValues(entity, association, parentFilterValues);
}
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.size() > 1) {
ref.stream().skip(1).forEach(s -> {
CdsEntity entity = path.getLast().entity;
path.add(new Entry(entity.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, sessionContext).getFkValues(filterValues);
}
private static boolean isPathExpression(List path) {
return path.size() > 1;
}
private static class Entry {
CdsEntity entity;
CqnPredicate filter;
public Entry(CdsEntity entity, Optional filter) {
this.entity = entity;
this.filter = filter.orElse(null);
}
}
}