com.sap.cds.impl.parser.SelectParser Maven / Gradle / Ivy
/*******************************************************************
* © 2019 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.impl.parser;
import static com.sap.cds.impl.builder.model.SelectList.Type.EXPAND;
import static com.sap.cds.impl.builder.model.SelectList.Type.INLINE;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.google.common.collect.Lists;
import com.sap.cds.impl.builder.model.ExpandBuilder;
import com.sap.cds.impl.builder.model.LockImpl;
import com.sap.cds.impl.builder.model.SelectList;
import com.sap.cds.impl.builder.model.SelectList.Type;
import com.sap.cds.impl.builder.model.StructuredTypeImpl;
import com.sap.cds.impl.parser.builder.LimitBuilder;
import com.sap.cds.impl.parser.builder.SortSpecBuilder;
import com.sap.cds.impl.parser.search.SearchParser;
import com.sap.cds.ql.CQL;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.cqn.CqnLimit;
import com.sap.cds.ql.cqn.CqnLock;
import com.sap.cds.ql.cqn.CqnNumericLiteral;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnSelectListItem;
import com.sap.cds.ql.cqn.CqnSelectListValue;
import com.sap.cds.ql.cqn.CqnSortSpecification;
import com.sap.cds.ql.cqn.CqnSource;
import com.sap.cds.ql.cqn.CqnStructuredTypeRef;
import com.sap.cds.ql.cqn.CqnSyntaxException;
import com.sap.cds.ql.cqn.CqnValue;
import com.sap.cds.ql.impl.SelectBuilder;
import com.sap.cds.ql.impl.SelectListValueBuilder;
public class SelectParser extends CqnParser {
private SelectParser(String json) {
super(getDocumentContext(json));
}
public static SelectParser of(String cqn) {
return new SelectParser(cqn);
}
public static Select parse(String cqn) {
return of(cqn).parse();
}
public Select parse() {
return SelectBuilder.from(from()).distinct(distinct()).count(count()).columns(columns()).excluding(excluding())
.where(where()).search(search()).orderBy(orderBy()).limit(limit(cqn.read("$.SELECT.limit")))
.having(having()).groupBy(groupBy()).lockSelectedRows(rowLock());
}
protected CqnSource from() {
return TokenParser.source(cqn.read("$.SELECT.from"));
}
private List groupBy() {
ArrayNode arrayNode = cqn.read("$.SELECT.groupBy");
List tokens = Lists.newArrayList();
if (arrayNode != null) {
for (JsonNode node : arrayNode) {
tokens.add(selectListValue(node));
}
}
return tokens;
}
private CqnLimit limit(JsonNode limitNode) {
if (limitNode == null) {
return null;
}
if (!limitNode.has("rows")) {
throw new CqnSyntaxException("Unsupported limit syntax, 'rows' is mandatory");
}
CqnNumericLiteral> limit = TokenParser.parseValue(limitNode.get("rows"));
LimitBuilder limitBuilder = LimitBuilder.rows(limit.value().longValue());
if (limitNode.has("offset")) {
CqnNumericLiteral> offset = TokenParser.parseValue(limitNode.get("offset"));
limitBuilder.offset(offset.value().longValue());
}
return limitBuilder.build();
}
private boolean distinct() {
return readBoolean("$.SELECT.distinct");
}
private boolean count() {
return readBoolean("$.SELECT.count");
}
private boolean readBoolean(String path) {
JsonNode node = cqn.read(path);
try {
if (node == null) {
return false;
} else {
return node.asBoolean();
}
} catch (IllegalArgumentException e) {
throw new CqnSyntaxException("Unsupported distinct syntax '" + node.toString() + "'", e);
}
}
private List orderBy() {
return orderBy(cqn.read("$.SELECT.orderBy"));
}
private static List orderBy(ArrayNode arrayNode) {
List orderBy = new ArrayList<>();
if (arrayNode != null) {
for (JsonNode node : arrayNode) {
CqnSelectListValue item = selectListValue(node);
SortSpecBuilder sortSpec = new SortSpecBuilder(item);
if (node.has("sort")) {
sortSpec.order(node.get("sort").asText());
}
orderBy.add(sortSpec.build());
}
}
return orderBy;
}
private Stream columns() {
ArrayNode cols = cqn.read("$.SELECT.columns");
if (cols == null) {
return Stream.empty();
}
return StreamSupport.stream(cols.spliterator(), false).map(this::createSelectListItem);
}
private CqnSelectListItem createSelectListItem(JsonNode node) {
if (node.has("ref")) {
if (node.has("inline")) {
return createSelectList(INLINE, node);
}
if (node.has("expand")) {
return createSelectList(EXPAND, node);
}
}
if (node.getNodeType() == JsonNodeType.STRING && "*".equals(node.asText())) {
return CQL.star();
}
return selectListValue(node);
}
private SelectList createSelectList(Type type, JsonNode node) {
CqnStructuredTypeRef prefix = TokenParser.ref(node);
SelectList selectList = SelectList.create(prefix, type);
ArrayNode inlineArrayNode = (ArrayNode) node.get(type.toString());
if (inlineArrayNode.size() == 1 && inlineArrayNode.get(0).asText().equals("*")) {
selectList.all();
} else {
for (JsonNode sli : inlineArrayNode) {
CqnSelectListItem selectListItem = createSelectListItem(sli);
selectList.items(selectListItem);
}
}
if (type == EXPAND) {
CqnLimit limit = limit(node.get("limit"));
if (limit != null) {
((ExpandBuilder>) selectList).limit(limit.top(), limit.skip());
}
if (node.has("orderBy")) {
List order = orderBy((ArrayNode) node.get("orderBy"));
((ExpandBuilder>) selectList).orderBy(order);
}
}
return selectList;
}
private static CqnSelectListValue selectListValue(JsonNode node) {
CqnValue val = TokenParser.parseValue(node);
SelectListValueBuilder builder = SelectListValueBuilder.select(val);
JsonNode alias = node.get("as");
if (alias != null) {
builder.as(alias.asText());
}
return builder.build();
}
private List excluding() {
ArrayNode cols = cqn.read("$.SELECT.excluding");
List columns = Lists.newArrayList();
if (cols != null) {
for (JsonNode node : cols) {
columns.add(node.asText());
}
}
return columns;
}
private CqnPredicate where() {
JsonNode where = cqn.read("$.SELECT.where");
return ExpressionParser.parsePredicate(where);
}
private CqnPredicate having() {
JsonNode having = cqn.read("$.SELECT.having");
return ExpressionParser.parsePredicate(having);
}
private CqnPredicate search() {
JsonNode search = cqn.read("$.SELECT.search");
CqnPredicate predicate = ExpressionParser.parsePredicate(search);
if (predicate != null) {
return new SearchParser().parsePredicate(predicate.tokens());
}
return predicate;
}
private CqnLock rowLock() {
JsonNode forUpdate = cqn.read("$.SELECT.forUpdate");
LockImpl lock = null;
if (forUpdate != null) {
lock = new LockImpl();
if (forUpdate.has("wait")) {
return lock.timeout(forUpdate.get("wait").asInt());
}
}
return lock;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy