com.sap.cds.adapter.odata.v4.serializer.json.Struct2JsonBuilder 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.serializer.json;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import org.apache.olingo.commons.api.IConstants;
import org.apache.olingo.commons.api.edm.EdmElement;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.core.edm.primitivetype.EdmStream;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.queryoption.ExpandItem;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.api.uri.queryoption.SelectItem;
import org.apache.olingo.server.api.uri.queryoption.SelectOption;
import org.apache.olingo.server.core.serializer.utils.ContextURLBuilder;
import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
import org.apache.olingo.server.core.uri.UriInfoImpl;
import org.apache.olingo.server.core.uri.queryoption.SelectItemImpl;
import org.apache.olingo.server.core.uri.queryoption.SelectOptionImpl;
import com.sap.cds.adapter.odata.v4.serializer.json.api.Data2Json;
import com.sap.cds.adapter.odata.v4.serializer.json.api.PropertyInfo;
import com.sap.cds.adapter.odata.v4.serializer.json.options.NavigationProperty2JsonOptions;
import com.sap.cds.adapter.odata.v4.serializer.json.options.Struct2JsonOptions;
import com.sap.cds.adapter.odata.v4.serializer.json.primitive.String2Json;
import com.sap.cds.adapter.odata.v4.utils.ETagHelper;
import com.sap.cds.adapter.odata.v4.utils.StreamUtils;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.services.utils.ETagUtils;
import com.sap.cds.services.utils.StringUtils;
/**
* Abstract builder for both complex types and entities.
*/
public abstract class Struct2JsonBuilder {
protected final Struct2JsonOptions options;
protected final EdmStructuredType structType;
protected IConstants constants;
protected String eTagProperty;
protected ServiceMetadata metadata;
protected Set selected = null;
protected boolean hasExpand;
protected ExpandItem expandAll;
protected String resourceName;
protected ContentType contentType;
protected Struct2JsonBuilder(Struct2JsonOptions options, EdmStructuredType structType) {
this.options = options;
this.structType = structType;
this.resourceName = options.getContextURL() == null ? null
: options.getContextURL().getEntitySetOrSingletonOrType();
}
protected EdmStructuredType structType() {
return structType;
}
protected void addContextAnnotation(Struct2Json struct2json, boolean withETag) {
if (!options.isODataMetadataNone()) {
if (options.getContextURL() != null) { // TODO: check isRootLevel
// "@context":"$metadata#Travel(to_Customer(CustomerID,FirstName,LastName))/$entity",
struct2json.context = String2Json.constant(constants.getContext(),
ContextURLBuilder.create(options.getContextURL()).toASCIIString());
// "@metadataEtag": // "W/\"bcbb542a7b53905240b\"",
if (metadata.getServiceMetadataETagSupport() != null
&& metadata.getServiceMetadataETagSupport().getMetadataETag() != null) {
struct2json.metadataEtag = String2Json.constant(constants.getMetadataEtag(),
metadata.getServiceMetadataETagSupport().getMetadataETag());
}
}
eTagProperty = withETag ? ETagHelper.getETagProperty(options.getGlobals(), structType) : null;
}
}
protected void createSelectList() {
if (!ExpandSelectHelper.isAll(options.getSelect())) {
selected = ExpandSelectHelper.getSelectedPropertyNames(options.getSelect().getSelectItems());
}
}
protected void addTypeAnnotation(Struct2Json struct2json) {
// "@type": "#TravelService.Travel"
final EdmStructuredType resolvedType = StructTypeHelper.resolveEntityType(metadata, structType,
options.getDerivedTypeName());
if (options.isODataMetadataFull() ||
(!options.isODataMetadataNone() && !resolvedType.equals(structType))) {
struct2json.type = String2Json.constant(constants.getType(),
"#" + structType.getFullQualifiedName().getFullQualifiedNameAsString());
}
}
protected void createProperties(Struct2Json struct2json) {
struct2json.primitiveProperties = createPrimitiveAndComplexProperties(struct2json, structType, eTagProperty, selected);
struct2json.navigationProperties = createNavigationProperties();
}
/* Add dynamic properties to a serialization tree. Additionally notice the non-serializable properties */
public void addDynamicExpandProperties(Struct2Json struct2json, Set potentialDynamicProperties) {
struct2json.dynamicProperties = new ArrayList<>();
for (Iterator it = potentialDynamicProperties.iterator(); it.hasNext();) {
String p = it.next();
if (structType.getNavigationPropertyNames().contains(p)) {
EdmElement navigationProperty = structType.getProperty(p);
NavigationProperty2JsonOptions navOptions = NavigationProperty2JsonOptions
.with(contentType, options.getODataVersion(), options.getGlobals())
.contextURL(options.getContextURL())
.writeOnlyReferences(options.getWriteOnlyReferences())
.autoExpand(options.isAutoExpand())
.build();
struct2json.dynamicProperties
.add(NavigationProperty2JsonBuilder.createNavigation(navigationProperty, contentType,
navOptions, false));
it.remove();
}
}
if (null == struct2json.excludedProperties) {
struct2json.excludedProperties = potentialDynamicProperties;
} else {
struct2json.excludedProperties.addAll(potentialDynamicProperties);
}
}
/* Create navigations via association, complex types not included */
private List>> createNavigationProperties() {
List>> navigationProperties = new ArrayList<>(
structType.getNavigationPropertyNames().size());
hasExpand = ExpandSelectHelper.hasExpand(options.getExpand());
if (hasExpand) {
expandAll = ExpandSelectHelper.getExpandAll(options.getExpand());
}
for (String navigationPropertyName : structType.getNavigationPropertyNames()) {
addNavigationLink(navigationPropertyName);
if (hasExpand) {
EdmElement navigationProperty = structType.getProperty(navigationPropertyName);
ExpandItem innerExpandItem = ExpandSelectHelper.getExpandItemBasedOnType(
options.getExpand().getExpandItems(),
navigationPropertyName, structType, resourceName);
ExpandOption innerExpandOption = null;
if (innerExpandItem != null) {
innerExpandOption = innerExpandItem.getExpandOption();
} else if (expandAll != null) {
innerExpandOption = expandAll.getExpandOption();
} else {
continue;
}
NavigationProperty2JsonOptions navOptions = NavigationProperty2JsonOptions
.with(contentType, options.getODataVersion(), options.getGlobals())
.contextURL(options.getContextURL())
.expand(innerExpandOption)
.count(innerExpandItem == null ? null : innerExpandItem.getCountOption())
.countPath(innerExpandItem != null && innerExpandItem.hasCountPath())
.select(innerExpandItem == null ? null : innerExpandItem.getSelectOption())
.writeOnlyReferences(options.getWriteOnlyReferences())
.autoExpand(options.isAutoExpand())
.build();
navigationProperties
.add(NavigationProperty2JsonBuilder.createNavigation(navigationProperty, contentType, navOptions, false));
}
}
return navigationProperties;
}
/* Create a list of simple and complex properties */
private List>> createPrimitiveAndComplexProperties(Struct2Json struct2json, EdmStructuredType structType, String eTagProperty,
Set selected) {
List>> properties = new ArrayList<>(2 * structType.getPropertyNames().size());
for (String propertyName : structType.getPropertyNames()) { // we stick to this order
if (Objects.equals(propertyName, eTagProperty)) {
struct2json.etag = String2Json.val(constants.getEtag(),
row -> ETagUtils.createETagHeaderValue(String.valueOf(row.get(propertyName))));
}
if (!options.getWriteOnlyReferences() && isSelected(selected, propertyName)) {
PropertyInfo propertyInfo = PropertyInfo.from((EdmProperty) structType.getProperty(propertyName),
options.isStrictSerialization());
if (options.isODataMetadataFull()) {
Data2Json