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

com.sap.cds.reflect.impl.CdsAssociationReader Maven / Gradle / Ivy

There is a newer version: 3.8.0
Show newest version
/*******************************************************************
 * © 2020 SAP SE or an SAP affiliate company. All rights reserved. *
 *******************************************************************/
package com.sap.cds.reflect.impl;

import static com.sap.cds.impl.parser.TokenParser.elementRef;
import static com.sap.cds.reflect.impl.CdsAnnotationReader.isAnnotation;
import static com.sap.cds.reflect.impl.CdsAssociationTypeBuilder.managed;
import static com.sap.cds.reflect.impl.CdsAssociationTypeBuilder.unmanaged;
import static com.sap.cds.reflect.impl.CdsModelReader.readType;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.sap.cds.impl.parser.ExpressionParser;
import com.sap.cds.ql.cqn.CqnElementRef;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsAssociationType.Cardinality;
import com.sap.cds.reflect.impl.reader.issuecollector.IssueCollector;
import com.sap.cds.reflect.impl.reader.issuecollector.IssueCollectorFactory;
import com.sap.cds.reflect.impl.reader.model.CdsConstants;
import com.sap.cds.util.StructuredTypeResolver;

public class CdsAssociationReader {

	private static final IssueCollector issueCollector = IssueCollectorFactory
			.getIssueCollector(CdsAssociationReader.class);

	private final CdsModelBuilder model;
	private final StructuredTypeResolver structResolver;

	private final CdsModelReader.Config config;

	public CdsAssociationReader(CdsModelReader.Config config, CdsModelBuilder model, StructuredTypeResolver structResolver) {
		this.config = config;
		this.model = model;
		this.structResolver = structResolver;
	}

	public CdsTypeBuilder read(String path, JsonNode csn) {
		Iterator> fields = csn.fields();
		boolean isComposition = false;
		CdsEntityBuilder target = null;
		CdsTypeBuilder targetAspect = null;
		CqnPredicate on = null;
		Cardinality cardinality = null;
		Set keyElements = new LinkedHashSet<>();
		String doc = null;
		while (fields.hasNext()) {
			String elementName = fields.next().getKey();
			if (!isAnnotation(elementName)) {
				JsonNode elementValue = csn.get(elementName);
				switch (elementName) {
				case CdsConstants.TYPE:
					isComposition = elementValue.asText().equals(CdsConstants.COMPOSITION);
					break;
				case CdsConstants.TARGET:
					target = model.getEntity(elementValue.asText());
					break;
				case CdsConstants.TARGET_ASPECT:
					if (elementValue.has(CdsConstants.ELEMENTS)) {
						targetAspect = readTargetAspect(path, elementValue);
					} else {
						targetAspect = model.getAspect(elementValue.asText());
					}
					break;
				case CdsConstants.CARDINALITY:
					cardinality = readCardinality(path, csn.get(elementName), isComposition);
					break;
				case CdsConstants.ON:
					on = ExpressionParser.parsePredicate(elementValue.toString());
					break;
				case CdsConstants.KIND:
				case CdsConstants.KEYS:
					for (JsonNode n : elementValue) {
						CqnElementRef ref = elementRef(n);
						keyElements.add(ref);
						if (ref.size() > 1) {
							issueCollector.unsupported(path, "The association '%s' has a path as key '%s'.", path, ref);
						}
					}
					break;
				case CdsConstants.DOC:
					if (config.readDocs()) {
						doc = csn.get(elementName).asText();
					}
					break;
				case CdsConstants.KEY:
				case CdsConstants.NOT_NULL:
					break;
				default:
					issueCollector.unrecognized(path, "The association '%s' contains an unrecognized property '%s'.",
							path, elementName);
				}
			}
		}
		if (cardinality == null) {
			String srcMax = isComposition ? "1" : "*";
			cardinality = CdsAssociationTypeBuilder.CardinalityImpl.create(srcMax, "0", "1");
		}
		if (on != null) {
			return unmanaged(CdsAnnotationReader.read(config, csn), target, cardinality, isComposition, on, targetAspect, doc);
		}
		return managed(CdsAnnotationReader.read(config, csn), target, keyElements, cardinality, isComposition, targetAspect, doc);
	}

	private CdsTypeBuilder readTargetAspect(String typeName, JsonNode elementValue) {
		ObjectNode modifiedJsonNode = ((ObjectNode) elementValue).put(CdsConstants.KIND, CdsConstants.ASPECT);
		return readType(config, typeName, new ObjectMapper().convertValue(modifiedJsonNode, JsonNode.class), model,
				structResolver);
	}

	private static Cardinality readCardinality(String path, JsonNode csn, boolean isComposition) {
		String sourceMax = null;
		String targetMin = "0";
		String targetMax = "1";
		Iterator> fields = csn.fields();
		while (fields.hasNext()) {
			String property = fields.next().getKey();
			switch (property) {
			case CdsConstants.MAX:
				targetMax = csn.get(property).asText();
				break;
			case CdsConstants.SRC:
				sourceMax = csn.get(property).asText();
				break;
			case CdsConstants.MIN:
				targetMin = csn.get(property).asText();
				break;
			default:
				issueCollector.unrecognized(path,
						"The association has a cardinality specification with an unrecognized property '%s'", property);
			}
		}
		if (sourceMax == null) {
			if (isComposition || targetMax.equals("*")) {
				sourceMax = "1";
			} else {
				sourceMax = "*";
			}
		}
		return CdsAssociationTypeBuilder.CardinalityImpl.create(sourceMax, targetMin, targetMax);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy