com.sap.cds.adapter.odata.v4.query.ExpressionParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cds-adapter-odata-v4 Show documentation
Show all versions of cds-adapter-odata-v4 Show documentation
OData V4 adapter for CDS Services Java
/**************************************************************************
* (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
**************************************************************************/
package com.sap.cds.adapter.odata.v4.query;
import static com.sap.cds.ql.CQL.func;
import static com.sap.cds.ql.CQL.val;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.apache.olingo.commons.api.edm.EdmEnumType;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceKind;
import org.apache.olingo.server.api.uri.UriResourceLambdaAll;
import org.apache.olingo.server.api.uri.UriResourceLambdaAny;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.api.uri.queryoption.apply.AggregateExpression.StandardMethod;
import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind;
import org.apache.olingo.server.api.uri.queryoption.expression.Expression;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException;
import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor;
import org.apache.olingo.server.api.uri.queryoption.expression.Literal;
import org.apache.olingo.server.api.uri.queryoption.expression.Member;
import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind;
import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind;
import com.google.common.annotations.VisibleForTesting;
import com.sap.cds.adapter.odata.v4.query.apply.ElementAggregator;
import com.sap.cds.adapter.odata.v4.utils.TypeConverterUtils;
import com.sap.cds.adapter.odata.v4.utils.mapper.EdmxFlavourMapper;
import com.sap.cds.impl.builder.model.CqnNull;
import com.sap.cds.ql.CQL;
import com.sap.cds.ql.ElementRef;
import com.sap.cds.ql.Predicate;
import com.sap.cds.ql.StructuredType;
import com.sap.cds.ql.Value;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnReference.Segment;
import com.sap.cds.ql.cqn.CqnStructuredTypeRef;
import com.sap.cds.ql.cqn.CqnToken;
import com.sap.cds.ql.cqn.CqnValue;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.reflect.CdsType;
import com.sap.cds.services.ErrorStatuses;
import com.sap.cds.services.utils.CdsErrorStatuses;
import com.sap.cds.services.utils.ErrorStatusException;
import com.sap.cds.util.CdsModelUtils;
public class ExpressionParser {
private final CdsStructuredType rootType;
private final EdmxFlavourMapper elementMapper;
public ExpressionParser(CdsStructuredType rootType, EdmxFlavourMapper elementMapper) {
this.rootType = rootType;
this.elementMapper = elementMapper;
}
public CqnValue parseValue(Expression expression) {
try {
return (CqnValue) expression.accept(new ExpressionToCqnVisitor());
} catch (ExpressionVisitException | ODataApplicationException e) {
throw new ErrorStatusException(CdsErrorStatuses.VALUE_PARSING_FAILED, e);
}
}
public CqnToken parseToken(Expression expression) {
try {
return (CqnToken) expression.accept(new ExpressionToCqnVisitor());
} catch (ExpressionVisitException | ODataApplicationException e) {
throw new ErrorStatusException(CdsErrorStatuses.VALUE_PARSING_FAILED, e);
}
}
public CqnStructuredTypeRef parseStructuredTypeRef(Expression expression) {
try {
return (CqnStructuredTypeRef) expression.accept(new ExpressionToCqnVisitor());
} catch (ExpressionVisitException | ODataApplicationException e) {
throw new ErrorStatusException(CdsErrorStatuses.VALUE_PARSING_FAILED, e);
}
}
public CqnPredicate parseFilter(Expression expression) {
try {
return (CqnPredicate) expression.accept(new ExpressionToCqnVisitor());
} catch (ODataApplicationException | ExpressionVisitException e) {
throw new ErrorStatusException(CdsErrorStatuses.FILTER_PARSING_FAILED, e);
}
}
public List toSegmentList(List uriResourceParts) {
List segmentList = new ArrayList<>();
CdsStructuredType type = rootType;
String prefix = null;
for (UriResource part : uriResourceParts) {
if (part.getKind() == UriResourceKind.root) {
prefix = rootType.getQualifier() + ".";
continue; // TODO? skip so far
}
if (part.getKind() != UriResourceKind.lambdaVariable) {
if (type == null) {
// uri contains navigation property or complex type property
// however the previous property already returned a simple element
throw new ErrorStatusException(CdsErrorStatuses.UNEXPECTED_URI_RESOURCE, part.getKind());
}
String segment = elementMapper.remap(part.getSegmentValue(), type);
type = structuredType(type.findElement(segment).orElse(null));
for (String id : segment.split("\\.")) {
if (prefix != null) {
id = prefix + id;
prefix = null;
}
segmentList.add(CQL.refSegment(id));
}
}
}
return segmentList;
}
public String remap(String element) {
return elementMapper.remap(element, rootType);
}
private CdsStructuredType structuredType(CdsElement element) {
if (element != null) {
CdsType type = element.getType();
if (type.isStructured()) {
return type.as(CdsStructuredType.class);
}
if (type.isAssociation()) {
return type.as(CdsAssociationType.class).getTarget();
}
}
return null;
}
public CdsStructuredType getRootType() {
return rootType;
}
public CqnStructuredTypeRef getTargetTypeRef(Expression exr) {
List resources = ((Member) exr).getResourcePath().getUriResourceParts();
UriResource last = resources.get(resources.size() - 1);
return switch (last.getKind()) {
case navigationProperty -> CQL.to(((UriResourceNavigation) last).getType().getName()).asRef();
case entitySet -> CQL.to(((UriResourceEntitySet) last).getEntityType().getName()).asRef();
default ->
throw new ErrorStatusException(CdsErrorStatuses.UNEXPECTED_URI_RESOURCE, last.getKind());
};
}
@VisibleForTesting
class ExpressionToCqnVisitor implements ExpressionVisitor