Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sap.cds.impl.AssociationLoader Maven / Gradle / Ivy
/************************************************************************
* © 2019-2023 SAP SE or an SAP affiliate company. All rights reserved. *
************************************************************************/
package com.sap.cds.impl;
import static com.sap.cds.impl.LazyRowImpl.lazyRow;
import static com.sap.cds.impl.builder.model.ExpressionImpl.matching;
import static com.sap.cds.impl.builder.model.StructuredTypeRefImpl.typeRef;
import static com.sap.cds.impl.parser.token.RefSegmentImpl.refSegment;
import static com.sap.cds.util.CdsModelUtils.element;
import static com.sap.cds.util.CdsModelUtils.isSingleValued;
import static com.sap.cds.util.CdsModelUtils.keyNames;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.sap.cds.CdsDataStore;
import com.sap.cds.CdsException;
import com.sap.cds.impl.builder.model.ExpandBuilder;
import com.sap.cds.impl.parser.token.RefSegmentBuilder;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.cqn.CqnElementRef;
import com.sap.cds.ql.cqn.CqnExpand;
import com.sap.cds.ql.cqn.CqnReference;
import com.sap.cds.ql.cqn.CqnReference.Segment;
import com.sap.cds.ql.cqn.CqnSelect;
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.CqnStructuredTypeRef;
import com.sap.cds.ql.cqn.CqnVisitor;
import com.sap.cds.ql.impl.SelectBuilder;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.util.DataUtils;
public class AssociationLoader {
private final CdsDataStore dataStore;
private final CdsStructuredType root;
private final Map rootValues = new HashMap<>();
public AssociationLoader(CdsDataStore dataStore, CdsStructuredType root) {
this.dataStore = dataStore;
this.root = root;
}
public void addValueOfRootEntity(CqnSelectListValue slv, Object value) {
rootValues.put(slv, value);
}
public void expand(CqnExpand expand, Map row) {
boolean lazy = ((ExpandBuilder>) expand).lazy();
expand(stream(expand.ref()), expand.items(), expand.orderBy(), expand.top(), expand.skip(), expand.alias(), row,
lazy);
}
public void expand(Stream paths, List slis,
List orderBy, long top, long skip, Optional alias, Map row,
boolean lazy) {
if (dataStore != null) {
List p = paths.collect(Collectors.toList());
injectors().forEach(i -> i.injectInto(row, p, slis, orderBy, top, skip, alias, lazy));
}
}
private Stream stream(CqnStructuredTypeRef prefix) {
if (prefix.size() == 1 && prefix.segments().get(0).id().equals("*")) {
return root.associations().map(a -> typeRef(a.getName()));
}
return Stream.of(prefix);
}
private Stream injectors() {
Map> keys = new HashMap<>();
rootValues.forEach((sli, value) -> {
if (value != null && sli.isRef() && sli.asRef().size() == 1) {
Optional keyElement = getKeyElement(sli);
keyElement.ifPresent(key -> {
CdsEntity declaringEntity = key.getDeclaringType();
keys.computeIfAbsent(declaringEntity, e -> new HashMap()).put(key.getName(), value);
});
}
});
return keys.entrySet().stream().map(e -> new LazyAssociationLoaderInjector(e.getKey(), e.getValue()));
}
private Optional getKeyElement(CqnSelectListItem sli) {
KeyFilter keyFilter = new KeyFilter();
sli.accept(keyFilter);
return keyFilter.keyElement();
}
private class LazyAssociationLoaderInjector {
private CdsEntity entity;
private Map keyValues;
LazyAssociationLoaderInjector(CdsEntity entity, Map keyValues) {
this.entity = entity;
this.keyValues = keyValues;
}
private void injectInto(Map row, Collection paths,
List slis, List orderBy, long top, long skip,
Optional alias, boolean lazy) {
paths.forEach(path -> injectInto(row, relativePath(entity, path), slis, orderBy, top, skip, alias, lazy));
}
private void injectInto(Map row, CqnStructuredTypeRef path, List slis,
List orderBy, long top, long skip, Optional alias, boolean lazy) {
CqnSelect query = query(path, slis, orderBy, top, skip);
Lazy loader = singleValued(entity, path) ? lazyRow(dataStore, query) : new LazyResultImpl(dataStore, query);
String displayName = alias.orElse(path.lastSegment());
if (lazy) {
DataUtils.putPath(row, displayName, loader);
} else {
Object value = loader.loadData();
boolean createMaps = value != null && !(value instanceof List && ((List>) value).isEmpty());
DataUtils.putPath(row, displayName, value, createMaps);
}
}
private CqnStructuredTypeRef relativePath(CdsEntity entity, CqnStructuredTypeRef path) {
if (!root.getQualifiedName().equals(entity.getQualifiedName())) {
List segments = new ArrayList<>(path.size());
CdsStructuredType e = root;
boolean add = false;
for (Segment seg : path.segments()) {
if (e.findAssociation(seg.id()).isPresent()) {
e = e.getTargetOf(seg.id());
if (add) {
segments.add(seg);
} else if (e.getQualifiedName().equals(entity.getQualifiedName())) {
add = true;
}
}
}
if (segments.isEmpty()) {
throw new IllegalStateException("Cannot resolve path: " + entity + "." + path);
}
return typeRef(segments, null);
}
return path;
}
private boolean singleValued(CdsEntity entity, CqnStructuredTypeRef path) {
CdsEntity e = entity;
CdsElement association = null;
for (Segment seg : path.segments()) {
String assocName = seg.id();
association = e.getAssociation(assocName);
e = e.getTargetOf(assocName);
}
if (association == null) {
throw new CdsException(
"Missing association for Entity " + e.getName() + ", under Path " + path.toJson() + ".");
}
return isSingleValued(association.getType());
}
private CqnSelect query(CqnStructuredTypeRef path, List slis,
List orderBy, long top, long skip) {
if (!keyValues.keySet().containsAll(keyNames(entity))) {
throw new CdsException("Missing key values for entity " + entity.getQualifiedName()
+ ". Please add all keys to the projection.");
}
List segments = new ArrayList<>();
segments.add(refSegment(entity.getQualifiedName(), matching(keyValues)));
segments.addAll(path.segments());
Select> query = SelectBuilder.from(typeRef(segments, null)).columns(slis).orderBy(orderBy).limit(top, skip);
return query;
}
}
private class KeyFilter implements CqnVisitor {
private CdsElement keyElement;
@Override
public void visit(CqnElementRef ref) {
CdsElement e = element(root, ref);
if (e != null && e.isKey()) {
keyElement = e;
}
}
Optional keyElement() {
return Optional.ofNullable(keyElement);
}
}
}