Search

Working with CDS Models

The Model Reflection API is a set of interfaces, which provide the ability to introspect a CDS model and retrieve details on the services, types, entities, and their elements that are defined by the model.

Content

The CDS Model

The interface CdsModel represents the complete CDS model of the CAP application and is the starting point for the introspection.

The CdsModel can be obtained from the EventContext:

import com.sap.cds.services.EventContext;
import com.sap.cds.reflect.CdsModel;

@On(event = "READ", entity = "my.catalogservice.books")
public void readBooksVerify(EventContext context) {
    CdsModel model = context.getModel();
   [...]
}

or, in Spring, be injected.

On a lower level, the CdsModel can be obtained from the CdsDataStoreConnector, or using the read method from a CSN String or InputStream:

InputStream csnJson = ...
CdsModel model = CdsModel.read(csnJson);

Examples

The following examples are using this CDS model:

namespace my.bookshop;

entity Books {
  title  : localized String(111);
  author : Association to Authors;
  ...
}

entity Authors {
  key ID : Integer;
  ...
}

entity Orders {
  OrderNo  : String @title:'Order Number';
  ...
}

Get and Inspect an Element of an Entity

In this example, we introspect the details of the type of the element title of the entity Books:

CdsEntity books = model.getEntity("my.bookshop.Books");
CdsElement title = books.getElement("title");

boolean key = title.isKey();      // false
CdsType type = title.getType();   // CdsSimpleType

if (type.isSimple()) {   // true
  CdsSimpleType simple = type.as(CdsSimpleType.class);

  String typeName = simple.getQualifiedName();  // "cds.String"
  CdsBaseType baseType = simple.getType();      // CdsBaseType.STRING
  Class<?> javaType = simple.getJavaType();     // String.class
  Boolean localized = simple.get("localized");  // true
  Integer length = simple.get("length");        // 111
}

Get and Inspect All Elements of an Entity

CdsEntity books = model.getEntity("my.bookshop.Books");	
Stream<CdsElement> elements = books.elements();	

The method elements() returns a stream of all elements of the given entity, structured type, or event. It’s important to note that the Model Reflection API doesn’t guarantee the element order to be exactly like in the source CSN document. However, the order is guaranteed to be stable during multiple consecutive model reads.

In case the element names are known beforehand it’s recommended to access them by name through the getElement(String name) method.

Get and Inspect an Association Element of an Entity

We can also analyze the details of an association:

CdsElement authorElement = book.getAssociation("author");
CdsAssociationType toAuthor = authorElement.getType();

CdsEntity author = toAuthor.getTarget(); // Entity: my.bookshop.Authors
boolean association = toAuthor.isAssociation();   // true
boolean composition = toAuthor.isComposition();   // false

Cardinality cardinality = toAuthor.getCardinality();
String sourceMax = cardinality.getSourceMax();    // "*"
String targetMin = cardinality.getTargetMin();    // "0"
String targetMax = cardinality.getTargetMax();    // "1"

Stream<CdsElement> keys = toAuthor.keys();  // Stream: [ ID ]
Optional<CqnExpression> onCondition = toAuthor.onCondition(); // empty

Find an Annotation by Name and Get Its Value

Here, we programmatically check if the element OrderNo carries the annotation title and set the value of displayName depending on the presence of the annotation:

CdsEntity order = model.getEntity("my.bookshop.Orders");
CdsElement orderNo = order.getElement("OrderNo");

Optional<CdsAnnotation<String>> annotation = orderNo
        .findAnnotation("title");
String displayName = annotation.map(CdsAnnotation::getValue)
        .orElse(orderNo.getName());   // "Order Number"

Filter a Stream of Services for non-abstract Services

Using a stream we determine all non-abstract services:

Stream<CdsService> services = model.services()
    .filter(s -> !s.isAbstract());
List<CdsService> serviceList = services.collect(Collectors.toList());

Filter a Stream of Entities by Namespace

The static method com.sap.cds.reflect.CdsDefinition.byNamespace allows to create a predicate to filter a stream of definitions (for example, entities, elements, …) for definitions contained in a given namespace:

import static com.sap.cds.reflect.CdsDefinition.byNamespace;
...

Stream<CdsEntity> entities = model.entities()
    .filter(byNamespace("my.bookshop"));

Get All Elements with Given Annotation

The static method com.sap.cds.reflect.CdsAnnotatable.byAnnotation allows to create a predicate to filter a stream of annotatable model components (for example, entities, elements, …) for components that carry a given annotation:

import static com.sap.cds.reflect.CdsAnnotatable.byAnnotation;
...

CdsEntity order = model.getEntity("my.bookshop.Orders");
Stream<CdsElement> elements = order.elements()
    .filter(byAnnotation("title"));
Show/Hide Beta Features