com.sap.cds.ql.impl.SelectBuilder Maven / Gradle / Ivy
/*******************************************************************
* © 2019 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.ql.impl;
import static com.sap.cds.impl.builder.model.StructuredTypeImpl.structuredType;
import static com.sap.cds.ql.impl.SelectListValueBuilder.refToSlv;
import static com.sap.cds.ql.impl.SelectListValueBuilder.select;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.sap.cds.impl.builder.model.CqnParam;
import com.sap.cds.impl.builder.model.ElementRefImpl;
import com.sap.cds.impl.builder.model.ExpressionImpl;
import com.sap.cds.impl.builder.model.LiteralImpl;
import com.sap.cds.impl.builder.model.LockImpl;
import com.sap.cds.impl.builder.model.Negation;
import com.sap.cds.impl.builder.model.ScalarFunctionCall;
import com.sap.cds.impl.builder.model.SearchPredicate;
import com.sap.cds.impl.builder.model.StructuredTypeImpl;
import com.sap.cds.impl.builder.model.StructuredTypeProxy;
import com.sap.cds.impl.builder.model.StructuredTypeRefImpl;
import com.sap.cds.impl.builder.model.SubQuery;
import com.sap.cds.impl.parser.builder.LimitBuilder;
import com.sap.cds.impl.parser.builder.SortSpecBuilder;
import com.sap.cds.impl.parser.token.Jsonizer;
import com.sap.cds.ql.FunctionCall;
import com.sap.cds.ql.Literal;
import com.sap.cds.ql.Parameter;
import com.sap.cds.ql.Predicate;
import com.sap.cds.ql.Searchable;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.Source;
import com.sap.cds.ql.StructuredType;
import com.sap.cds.ql.cqn.CqnLimit;
import com.sap.cds.ql.cqn.CqnLiteral;
import com.sap.cds.ql.cqn.CqnLock;
import com.sap.cds.ql.cqn.CqnModifier;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnSelect;
import com.sap.cds.ql.cqn.CqnSelectListItem;
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.CqnValue;
public class SelectBuilder> extends Select {
private final List columns = new ArrayList<>();
private final Set searchableElements = new HashSet<>();
private final List excluding = new ArrayList<>();
private final List groupBy = new ArrayList<>();
private final List orderBy = new ArrayList<>();
private final Source source;
private boolean distinct;
private boolean count;
private CqnPredicate where;
private CqnPredicate having;
private CqnLimit limit;
private CqnPredicate search;
private CqnLock rowLock;
private SelectBuilder(Source source) {
this.source = source;
}
public static > SelectBuilder from(Class entity) {
return new SelectBuilder<>(StructuredTypeProxy.create(entity));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static SelectBuilder> from(CqnStructuredTypeRef ref) {
return new SelectBuilder(structuredType(ref));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Select> from(CqnSelect select) {
SubQuery sq = new SubQuery(select);
return new SelectBuilder(sq);
}
@SuppressWarnings("unchecked")
public static SelectBuilder from(CqnSource source) {
if (source.isRef()) {
StructuredType structuredType = structuredType(source.asRef());
return new SelectBuilder<>(structuredType);
} else if (source.isSelect()) {
return new SelectBuilder<>((Source) source);
} else if (source.isJoin()) {
Source query = (Source) source;
return new SelectBuilder<>(query);
}
throw new IllegalStateException("unexpected source type " + source.getClass().getName());
}
public static , R extends StructuredType> SelectBuilder from(Class type,
Function path) {
return new SelectBuilder<>(path.apply(StructuredTypeProxy.create(type)));
}
public static > SelectBuilder from(Source source) {
return new SelectBuilder<>(source);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static SelectBuilder> from(String entityName) {
return new SelectBuilder(structuredType(entityName));
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public static SelectBuilder> from(String entity, UnaryOperator> path) {
return new SelectBuilder(path.apply(structuredType(entity)));
}
public static SelectBuilder> copy(CqnSelect select) {
return copy(select, ExpressionVisitor.COPY);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static SelectBuilder> copy(CqnSelect select, CqnModifier modifier) {
SelectBuilder copy = SelectBuilder.from(copy(select.from(), modifier));
copy.distinct = modifier.distinct(select.isDistinct());
copy.count = modifier.inlineCount(select.hasInlineCount());
copy.where = modifier.where(ExpressionVisitor.copy(select.where(), modifier));
copy.having = modifier.having(ExpressionVisitor.copy(select.having(), modifier));
copy.search = modifier.search(ExpressionVisitor.copy(select.search(), modifier));
copy.searchableElements
.addAll(modifier.searchableElements(new HashSet<>(((SelectBuilder) select).searchableElements)));
select.limit().ifPresent(limit -> copy.limit = modifier
.limit(LimitBuilder.rows(limit.top()).offset(limit.skip())));
select.getLock().ifPresent(lock -> copy.rowLock = lock);
Set excluding = modifier.excluding(new HashSet<>(select.excluding()));
List items = modifier.items(ExpressionVisitor.copy(select.items(), modifier));
List groupBy = modifier.groupBy(ExpressionVisitor.copy(select.groupBy(), modifier));
List orderBy = modifier
.orderBy(select.orderBy().stream().map(c -> ExpressionVisitor.copy(c, modifier)).collect(toList()));
return copy.columns(items).excluding(excluding).groupBy(groupBy).orderBy(orderBy);
}
private static CqnSource copy(CqnSource source, CqnModifier modifier) {
if (source.isRef()) {
return ExpressionVisitor.copy(source.asRef(), modifier);
}
if (source.isSelect()) {
CqnSelect inner = source.asSelect();
return new SubQuery(SelectBuilder.copy(inner, modifier));
}
throw new UnsupportedOperationException("Joins are not supported as source of SELECT");
}
@Override
public Source from() {
return source;
}
@Override
public CqnStructuredTypeRef ref() {
return StructuredTypeRefImpl.typeRef(source);
}
@Override
public SelectBuilder distinct() {
return distinct(true);
}
public SelectBuilder distinct(boolean distinct) {
this.distinct = distinct;
return this;
}
@Override
public boolean isDistinct() {
return distinct;
}
@Override
public SelectBuilder inlineCount() {
return count(true);
}
public SelectBuilder count(boolean count) {
this.count = count;
return this;
}
@Override
public boolean hasInlineCount() {
return count;
}
@Override
public SelectBuilder columns(List items) {
return columns(items.stream());
}
@Override
public SelectBuilder columns(Stream items) {
columns.clear();
items.forEach(this::columns);
return this;
}
private SelectBuilder columns(CqnSelectListItem item) {
columns.add(refToSlv(item));
return this;
}
@Override
public SelectBuilder columns(String... elements) { // NOSONAR
return columns(stream(elements).map(e -> select(e).build()));
}
public SelectBuilder addItem(CqnSelectListItem item) {
columns.add(item);
return this;
}
public Select columns(CqnLiteral> literal) {
return columns(select(literal).as("val").build());
}
@Override
public List items() {
return Collections.unmodifiableList(columns);
}
@Override
public SelectBuilder excluding(Collection slis) {
excluding.clear();
excluding.addAll(slis);
return this;
}
public SelectBuilder addExclude(String slis) {
excluding.add(slis);
return this;
}
@Override
public List excluding() {
return Collections.unmodifiableList(excluding);
}
@Override
public SelectBuilder where(Function pred) {
return where(pred.apply(getType()));
}
@Override
public SelectBuilder where(CqnPredicate pred) {
where = pred;
return this;
}
public SelectBuilder where(Optional extends CqnPredicate> optionalWhere) {
where = optionalWhere.orElse(null);
return this;
}
@Override
public SelectBuilder matching(Map values) {
return where(ExpressionImpl.matching(values));
}
@Override
public SelectBuilder byId(Object idValue) {
return where(ExpressionImpl.byId(idValue));
}
@Override
public SelectBuilder search(String searchTerm) {
return search(searchTerm, Collections.emptyList());
}
@Override
public SelectBuilder search(String searchTerm, Iterable searchableElements) {
return search(searchable -> searchable.has(searchTerm), searchableElements);
}
@Override
public SelectBuilder search(Function pred) {
return search(pred, Collections.emptyList());
}
@Override
public SelectBuilder search(Function pred, Iterable searchableElements) {
this.searchableElements.clear();
searchableElements.forEach(this.searchableElements::add);
search = pred.apply(SearchPredicate::new);
return this;
}
@Override
public SelectBuilder search(CqnPredicate predicate) {
search = predicate;
return this;
}
public Collection searchableElements() {
return Collections.unmodifiableCollection(searchableElements);
}
@Override
public Optional where() {
return Optional.ofNullable(where);
}
@Override
public Optional search() {
return Optional.ofNullable(search);
}
@Override
public Select lock(int timeout) {
this.rowLock = new LockImpl(timeout);
return this;
}
@Override
public Select lock() {
this.rowLock = new LockImpl();
return this;
}
public Select lockSelectedRows(CqnLock rowLock) {
this.rowLock = rowLock;
return this;
}
@Override
public Optional getLock() {
return Optional.ofNullable(rowLock);
}
@Override
public SelectBuilder groupBy(List items) {
groupBy.clear();
groupBy.addAll(items);
return this;
}
@Override
public Select groupBy(String... refs) {
groupBy.clear();
Arrays.stream(refs).map(ElementRefImpl::parse).forEach(groupBy::add);
return this;
}
@Override
public List groupBy() {
return Collections.unmodifiableList(groupBy);
}
@Override
public SelectBuilder having(Function pred) {
return having(pred.apply(getType()));
}
@Override
public SelectBuilder having(CqnPredicate pred) {
having = pred;
return this;
}
public SelectBuilder having(Optional pred) {
having = pred.orElse(null);
return this;
}
@Override
public Optional having() {
return Optional.ofNullable(having);
}
@Override
public final SelectBuilder orderBy(List sortSpec) {
orderBy.clear();
orderBy.addAll(sortSpec);
return this;
}
@Override
public List orderBy() {
return Collections.unmodifiableList(orderBy);
}
@Override
public Select orderBy(String... refs) {
orderBy.clear();
for (String ref : refs) {
orderBy.add(SortSpecBuilder.by(ElementRefImpl.parse(ref)).build());
}
return this;
}
@Override
public SelectBuilder limit(long rows, long offset) {
return limit(LimitBuilder.rows(rows).offset(offset).build());
}
public SelectBuilder limit(CqnLimit limit) {
this.limit = limit;
return this;
}
public SelectBuilder limit(Optional optionalLimit) {
this.limit = optionalLimit.orElse(null);
return this;
}
@Override
public Optional limit() {
return Optional.ofNullable(limit);
}
@Override
public T getType() {
return source.getType();
}
public static Parameter param() {
return CqnParam.param();
}
public static Parameter param(String name) {
return CqnParam.param(name);
}
public static FunctionCall func(String functionName, CqnValue... args) {
return ScalarFunctionCall.create(functionName, args);
}
public static Literal literal(U val) {
return LiteralImpl.literal(val);
}
public static Predicate not(Predicate p) {
return Negation.not(p);
}
@Override
public String toJson() {
Jsonizer cqn = Jsonizer.object("from", source);
if (isDistinct()) {
cqn.put("distinct", true);
}
if (hasInlineCount()) {
cqn.put("count", true);
}
if (!columns.isEmpty()) {
ArrayNode cols = cqn.array("columns");
columns.forEach(cols::addPOJO);
}
if (!excluding.isEmpty()) {
ArrayNode excl = cqn.array("excluding");
excluding.forEach(excl::add);
}
where().ifPresent(w -> {
ArrayNode whereArr = cqn.array("where");
w.tokens().forEach(whereArr::addPOJO);
});
if (!groupBy.isEmpty()) {
ArrayNode groupByArray = cqn.array("groupBy");
groupBy.forEach(groupByArray::addPOJO);
}
having().ifPresent(h -> {
ArrayNode havingArr = cqn.array("having");
h.tokens().forEach(havingArr::addPOJO);
});
if (!orderBy.isEmpty()) {
ArrayNode orderByArray = cqn.array("orderBy");
orderBy.forEach(orderByArray::addPOJO);
}
limit().ifPresent(l -> cqn.put("limit", l));
search().ifPresent(s -> cqn.put("search", s));
getLock().ifPresent(f -> cqn.put("forUpdate", f.timeout().isPresent() ? f : Jsonizer.empty()));
return Jsonizer.object("SELECT", cqn).toJson();
}
public CqnSelect build() {
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy