@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
1,298 lines (1,218 loc) • 82.8 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*eslint-disable max-len */
sap.ui.define([
"./_ODataMetaModelUtils",
"sap/base/Log",
"sap/base/i18n/Localization",
"sap/base/util/isEmptyObject",
"sap/ui/base/BindingParser",
"sap/ui/base/ManagedObject",
"sap/ui/base/SyncPromise",
"sap/ui/model/_Helper",
"sap/ui/model/BindingMode",
"sap/ui/model/ClientContextBinding",
"sap/ui/model/Context",
"sap/ui/model/FilterProcessor",
"sap/ui/model/MetaModel",
"sap/ui/model/json/JSONListBinding",
"sap/ui/model/json/JSONModel",
"sap/ui/model/json/JSONPropertyBinding",
"sap/ui/model/json/JSONTreeBinding"
], function (Utils, Log, Localization, isEmptyObject, BindingParser, ManagedObject, SyncPromise, _Helper, BindingMode,
ClientContextBinding, Context, FilterProcessor, MetaModel, JSONListBinding, JSONModel, JSONPropertyBinding,
JSONTreeBinding) {
"use strict";
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.AbstractProperty
*
* The abstraction of an OData property.
*
* @property {string} name
* The name of the property
* @property {string} [default]
* The default value of the property
* @property {"false"|"true"} [nullable]
* Whether this property can be <code>null</code>
* @property {"false"|"true"} [readOnly]
* Whether this property is read-only
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.Annotatable
*
* An OData metadata element which can be annotated. For annotations from SAP or OData vocabularies, it may contain
* properties like <code>com.sap.vocabularies.Common.v1.Label</code> with the annotation value as an object.
* For annotations from the "http://www.sap.com/Protocols/SAPData" namespace, it may contain properties like
* <code>sap:label</code> with the annotation value as a string. For details on annotation representation, see
* {@link sap.ui.model.odata.ODataMetaModel ODataMetaModel} resp.
* {@link topic:6c47b2b39db9404582994070ec3d57a2%23loio341823349ed04df1813197f2a0d71db2 OData V2 Model - Meta Model
* for OData V2}.
*
* @property {Array<sap.ui.model.odata.ODataMetaModel.Extension>} [extensions]
* The array of extension elements, see {@link sap.ui.model.odata.ODataMetaModel.Extension Extension}
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.Extension
*
* An extension object generated from OData annotations.
*
* @property {string} name
* The name of the extension
* @property {string} namespace
* The namespace of the extension
* @property {string} value
* The value of the extension
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.BinaryProperty
*
* A property of type <code>Edm.Binary</code>, see
* <a href="http://www.odata.org/documentation/odata-version-2-0/overview#AbstractTypeSystem" target="_blank">
* <code>Edm.Binary</code></a>.
*
* @property {"Edm.Binary"} type
* The type name
* @property {string} [maxLength]
* The maximum size of the binary data
* @property {"false"|"true"} [fixedLength]
* Whether the length can vary
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.BooleanProperty
*
* A property of type <code>Edm.Boolean</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.Boolean}.
*
* @property {"Edm.Boolean"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.ByteProperty
*
* A property of type <code>Edm.Byte</code>, see the corresponding UI5 type {@link sap.ui.model.odata.type.Byte}.
*
* @property {"Edm.Byte"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.DateTimeProperty
*
* A property of type <code>Edm.DateTime</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.DateTime}.
*
* @property {"Edm.DateTime"} type
* The type name
* @property {string} [precision]
* The maximum number of fractional seconds
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.DateTimeOffsetProperty
*
* A property of type <code>Edm.DateTimeOffset</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.DateTimeOffset}.
*
* @property {"Edm.DateTimeOffset"} type
* The type name
* @property {string} [precision]
* The maximum number of fractional seconds
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.DecimalProperty
*
* A property of type <code>Edm.Decimal</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.Decimal}.
*
* @property {"Edm.Decimal"} type
* The type name
* @property {string} [precision]
* The maximum number of decimal digits
* @property {string} [scale]
* The maximum number of decimal digits to the right of the decimal point
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.DoubleProperty
*
* A property of type <code>Edm.Double</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.Double}.
*
* @property {"Edm.Double"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.GuidProperty
*
* A property of type <code>Edm.Guid</code>, see the corresponding UI5 type {@link sap.ui.model.odata.type.Guid}.
*
* @property {"Edm.Guid"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.Int16Property
*
* A property of type <code>Edm.Int16</code>, see the corresponding UI5 type {@link sap.ui.model.odata.type.Int16}.
*
* @property {"Edm.Int16"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.Int32Property
*
* A property of type <code>Edm.Int32</code>, see the corresponding UI5 type {@link sap.ui.model.odata.type.Int32}.
*
* @property {"Edm.Int32"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.Int64Property
*
* A property of type <code>Edm.Int64</code>, see the corresponding UI5 type {@link sap.ui.model.odata.type.Int64}.
*
* @property {"Edm.Int64"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.SByteProperty
*
* A property of type <code>Edm.SByte</code>, see the corresponding UI5 type {@link sap.ui.model.odata.type.SByte}.
*
* @property {"Edm.SByte"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.SingleProperty
*
* A property of type <code>Edm.Single</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.Single}.
*
* @property {"Edm.Single"} type
* The type name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.StreamProperty
*
* A property of type <code>Edm.Stream</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.Stream}.
*
* @property {"Edm.Stream"} type
* The type name
* @property {"false"|"true"} [fixedLength]
* Whether the stream requires a fixed length
* @property {string} [maxLength]
* The maximal length of the stream
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.StringProperty
*
* A property of type <code>Edm.String</code>, see the corresponding UI5 type
* {@link sap.ui.model.odata.type.String}.
*
* @property {"Edm.String"} type
* The type name
* @property {"false"|"true"} [fixedLength]
* Whether the string requires a fixed length
* @property {string} [maxLength]
* The maximal length of the string
* @property {"false"|"true"} [unicode]
* Whether Unicode characters are used instead of ASCII characters
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.AbstractProperty} sap.ui.model.odata.ODataMetaModel.TimeProperty
*
* A property of type <code>Edm.Time</code>, see the corresponding UI5 type {@link sap.ui.model.odata.type.Time}.
*
* @property {"Edm.Time"} type
* The type name
* @property {string} [precision]
* The maximum number of fractional seconds
*
* @public
*/
/**
* The alias type for an OData property of an entity type; the alias comprises all supported OData EDM types.
*
* @typedef {
* sap.ui.model.odata.ODataMetaModel.BinaryProperty |
* sap.ui.model.odata.ODataMetaModel.BooleanProperty |
* sap.ui.model.odata.ODataMetaModel.ByteProperty |
* sap.ui.model.odata.ODataMetaModel.DateTimeProperty |
* sap.ui.model.odata.ODataMetaModel.DateTimeOffsetProperty |
* sap.ui.model.odata.ODataMetaModel.DecimalProperty |
* sap.ui.model.odata.ODataMetaModel.DoubleProperty |
* sap.ui.model.odata.ODataMetaModel.GuidProperty |
* sap.ui.model.odata.ODataMetaModel.Int16Property |
* sap.ui.model.odata.ODataMetaModel.Int32Property |
* sap.ui.model.odata.ODataMetaModel.Int64Property |
* sap.ui.model.odata.ODataMetaModel.SByteProperty |
* sap.ui.model.odata.ODataMetaModel.SingleProperty |
* sap.ui.model.odata.ODataMetaModel.StreamProperty |
* sap.ui.model.odata.ODataMetaModel.StringProperty |
* sap.ui.model.odata.ODataMetaModel.TimeProperty
* } sap.ui.model.odata.ODataMetaModel.Property
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.AssociationEnd
*
* An object representing one end of an OData association.
*
* @property {string} type
* The qualified name of the entity type at the end of the association
* @property {"0" | "0..1" | "*"} multiplicity
* The multiplicity of the association end
* @property {string} role
* The name of the association role
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.AssociationSet
*
* An object representing an OData association set.
*
* @property {string} association
* The qualified name of the association set's association; the value is identical to the corresponding
* XML attribute value in the service metadata document.
* @property {Array<sap.ui.model.odata.ODataMetaModel.AssociationSetEnd>} end
* The two ends of the association set
* @property {string} name
* The association set's name
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.AssociationSetEnd
*
* An object representing one end of an OData association set.
*
* @property {string} entitySet
* The entity set's name at the end of the association
* @property {string} role
* The name of the association role
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.ComplexType
*
* An object representing an OData complex type.
*
* @property {string} $path
* The path to the complex type
* @property {string} name
* The complex type's name
* @property {string} namespace
* The complex type's namespace
* @property {Array<sap.ui.model.odata.ODataMetaModel.Property>} property
* The complex type's properties
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.PropertyRef
*
* An object representing an OData property reference.
*
* @property {string} name
* The property name
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.EntityKey
*
* An object representing an OData entity type's key.
*
* @property {Array<sap.ui.model.odata.ODataMetaModel.PropertyRef>} propertyRef
* The references to the properties defining the entity type's key
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.EntitySet
*
* An object representing an OData entity set.
*
* @property {string} entityType
* The qualified name of the entity set's entity type
* @property {string} name
* The name of the entity set
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.NavigationProperty
*
* An OData navigation property of an entity type.
*
* @property {string} name
* The name of the navigation property
* @property {string} fromRole
* The name of the starting point for the navigation; refers to a role defined in the association
* @property {string} relationship
* The qualified name of the navigation property's association
* @property {string} toRole
* The name of the other end of the relationship; refers to a role defined in the association
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.EntityType
*
* An object representing an OData entity type.
*
* @property {string} $path
* The path to the entity type
* @property {sap.ui.model.odata.ODataMetaModel.EntityKey} key
* The entity type's key
* @property {string} name
* The entity type's name
* @property {string} namespace
* The entity type's namespace
* @property {Array<sap.ui.model.odata.ODataMetaModel.NavigationProperty>} [navigationProperty]
* The entity type's navigation properties
* @property {Array<sap.ui.model.odata.ODataMetaModel.Property>} [property]
* The entity type's properties
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable}
* sap.ui.model.odata.ODataMetaModel.FunctionImportParameter
*
* An object representing an OData function import parameter.
*
* @property {string} name
* The function import parameter's name
* @property {string} type
* The function import parameter's type; the value is identical to the corresponding XML attribute value in the
* service metadata document.
* @property {"false"|"true"} [fixedLength]
* The fixedLength constraint if supported by the function import parameter's type
* @property {string} [maxLength]
* The maxLength constraint if supported by the function import parameter's type
* @property {"In"|"InOut"|"Out"} [mode]
* The function import parameter's mode
* @property {string} [precision]
* The precision constraint if supported by the function import parameter's type
* @property {string} [scale]
* The scale constraint if supported by the function import parameter's type
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.FunctionImport
*
* An object representing an OData function import.
*
* @property {string} [entitySet]
* The entity set of the function import's return value
* @property {"GET"|"POST"} [httpMethod]
* The http method to execute the function import
* @property {string} name
* The function import's name
* @property {Array<sap.ui.model.odata.ODataMetaModel.FunctionImportParameter>} [parameter]
* The function import parameters
* @property {string} [returnType]
* The qualified name of the function import's return type; the value is identical to the corresponding
* XML attribute value in the service metadata document.
*
* @public
*/
/**
* @typedef {sap.ui.model.odata.ODataMetaModel.Annotatable} sap.ui.model.odata.ODataMetaModel.EntityContainer
*
* An object representing an OData entity container.
*
* @property {string} $path
* The path to the entity container
* @property {string} name
* The entity container's name
* @property {string} namespace
* The entity container's namespace
* @property {Array<sap.ui.model.odata.ODataMetaModel.AssociationSet>} [associationSet]
* The association sets in the entity container
* @property {Array<sap.ui.model.odata.ODataMetaModel.EntitySet>} [entitySet]
* The entity sets in the entity container
* @property {Array<sap.ui.model.odata.ODataMetaModel.FunctionImport>} [functionImport]
* The function imports in the entity container
* @property {"false"|"true"} [isDefaultEntityContainer]
* Whether this is the default entity container
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.StringExpression
*
* An object representing a string value.
*
* @property {string} String
* The string value
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.BoolExpression
*
* An object representing a Boolean value.
*
* @property {"true"|"false"} Bool
* The Boolean value
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.PropertyPathExpression
*
* An object representing a <code>PropertyPathExpression</code> with its corresponding value.
*
* @property {string} PropertyPath
* The value of the <code>PropertyPathExpression</code>
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.SimpleIdentifier
*
* An object representing an
* {@link https://github.com/oasis-tcs/odata-vocabularies/blob/main/vocabularies/Org.OData.Core.V1.md#SimpleIdentifier
* OData SimpleIdentifier}
* @property {string} String
* The simple identifier string
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.ValueListProperty
*
* An object representing a <code>ValueListProperty</code> of a
* {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListParameter ValueListParameter}.
*
* @property {string} String
* The value of the <code>ValueListProperty</code>
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.ValueListParameterIn
*
* An object representing the value list parameter type
* {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListParameterIn
* ValueListParameterIn}.
*
* @property {sap.ui.model.odata.ODataMetaModel.PropertyPathExpression} LocalDataProperty
* An object containing the property path that is used to filter the value list with an <code>eq</code> comparison
* @property {sap.ui.model.odata.ODataMetaModel.ValueListProperty} ValueListProperty
* An object containing the path to the property in the value list
* @property {"com.sap.vocabularies.Common.v1.ValueListParameterIn"} RecordType
* Fully qualified name of the value list parameter type <code>ValueListParameterIn</code>
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.ValueListParameterConstant
*
* An object representing the value list parameter type
* {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListParameterConstant
* ValueListParameterConstant}.
*
* @property {sap.ui.model.odata.ODataMetaModel.ValueListProperty} ValueListProperty
* An object containing the path to the property in the value list
* @property {string} Constant
* A string representing a constant value that is used to filter the value list with an <code>eq</code>
* comparison, using the same representation as property default values
* @property {"com.sap.vocabularies.Common.v1.ValueListParameterConstant"} RecordType
* Fully qualified name of the value list parameter type <code>ValueListParameterConstant</code>
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.ValueListParameterInOut
*
* An object representing the value list parameter type
* {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListParameterInOut
* ValueListParameterInOut}.
*
* @property {sap.ui.model.odata.ODataMetaModel.PropertyPathExpression} LocalDataProperty
* An object containing the property path that is used to filter the value list with an <code>startswith</code>
* comparison and filled from the picked value list item
* @property {sap.ui.model.odata.ODataMetaModel.ValueListProperty} ValueListProperty
* An object containing the path to the property in the value list
* @property {"com.sap.vocabularies.Common.v1.ValueListParameterInOut"} RecordType
* Fully qualified name of the value list parameter type <code>ValueListParameterInOut</code>
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.ValueListParameterOut
*
* An object representing the value list parameter type
* {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListParameterOut
* ValueListParameterOut}.
*
* @property {sap.ui.model.odata.ODataMetaModel.PropertyPathExpression} LocalDataProperty
* An object containing the path to the property that is filled from the response
* @property {sap.ui.model.odata.ODataMetaModel.ValueListProperty} ValueListProperty
* An object containing the path to the property in the value list
* @property {"com.sap.vocabularies.Common.v1.ValueListParameterOut"} RecordType
* Fully qualified name of the value list parameter type <code>ValueListParameterOut</code>
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.ValueListParameterDisplayOnly
*
* An object representing the value list parameter type
* {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListParameterDisplayOnly
* ValueListParameterDisplayOnly}.
*
* @property {sap.ui.model.odata.ODataMetaModel.ValueListProperty} ValueListProperty
* An object containing the path to the property in the value list
* @property {"com.sap.vocabularies.Common.v1.ValueListParameterDisplayOnly"} RecordType
* Fully qualified name of the value list parameter type <code>ValueListParameterDisplayOnly</code>
*
* @public
*/
/**
* The alias type for the OData
* {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListParameter ValueListParameter};
* this alias comprises all supported value list parameter types.
*
* @typedef {
* sap.ui.model.odata.ODataMetaModel.ValueListParameterIn |
* sap.ui.model.odata.ODataMetaModel.ValueListParameterConstant |
* sap.ui.model.odata.ODataMetaModel.ValueListParameterInOut |
* sap.ui.model.odata.ODataMetaModel.ValueListParameterOut |
* sap.ui.model.odata.ODataMetaModel.ValueListParameterDisplayOnly
* } sap.ui.model.odata.ODataMetaModel.ValueListParameter
*
* @public
*/
/**
* @typedef {object} sap.ui.model.odata.ODataMetaModel.ValueListType
*
* An object describing the {@link https://sap.github.io/odata-vocabularies/vocabularies/Common.html#ValueListType
* ValueListType}.
*
* @property {sap.ui.model.odata.ODataMetaModel.StringExpression} [Label]
* A <code>StringExpression</code> object representing the <code>Label</code> property
* @property {sap.ui.model.odata.ODataMetaModel.StringExpression} CollectionPath
* A <code>StringExpression</code> object representing the <code>CollectionPath</code> property
* @property {sap.ui.model.odata.ODataMetaModel.StringExpression} [CollectionRoot]
* A <code>StringExpression</code> object representing the <code>CollectionRoot</code> property
* @property {sap.ui.model.odata.ODataMetaModel.BoolExpression} DistinctValuesSupported
* A <code>BoolExpression</code> object representing the <code>DistinctValuesSupported</code> property
* @property {sap.ui.model.odata.ODataMetaModel.BoolExpression} SearchSupported
* A <code>BoolExpression</code> object representing the <code>SearchSupported</code> property
* @property {sap.ui.model.odata.ODataMetaModel.SimpleIdentifier} [PresentationVariantQualifier]
* The <code>PresentationVariantQualifier</code> property defines the identifier for an alternative representation
* of the value help, e.g. as a bar chart
* @property {sap.ui.model.odata.ODataMetaModel.SimpleIdentifier} [SelectionVariantQualifier]
* The <code>SelectionVariantQualifier</code> property contains a combination of parameters and filters to query
* the value help entity set
* @property {sap.ui.model.odata.ODataMetaModel.ValueListParameter[]} Parameters
* An array of parameters used to construct the value list request and consume the response properties
*
* @public
*/
var // maps the metadata URL with query parameters concatenated with the code list collection
// path (e.g. /foo/bar/$metadata#SAP__Currencies) to a SyncPromise resolving with the code
// list customizing as needed by the OData type
mCodeListUrl2Promise = new Map(),
sODataMetaModel = "sap.ui.model.odata.ODataMetaModel",
// path to a type's property e.g. ("/dataServices/schema/<i>/entityType/<j>/property/<k>")
rPropertyPath =
/^((\/dataServices\/schema\/\d+)\/(?:complexType|entityType)\/\d+)\/property\/\d+$/;
// path to a function import's parameter e.g.:
// '/dataServices/schema/0/entityContainer/0/functionImport/12/parameter/0'
const rFunctionImportParameterPath =
/^(((\/dataServices\/schema\/\d+)\/entityContainer\/\d+)\/functionImport\/\d+)\/parameter\/\d+$/;
/**
* @class List binding implementation for the OData meta model which supports filtering on
* the virtual property "@sapui.name" (which refers back to the name of the object in
* question).
*
* Example:
* <pre>
* <template:repeat list="{path:'entityType>', filters: {path: '@sapui.name', operator: 'StartsWith', value1: 'com.sap.vocabularies.UI.v1.FieldGroup'}}" var="fieldGroup">
* </pre>
*
* @extends sap.ui.model.json.JSONListBinding
* @private
*/
var ODataMetaListBinding = JSONListBinding.extend("sap.ui.model.odata.ODataMetaListBinding"),
Resolver = ManagedObject.extend("sap.ui.model.odata._resolver", {
metadata : {
properties : {
any : "any"
}
}
});
ODataMetaListBinding.prototype.applyFilter = function () {
var that = this,
oCombinedFilter = FilterProcessor.combineFilters(this.aFilters, this.aApplicationFilters);
this.aIndices = FilterProcessor.apply(this.aIndices, oCombinedFilter, function (vRef, sPath) {
return sPath === "@sapui.name"
? vRef
: that.oModel.getProperty(sPath, that.oList[vRef]);
}, this.mNormalizeCache);
this.iLength = this.aIndices.length;
};
/**
* DO NOT CALL this private constructor for a new <code>ODataMetaModel</code>,
* but rather use {@link sap.ui.model.odata.v2.ODataModel#getMetaModel getMetaModel} instead!
*
* @param {sap.ui.model.odata.ODataMetadata} oMetadata
* the OData model's metadata object
* @param {sap.ui.model.odata.ODataAnnotations} [oAnnotations]
* the OData model's annotations object
* @param {sap.ui.model.odata.v2.ODataModel} oDataModel
* the data model instance
*
* @class Implementation of an OData meta model which offers a unified access to both OData V2
* metadata and V4 annotations. It uses the existing {@link sap.ui.model.odata.ODataMetadata}
* as a foundation and merges V4 annotations from the existing
* {@link sap.ui.model.odata.ODataAnnotations} directly into the corresponding model element.
*
* This model is not prepared to be inherited from.
*
* Also, annotations from the "http://www.sap.com/Protocols/SAPData" namespace are lifted up
* from the <code>extensions</code> array and transformed from objects into simple properties
* with an "sap:" prefix for their name. Note that this happens in addition, thus the
* following example shows both representations. This way, such annotations can be addressed
* via a simple relative path instead of searching an array.
* <pre>
{
"name" : "BusinessPartnerID",
"extensions" : [{
"name" : "label",
"value" : "Bus. Part. ID",
"namespace" : "http://www.sap.com/Protocols/SAPData"
}],
"sap:label" : "Bus. Part. ID"
}
* </pre>
*
* As of 1.29.0, the corresponding vocabulary-based annotations for the following
* "<a href="http://www.sap.com/Protocols/SAPData">SAP Annotations for OData Version 2.0</a>"
* are added, if they are not yet defined in the V4 annotations:
* <ul>
* <li><code>label</code>;</li>
* <li><code>schema-version</code> (since 1.53.0) on schemas;</li>
* <li><code>creatable</code>, <code>deletable</code>, <code>deletable-path</code>,
* <code>pageable</code>, <code>requires-filter</code>, <code>searchable</code>,
* <code>topable</code>, <code>updatable</code> and <code>updatable-path</code> on entity sets;
* </li>
* <li><code>creatable</code> (since 1.41.0), <code>creatable-path</code> (since 1.41.0) and
* <code>filterable</code> (since 1.39.0) on navigation properties;</li>
* <li><code>aggregation-role</code> ("dimension" and "measure", both since 1.45.0),
* <code>creatable</code>, <code>display-format</code> ("UpperCase" and "NonNegative"),
* <code>field-control</code>, <code>filterable</code>, <code>filter-restriction</code>,
* <code>heading</code>, <code>precision</code>, <code>quickinfo</code>,
* <code>required-in-filter</code>, <code>sortable</code>, <code>text</code>, <code>unit</code>,
* <code>updatable</code> and <code>visible</code> on properties;</li>
* <li><code>semantics</code>; the following values are supported:
* <ul>
* <li>"bday", "city", "country", "email" (including support for types, for example
* "email;type=home,pref"), "familyname", "givenname", "honorific", "middlename", "name",
* "nickname", "note", "org", "org-unit", "org-role", "photo", "pobox", "region", "street",
* "suffix", "tel" (including support for types, for example "tel;type=cell,pref"), "title" and
* "zip" (mapped to V4 annotation <code>com.sap.vocabularies.Communication.v1.Contact</code>);
* </li>
* <li>"class", "dtend", "dtstart", "duration", "fbtype", "location", "status", "transp" and
* "wholeday" (mapped to V4 annotation
* <code>com.sap.vocabularies.Communication.v1.Event</code>);</li>
* <li>"body", "from", "received", "sender" and "subject" (mapped to V4 annotation
* <code>com.sap.vocabularies.Communication.v1.Message</code>);</li>
* <li>"completed", "due", "percent-complete" and "priority" (mapped to V4 annotation
* <code>com.sap.vocabularies.Communication.v1.Task</code>);</li>
* <li>"fiscalyear", "fiscalyearperiod" (mapped to the corresponding V4 annotation
* <code>com.sap.vocabularies.Common.v1.IsFiscal(Year|YearPeriod)</code>);</li>
* <li>"year", "yearmonth", "yearmonthday", "yearquarter", "yearweek" (mapped to the
* corresponding V4 annotation
* <code>com.sap.vocabularies.Common.v1.IsCalendar(Year|YearMonth|Date|YearQuarter|YearWeek)</code>);
* </li>
* <li>"url" (mapped to V4 annotation <code>Org.OData.Core.V1.IsURL"</code>).</li>
* </ul>
* </ul>
* For example:
* <pre>
{
"name" : "BusinessPartnerID",
...
"sap:label" : "Bus. Part. ID",
"com.sap.vocabularies.Common.v1.Label" : {
"String" : "Bus. Part. ID"
}
}
* </pre>
* <b>Note:</b> Annotation terms are not merged, but replaced as a whole ("PUT" semantics). That
* means, if you have, for example, an OData V2 annotation <code>sap:sortable=false</code> at a
* property <code>PropA</code>, the corresponding OData V4 annotation is added to each entity
* set to which this property belongs:
* <pre>
"Org.OData.Capabilities.V1.SortRestrictions": {
"NonSortableProperties" : [
{"PropertyPath" : "BusinessPartnerID"}
]
}
* </pre>
* If the same term <code>"Org.OData.Capabilities.V1.SortRestrictions"</code> targeting one of
* these entity sets is also contained in an annotation file, the complete OData V4 annotation
* converted from the OData V2 annotation is replaced by the one contained in the annotation
* file for the specified target. Converted annotations never use a qualifier and are only
* overwritten by the same annotation term without a qualifier.
*
* This model is read-only and thus only supports
* {@link sap.ui.model.BindingMode.OneTime OneTime} binding mode. No events
* ({@link sap.ui.model.Model#event:parseError parseError},
* {@link sap.ui.model.Model#event:requestCompleted requestCompleted},
* {@link sap.ui.model.Model#event:requestFailed requestFailed},
* {@link sap.ui.model.Model#event:requestSent requestSent}) are fired!
*
* Within the meta model, the objects are arranged in arrays.
* <code>/dataServices/schema</code>, for example, is an array of schemas where each schema has
* an <code>entityType</code> property with an array of entity types, and so on. So,
* <code>/dataServices/schema/0/entityType/16</code> can be the path to the entity type with
* name "Order" in the schema with namespace "MySchema". However, these paths are not stable:
* If an entity type with lower index is removed from the schema, the path to
* <code>Order</code> changes to <code>/dataServices/schema/0/entityType/15</code>.
*
* To avoid problems with changing indexes, {@link sap.ui.model.Model#getObject getObject} and
* {@link sap.ui.model.Model#getProperty getProperty} support XPath-like queries for the
* indexes (since 1.29.1). Each index can be replaced by a query in square brackets. You can,
* for example, address the schema using the path
* <code>/dataServices/schema/[${namespace}==='MySchema']</code> or the entity using
* <code>/dataServices/schema/[${namespace}==='MySchema']/entityType/[${name}==='Order']</code>.
*
* The syntax inside the square brackets is the same as in expression binding. The query is
* executed for each object in the array until the result is true (truthy) for the first time.
* This object is then chosen.
*
* <b>BEWARE:</b> Access to this OData meta model will fail before the promise returned by
* {@link #loaded loaded} has been resolved!
*
* @author SAP SE
* @version 1.147.0
* @alias sap.ui.model.odata.ODataMetaModel
* @extends sap.ui.model.MetaModel
* @public
* @since 1.27.0
*/
var ODataMetaModel = MetaModel.extend("sap.ui.model.odata.ODataMetaModel", {
constructor : function (oMetadata, oAnnotations, oDataModel) {
var oAnnotationsLoadedPromise = oDataModel.annotationsLoaded(),
that = this;
function load() {
var oData;
if (that.bDestroyed) {
throw new Error("Meta model already destroyed");
}
oData = JSON.parse(JSON.stringify(oMetadata.getServiceMetadata()));
that.oModel = new JSONModel(oData);
that.oModel.setDefaultBindingMode(that.sDefaultBindingMode);
/** @deprecated As of version 1.48.0, reason sap.ui.model.odata.ODataModel */
if (!oDataModel._requestAnnotationChanges) {
Utils.merge(oAnnotations ? oAnnotations.getData() : {}, oData, that,
oDataModel.bIgnoreAnnotationsFromMetadata);
return undefined;
}
return oDataModel._requestAnnotationChanges().then((aAnnotationChanges) => {
Utils.merge(oAnnotations ? oAnnotations.getData() : {}, oData, that,
oDataModel.bIgnoreAnnotationsFromMetadata);
that.aAnnotationChanges = _Helper.deepClone(aAnnotationChanges, 20);
that._applyAnnotationChanges();
});
}
MetaModel.apply(this); // no arguments to pass!
this.oModel = null; // not yet available!
// map path of property to promise for loading its value list
this.mContext2Promise = {};
this.sDefaultBindingMode = BindingMode.OneTime;
this.oLoadedPromise = oAnnotationsLoadedPromise
? oAnnotationsLoadedPromise.then(load)
: new Promise(function (fnResolve) {
load();
fnResolve();
}); // call load() synchronously!
this.oLoadedPromiseSync = SyncPromise.resolve(this.oLoadedPromise);
this.oMetadata = oMetadata;
this.oDataModel = oDataModel;
this.mQueryCache = {};
// map qualified property name to internal "promise interface" for request bundling
this.mQName2PendingRequest = {};
this.oResolver = undefined;
this.mSupportedBindingModes = {"OneTime" : true};
}
});
/**
* Apply the annotation changes for this model.
*
* @private
* @see sap.ui.model.odata.v2.ODataModel#setAnnotationChangePromise
*/
ODataMetaModel.prototype._applyAnnotationChanges = function () {
if (!this.aAnnotationChanges) {
return;
}
const aNotAppliedChanges = [];
this.aAnnotationChanges.forEach((oAnnotationChange) => {
const sResolvedPath = this._getObject(oAnnotationChange.path, undefined, /*bAsPath*/true);
// value help metadata maybe loaded and merged later; if the path cannot be resolved yet ignore the
// annotation change and process it later
if (sResolvedPath) {
this.oModel.setProperty(sResolvedPath, oAnnotationChange.value);
} else {
aNotAppliedChanges.push(oAnnotationChange);
}
});
this.aAnnotationChanges = aNotAppliedChanges.length ? aNotAppliedChanges : undefined;
};
/**
* Returns the value of the object or property inside this model's data which can be reached,
* starting at the given context, by following the given path.
*
* @param {string} sPath
* a relative or absolute path
* @param {object|sap.ui.model.Context} [oContext]
* the context to be used as a starting point in case of a relative path
* @param {boolean} [bAsPath]
* Whether to return the JSON model path instead of the value for the given path and context
* @returns {any}
* the value of the object or property or <code>null</code> in case a relative path without
* a context is given
* @private
*/
ODataMetaModel.prototype._getObject = function (sPath, oContext, bAsPath) {
var oBaseNode = oContext,
oBinding,
sCacheKey,
i,
iEnd,
oNode,
vPart,
sProcessedPath,
sResolvedPath = sPath || "",
oResult;
if (!oContext || oContext instanceof Context) {
sResolvedPath = this.resolve(sPath || "", oContext);
if (!sResolvedPath) {
Log.error("Invalid relative path w/o context", sPath,
sODataMetaModel);
return null;
}
}
if (sResolvedPath.charAt(0) === "/") {
oBaseNode = this.oModel._getObject("/");
sResolvedPath = sResolvedPath.slice(1);
}
sProcessedPath = "/";
oNode = oBaseNode;
while (sResolvedPath) {
vPart = undefined;
oBinding = undefined;
if (sResolvedPath.charAt(0) === '[') {
try {
oResult = BindingParser.parseExpression(sResolvedPath, 1);
iEnd = oResult.at;
if (sResolvedPath.length === iEnd + 1
|| sResolvedPath.charAt(iEnd + 1) === '/') {
oBinding = oResult.result;
vPart = sResolvedPath.slice(0, iEnd + 1);
sResolvedPath = sResolvedPath.slice(iEnd + 2);
}
} catch (ex) {
if (!(ex instanceof SyntaxError)) {
throw ex;
}
// do nothing, syntax error is logged already
}
}
if (vPart === undefined) {
// No query or unsuccessful query, simply take the next part until '/'
iEnd = sResolvedPath.indexOf("/");
if (iEnd < 0) {
vPart = sResolvedPath;
sResolvedPath = "";
} else {
vPart = sResolvedPath.slice(0, iEnd);
sResolvedPath = sResolvedPath.slice(iEnd + 1);
}
}
if (!oNode) {
if (bAsPath) {
return null;
}
if (Log.isLoggable(Log.Level.WARNING, sODataMetaModel)) {
Log.warning("Invalid part: " + vPart,
"path: " + sPath + ", context: "
+ (oContext instanceof Context ? oContext.getPath() : oContext),
sODataMetaModel);
}
break;
}
if (oBinding) {
if (oBaseNode === oContext) {
Log.error(
"A query is not allowed when an object context has been given", sPath,
sODataMetaModel);
return null;
}
if (!Array.isArray(oNode)) {
Log.error(
"Invalid query: '" + sProcessedPath + "' does not point to an array",
sPath, sODataMetaModel);
return null;
}
sCacheKey = sProcessedPath + vPart;
vPart = this.mQueryCache[sCacheKey];
if (vPart === undefined) {
// Set the resolver on the internal JSON model, so that resolving does not use
// this._getObject itself.
this.oResolver = this.oResolver || new Resolver({models: this.oModel});
for (i = 0; i < oNode.length; i += 1) {
this.oResolver.bindObject(sProcessedPath + i);
this.oResolver.bindProperty("any", oBinding);
try {
if (this.oResolver.getAny()) {
this.mQueryCache[sCacheKey] = vPart = i;
break;
}
} finally {
this.oResolver.unbindProperty("any");
this.oResolver.unbindObject();
}
}
}
}
oNode = oNode[vPart];
sProcessedPath = sProcessedPath + vPart + "/";
}
return bAsPath ? sProcessedPath : oNode;
};
/**
* Gets an object containing a shared {@link sap.ui.model.odata.v2.ODataModel} instance, which
* is used to load code lists for currencies and units, and
* <code>bFirstCodeListRequested</code>, which is initially <code>false</code> and is used to
* destroy the shared model at the right time. The <code>ODataMetaModel</code> is able to handle
* two code lists, one for currencies and one for units. As soon as the first code list is
* processed, whether successfully or not, <code>bFirstCodeListRequested</code> is set to
* <code>true</code>. Once a second code list has been processed, the shared model is not needed
* any more and is destroyed. The shared model is also destroyed when this instance of the
* <code>ODataMetaModel</code> gets destroyed.
*
* @returns {object}
* An object containing an OData model and <code>bFirstCodeListRequested</code>
*
* @private
*/
ODataMetaModel.prototype._getOrCreateSharedModelCache = function () {
var oDataModel = this.oDataModel;
if (!this.oSharedModelCache) {
this.oSharedModelCache = {
bFirstCodeListRequested : false,
oModel : new oDataModel.constructor(oDataModel.getCodeListModelParameters())
};
}
return this.oSharedModelCache;
};
/**
* Merges metadata retrieved via <code>this.oDataModel.addAnnotationUrl</code>.
*
* @param {object} oResponse response from addAnnotationUrl.
*
* @private
*/
ODataMetaModel.prototype._mergeMetadata = function (oResponse) {
var oEntityContainer = this.getODataEntityContainer(),
mChildAnnotations = Utils.getChildAnnotations(oResponse.annotations,
oEntityContainer.namespace + "." + oEntityContainer.name, true),
iFirstNewEntitySet = oEntityContainer.entitySet.length,
aSchemas = this.oModel.getObject("/dataServices/schema"),
that = this;
// merge metadata for entity sets/types
oResponse.entitySets.forEach(function (oEntitySet) {
var oEntityType,
oSchema,
sTypeName = oEntitySet.entityType,
// Note: namespaces may contain dots themselves!
sNamespace = sTypeName.slice(0, sTypeName.lastIndexOf("."));
if (!that.getODataEntitySet(oEntitySet.name)) {
oEntityContainer.entitySet.push(JSON.parse(JSON.stringify(oEntitySet)));
if (!that.getODataEntityType(sTypeName)) {
oEntityType = that.oMetadata._getEntityTypeByName(sTypeName);
oSchema = Utils.getSchema(aSchemas, sNamespace);
oSchema.entityType.push(JSON.parse(JSON.stringify(oEntityType)));
// visit all entity types before visiting the entity sets to ensure that V2
// annotations are already lifted up and can be used for calculating entity
// set annotations which are based on V2 annotations on entity properties
Utils.visitParents(oSchema, oResponse.annotations,
"entityType", Utils.visitEntityType,
oSchema.entityType.length - 1);
}
}
});
Utils.visitChildren(oEntityContainer.entitySet, mChildAnnotations, "EntitySet", aSchemas,
/*fnCallback*/null, iFirstNewEntitySet);
Utils.addUnitAnnotations(aSchemas, this);
};
/**
* Send all currently pending value list requests as a single bundle.
*
* @private
*/
ODataMetaModel.prototype._sendBundledRequest = function () {
var mQName2PendingRequest = this.mQName2PendingRequest, // remember current state
aQualifiedPropertyNames = Object.keys(mQName2PendingRequest),
that = this;
if (!aQualifiedPropertyNames.length) {
return; // nothing to do
}
this.mQName2PendingRequest = {}; // clear pending requests for next bundle
// normalize URL to be browser cache friendly with value list request
aQualifiedPropertyNames = aQualifiedPropertyNames.sort();
aQualifiedPropertyNames.forEach(function (sQualifiedPropertyName, i) {
aQualifiedPropertyNames[i] = encodeURIComponent(sQualifiedPropertyName);
});
this.oDataModel
.addAnnotationUrl("$metadata?sap-value-list=" + aQualifiedPropertyNames.join(","))
.then(
function (oResponse) {
var sQualifiedPropertyName;
that._mergeMetadata(oResponse);
for (sQualifiedPropertyName in mQName2PendingRequest) {
try {
mQName2PendingRequest[sQualifiedPropertyName].resolve(oResponse);
} catch (oError) {
mQName2PendingRequest[sQualifiedPropertyName].reject(oError);
}
}
that._applyAnnotationChanges(); // new metadata is merged, apply annotation changes again
},
function (oError) {
var sQualifiedPropertyName;
for (sQualifiedPropertyName in mQName2PendingRequest) {
mQName2PendingRequest[sQualifiedPropertyName].reject(oError);
}
}
);
};
ODataMetaModel.prototype.bindContext = function (sPath, oContext, mParameters) {
return new ClientContextBinding(this, sPath, oContext, mParameters);
};
ODataMetaModel.prototype.bindList = function (sPath, oContext, aSorters, aFilters,
mParameters) {
return new ODataMetaListBinding(this, sPath, oContext, aSorters, aFilters, mParameters);
};
ODataMetaModel.prototype.bindProperty = function (sPath, oContext, mParameters) {
return new JSONPropertyBinding(this, sPath, oContext, mParameters);
};
ODataMetaModel.prototype.bindTree = function (sPath, oContext, aFilters, mParameters) {
return new JSONTreeBinding(this, sPath, oContext, aFilters, mParameters);
};
ODataMetaModel.prototype.destroy = function () {
MetaModel.prototype.destroy.apply(this, arguments);
if (this.oSharedModelCache) {
this.oSharedModelCache.oModel.destroy();
delete this.oSharedModelCache;
}
return this.oModel && this.oModel.destroy.apply(this.oModel, arguments);
};
/**
* Removes all parameters from the given model's <code>aUrlParams</code> except "sap-language" and "sap-client".
* Other parameters are not relevant for fetching the code lists.
*
* @param {sap.ui.model.odata.v2.ODataModel} oModel The code list OData model
* @param {string} sMetaDataUrl The metadata URL
*
* @private
*/
ODataMetaModel.cleanupCodeListModelURLParameters = function (oModel, sMetaDataUrl) {
let bHasLanguage = false;
// aUrlParams may have single or multiple with "&" joined parameters per entry, e.g.
// ["sap-language=EN&sap-client=123", "sap-statistics=true", ...]
oModel.aUrlParams = oModel.aUrlParams.join("&").split("&").filter((sParameter) => {
if (sParameter.startsWith("sap-language=")) {
bHasLanguage = true;
return true;
}
return sParameter.startsWith("sap-client=");
});
if (!bHasLanguage) {
const oURLSearchParams = new URL(sMetaDataUrl, "https://localhost").searchParams;
// ensure that "sap-language" is always set for caching purposes (browser cache);
// if the model has been created via sap.ui.core.Component the metadata URL always contains
// "sap-language" parameter; if the model is created otherwise "sap-language" parameter might
// be missing; no need to add sap-client, as is automatically added if given via
// configuration or parameters
oModel.aUrlParams.push("sap-language="
+ (oURLSearchParams.get("sap-language") ?? Localization.getSAPLogonLanguage()));
}
};
/**
* Requests the customizing based on the code list reference given in the entity container's
* <code>com.sap.vocabularies.CodeList.v1.*</code> annotation for the term specified in the
* <code>sTerm</code> parameter. Once a code list has been requested, the promise is cached.
*
* @param {string} sTerm
* The unqualified name of the term from the <code>com.sap.vocabularies.CodeList.v1</code>
* vocabulary used to annotate the entity container, e.g. "CurrencyCodes" or "UnitsOfMeasure"
* @returns {sap.ui.base.SyncPromise}
* A promise resolving with the customizing, which is a map from the code key to an object
* with the following properties:
* <ul>
* <li>StandardCode: The language-independent standard code (e.g. ISO) for the code as
* referred to via the <code>com.sap.vocabularies.CodeList.v1.StandardCode</code>
* annotation on the code's key, if present
* <li>Text: The language-dependent text for the code as referred to via the
* <code>com.sap.vocabularies.Common.v1.Text</code> annotation on the code's key
* <li>UnitSpecificScale: The decimals for the code as referred to via the
* <code>com.sap.vocabularies.Common.v1.UnitSpecificScale</code> annotation on the code's
* key; entries where this would be <code>null</code> are ignored, and an error is logged
* </ul>
* It resolves with <code>null</code> if no given
* <code>com.sap.vocabularies.CodeList.v1.*</code> annotation is found.
* It is rejected if the code list URL is not "./$metadata", there is not exactly one code
* key, or if the customizing cannot be loaded.
*
* @private
* @see #re