UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

1,298 lines (1,218 loc) 82.8 kB
/*! * 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> * &lt;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