All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cds.impl.parser.SelectParser Maven / Gradle / Ivy

There is a newer version: 3.6.1
Show newest version
/*******************************************************************
 * © 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