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

com.sap.cds.adapter.odata.v4.serializer.json.Entity2JsonBuilder Maven / Gradle / Ivy

There is a newer version: 3.2.0
Show newest version
/**************************************************************************
 * (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 **************************************************************************/
package com.sap.cds.adapter.odata.v4.serializer.json;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Map;
import java.util.function.Function;

import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.server.core.serializer.utils.ExpandSelectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cds.adapter.odata.v4.processors.response.ResultSetProcessor;
import com.sap.cds.adapter.odata.v4.serializer.json.options.Struct2JsonOptions;
import com.sap.cds.adapter.odata.v4.serializer.json.primitive.String2Json;

public final class Entity2JsonBuilder extends Struct2JsonBuilder {

	private static final Logger logger = LoggerFactory.getLogger(Entity2JsonBuilder.class);
	private boolean keysAreSelected = true;
	private Entity2Json entity2json;

	protected Entity2JsonBuilder(Struct2JsonOptions options, EdmStructuredType entityType) {
		super(options, entityType);
	}

	public static Entity2Json createRoot(
			Struct2JsonOptions options,
			EdmStructuredType entityType,
			ContentType contentType) {

		Entity2JsonBuilder builder = new Entity2JsonBuilder(options, entityType);
		return builder.create(contentType, true);
	}

	public static Entity2Json createNested(
			Struct2JsonOptions options,
			EdmStructuredType entityType,
			ContentType contentType) {

		Entity2JsonBuilder builder = new Entity2JsonBuilder(options, entityType);
		return builder.create(contentType, false);
	}

	protected EdmEntityType structType() {
		return (EdmEntityType) structType;
	}

	private Entity2Json create(ContentType contentType, boolean isRootLevel) {
		this.contentType = contentType;
		constants = options.getConstants();
		metadata = options.getGlobals().getServiceMetadata();

		entity2json = new Entity2Json();
		entity2json.isRootLevel = isRootLevel;
		entity2json.keyPredicate = row -> getOrCreateKeyPredicate(row);
		entity2json.isAutoExpand = options.isAutoExpand();
		entity2json.isOpenType = this.structType.isOpenType();

		createSelectList();
		addContextAnnotation(entity2json, true);
		calculateEntityId();
		addTypeAnnotation(entity2json);

		if (options.getWriteOnlyReferences()) {
			return entity2json;
		}

		createProperties(entity2json);
		entity2json.builder = this;
		return entity2json;
	}

	@Override
	protected void addTypeAnnotation(Struct2Json  entity2json) {
		// "@type": "#TravelService.Travel"
		final EdmStructuredType resolvedType = StructTypeHelper.resolveEntityType(metadata, structType,
				options.getDerivedTypeName());
		if (options.isODataMetadataFull() ||
				(!options.isODataMetadataNone()  && !keysAreSelected
						&& !resolvedType.equals(structType))) {
			entity2json.type = String2Json.constant(constants.getType(),
					"#" + structType().getFullQualifiedName().getFullQualifiedNameAsString());
		}
	}

	@Override
	protected void createSelectList() {
		if (!ExpandSelectHelper.isAll(options.getSelect())) {
			selected = ExpandSelectHelper.getSelectedPropertyNames(options.getSelect().getSelectItems());
			// keys need to be always selected
			if (!selected.isEmpty()) {
				for (String key : structType().getKeyPredicateNames()) {
					keysAreSelected = !selected.add(key) && keysAreSelected;
				}
			}
		}
	}

	private void calculateEntityId() {
		// "@id":"Travel(TravelUUID='123',IsActiveEntity=false)"
		if (options.isODataMetadataFull()
				|| options.getWriteOnlyReferences()
				|| (!options.isODataMetadataNone() && !keysAreSelected)) {
			entity2json.id = String2Json.val(options.getConstants().getId(),
					row -> getOrCreateId(row));
		}
	}

	@Override
	protected void addNavigationLink(String navigationPropertyName) {
		if (options.isODataMetadataFull()) {
			String2Json> navigationLinkProperty = String2Json
					.val(navigationPropertyName + constants.getNavigationLink(),
							row -> getOrCreateId(row) + "/" + navigationPropertyName);
			if (entity2json.navigationLinkProperties == null) {
				entity2json.navigationLinkProperties = new ArrayList<>(
						structType.getNavigationPropertyNames().size());
			}
			entity2json.navigationLinkProperties.add(navigationLinkProperty);
		}
	}

	// cached calculations:

	private static String getOrCreate(
			Map row,
			Function, String> provider,
			Map, String> calculated) {

		String value = calculated.get(row);
		if (value == null) {
			value = provider.apply(row);
			calculated.put(row, value);
		}
		return value;
	}

	public String getOrCreateKeyPredicate(Map row) {
		return getOrCreate(row, r -> ResultSetProcessor.getEntityId(structType(), r), entity2json.calculatedKeyPredicate);
	}

	public String getOrCreateId(Map row) {
		if (entity2json.bindingTarget == null) {
			entity2json.bindingTarget = options.getEdmUtils().getEdmBindingTarget(structType());
		}
		if (entity2json.bindingTarget == null) {
			// TODO this actually needs to build the correct link if no binding target is
			// available
			// we rely on a bindingTarget here. Olingo serializer seems to have an
			// alternative path.

			// throw new ErrorStatusException(CdsErrorStatuses.MISSING_BINDING_TARGET,
			// entityType.getFullQualifiedName().getFullQualifiedNameAsString());
			String keyPredicate = ResultSetProcessor.getEntityId(structType(), row);
			// In CAP entityset == entity type, except parameterized views,
			// where  =  - "Parameters"
			String singletonOrTypeName = options.getContextURL() == null ? null
					: options.getContextURL().getEntitySetOrSingletonOrType();
			String entitySetName = singletonOrTypeName == null ? structType.getName() : singletonOrTypeName;
			return entitySetName + keyPredicate;
		}

		return getOrCreate(
				row,
				r -> {
					try {
						return new URI(entity2json.bindingTarget.getName() + getOrCreateKeyPredicate(row))
								.toASCIIString();
					} catch (URISyntaxException e) {
						logger.error("Failed to create URI of entity", e);
						return ""; // we can't cancel the stream
					}
				},
				entity2json.calculatedId);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy