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

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

/**************************************************************************
 * (C) 2020-2023 SAP SE or an SAP affiliate company. All rights reserved. *
 **************************************************************************/
package com.sap.cds.reflect.impl;

import java.util.Collections;
import java.util.Iterator;
import java.util.Map.Entry;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.sap.cds.impl.parser.TokenParser;
import com.sap.cds.ql.CQL;
import com.sap.cds.ql.cqn.CqnValue;
import com.sap.cds.ql.cqn.Modifier;
import com.sap.cds.ql.impl.ExpressionVisitor;
import com.sap.cds.reflect.CdsBaseType;
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.ConstantLiteralSealingModifier;
import com.sap.cds.util.StructuredTypeResolver;

import static com.sap.cds.reflect.impl.CdsModelReader.findType;
import static com.sap.cds.reflect.impl.CdsModelReader.readType;

public class CdsElementReader {
	private static final IssueCollector issueCollector = IssueCollectorFactory
			.getIssueCollector(CdsElementReader.class);
	public static final Modifier LITERAL_SEALING_MODIFIER = new ConstantLiteralSealingModifier();

	private final String pathToElement;
	private final String elementName;
	private final CdsModelBuilder model;
	private final CdsModelReader.Config config;

	public CdsElementReader(CdsModelReader.Config config, String pathToElement, String elementName, CdsModelBuilder model) {
		this.pathToElement = pathToElement;
		this.elementName = elementName;
		this.model = model;
		this.config = config;
	}

	public CdsElementBuilder read(JsonNode csn, StructuredTypeResolver structResolver) {
		// remove element default val before reading the type
		JsonNode defaultVal = ((ObjectNode) csn).remove(CdsConstants.DEFAULT);
		Object defaultValue = CdsSimpleTypeReader.defaultValue(defaultVal);
		CdsTypeBuilder type = findType(csn, model).orElseGet(() -> readType(config, pathToElement, csn, model, structResolver));
		Iterator> fields = csn.fields();
		boolean isKey = false;
		boolean isVirtual = false;
		boolean isLocalized = false;
		boolean notNull = false;
		String doc = null;
		CqnValue calculatedBy = null;

		while (fields.hasNext()) {
			String property = fields.next().getKey();
			if (!CdsAnnotationReader.isAnnotation(property)) {
				switch (property) {
				case CdsConstants.ITEMS:
				case CdsConstants.TYPE:
				case CdsConstants.LENGTH:
				case CdsConstants.PRECISION:
				case CdsConstants.SCALE:
				case CdsConstants.ORIGIN:
				case CdsConstants.ENUM:
				case CdsConstants.CDS_LOCALIZED:
				case CdsConstants.ELEMENTS:
				case CdsConstants.INDEXNUM:
				case CdsConstants.$INFERRED:
				case CdsConstants.VALUE:
				case CdsConstants.ON:
				case CdsConstants.TARGET:
				case CdsConstants.TARGET_ASPECT:
				case CdsConstants.CARDINALITY:
					break;
				case CdsConstants.LOCALIZEDCSN:
					isLocalized = csn.get(property).asBoolean();
					break;
				case CdsConstants.KEYS:
					break;
				case CdsConstants.KEY:
					isKey = csn.get(property).asBoolean();
					break;
				case CdsConstants.VIRTUAL:
					isVirtual = csn.get(property).asBoolean();
					break;
				case CdsConstants.NOT_NULL:
					notNull = csn.get(property).asBoolean();
					break;
				case CdsConstants.DOC:
					if(config.readDocs()) {
						doc = csn.get(property).asText();
					}
					break;
				default:
					issueCollector.unrecognized(pathToElement,
							"The element '%s' contains an unrecognized property '%s'.", pathToElement, property);
				}
			}
			if (CdsConstants.VALUE.equals(property) && (!csn.get(property).has(CdsConstants.STORED))) {
				calculatedBy = parseCalculatedBy(csn.get(property));
				// Literal may infer its type. If the type is not set explicitly, the inferred one is used instead.
				if (!csn.has(CdsConstants.TYPE) && calculatedBy != null && calculatedBy.isLiteral()) {
					type = calculatedBy.type()
						.map(t -> CdsSimpleTypeBuilder.simpleType(CdsBaseType.cdsType(t), Collections.emptyMap()))
						.orElseGet(() -> CdsSimpleTypeReader.undefined(pathToElement));
				}
			}
		}
		return new CdsElementBuilder<>(CdsAnnotationReader.read(config, csn), elementName, type, isKey, isVirtual, notNull,
				isLocalized, defaultValue, doc).value(calculatedBy);
	}

	private CqnValue parseCalculatedBy(JsonNode node) {
		// arrayed elements are not yet supported by the compiler
		if (node.has("xpr")) {
			return ExpressionVisitor.copy(TokenParser.xpr(node), LITERAL_SEALING_MODIFIER);
		} else if (node.has("ref")) {
			return ExpressionVisitor.copy(TokenParser.elementRef(node), LITERAL_SEALING_MODIFIER);
		} else if (node.has("func")) {
			return ExpressionVisitor.copy(TokenParser.func(node), LITERAL_SEALING_MODIFIER);
		} else if (node.has("val")) {
			CqnValue result = TokenParser.val(node);
			// Seal literal as a constant
			if (result != null && result.isLiteral()) {
				return CQL.constant(result.asLiteral().value());
			}
		}
		return null;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy