
com.sap.cds.util.OnConditionAnalyzer Maven / Gradle / Ivy
/*******************************************************************
* © 2019 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.util;
import static com.sap.cds.reflect.impl.DraftAdapter.IS_ACTIVE_ENTITY;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import com.sap.cds.SessionContext;
import com.sap.cds.impl.AssociationAnalyzer;
import com.sap.cds.impl.builder.model.ElementRefImpl;
import com.sap.cds.ql.ElementRef;
import com.sap.cds.ql.Value;
import com.sap.cds.ql.cqn.CqnComparisonPredicate;
import com.sap.cds.ql.cqn.CqnConnectivePredicate;
import com.sap.cds.ql.cqn.CqnElementRef;
import com.sap.cds.ql.cqn.CqnLiteral;
import com.sap.cds.ql.cqn.CqnModifier;
import com.sap.cds.ql.cqn.CqnNegation;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnToken;
import com.sap.cds.ql.cqn.CqnValue;
import com.sap.cds.ql.cqn.CqnVisitor;
import com.sap.cds.ql.impl.ExpressionVisitor;
import com.sap.cds.reflect.CdsElement;
public class OnConditionAnalyzer {
private final String associationName;
private final boolean reverse;
private final CqnPredicate on;
public OnConditionAnalyzer(String associationName, CqnPredicate on, boolean reverse) {
this.associationName = associationName;
this.reverse = reverse;
this.on = on;
}
public OnConditionAnalyzer(CdsElement association, boolean reverse, SessionContext session) {
this(association.getName(), onCondition(association, session), reverse);
}
private static CqnPredicate onCondition(CdsElement association, SessionContext session) {
AssociationAnalyzer associationAnalyzer = new AssociationAnalyzer();
return associationAnalyzer.getOnCondition(association);
}
private void getFkMapping(BiConsumer consumer) {
CqnVisitor v = new CqnVisitor() {
@Override
public void visit(CqnComparisonPredicate cmp) {
if (!cmp.operator().equals(CqnComparisonPredicate.Operator.EQ)) {
throw new UnsupportedOperationException("expecting comparison predicate \"=\"");
}
CqnValue lhs = cmp.left();
CqnValue rhs = cmp.right();
if (isRefToTarget(rhs) ^ reverse) {
consumer.accept(lhs, rhs);
} else {
consumer.accept(rhs, lhs);
}
}
@Override
public void visit(CqnConnectivePredicate connective) {
if (connective.operator() != CqnConnectivePredicate.Operator.AND) {
throw new UnsupportedOperationException("expecting 'and' operator");
}
}
@Override
public void visit(CqnNegation neg) {
throw new UnsupportedOperationException("expecting 'and' operator");
}
};
on.accept(v);
}
public Map getFkValues(Map sourceObject) {
Map fkValues = new HashMap<>();
getFkMapping((fk, pk) -> addMapping(sourceObject, fkValues, fk, pk));
return fkValues;
}
public Map getParentPkValues() {
Map pkValues = new HashMap<>();
getFkMapping((fk, pk) -> addMapping(Collections.emptyMap(), pkValues, fk, pk));
return pkValues;
}
/**
* Calculate the target predicate based on the on-condition and predicates'
* concrete values. Example: on-condition has (PK) id = (FK)
* parent_id, filter contains where id = 42. The
* resulting predicate will be where parent_id = 42
*
* @param predicate containing concrete values
* @return calculated target predicate
*/
public CqnPredicate getTargetPredicate(CqnPredicate predicate) {
final Map pkToFkMap = new HashMap<>();
// TODO: workaround for Draft. Until usage of $generatedFieldName is done
pkToFkMap.put(IS_ACTIVE_ENTITY, IS_ACTIVE_ENTITY);
getFkMapping((fk, pk) -> addMapping(pkToFkMap, (CqnElementRef) pk, (CqnElementRef) fk));
return ExpressionVisitor.copy(predicate, new CqnModifier() {
@Override
public Value> ref(ElementRef> ref) {
return ElementRefImpl.element(pkToFkMap.get(ref.lastSegment()));
}
});
}
private void addMapping(Map sourceObject, Map fkValues, CqnToken fkSide,
CqnToken pkSide) {
String fk = ref(fkSide).lastSegment();
Object value = val(sourceObject, pkSide);
fkValues.put(fk, value);
}
private void addMapping(Map values, CqnElementRef fkSide, CqnElementRef pkSide) {
values.put(fkSide.lastSegment(), pkSide.lastSegment());
}
private Object val(Map sourceObject, CqnToken token) {
if (token instanceof CqnElementRef) {
String pk = ref(token).lastSegment();
return sourceObject.get(pk);
}
if (token instanceof CqnLiteral>) {
return literal(token).value();
}
throw new UnsupportedOperationException();
}
private boolean isRefToTarget(CqnValue token) {
if (!(token instanceof CqnElementRef)) {
return false;
}
CqnElementRef ref = ref(token);
return associationName.equals(ref.firstSegment());
}
@SuppressWarnings("unchecked")
private static CqnLiteral
© 2015 - 2025 Weber Informatics LLC | Privacy Policy