@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
828 lines (794 loc) • 36.9 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
//Provides class sap.ui.model.odata.v4.AnnotationHelper
sap.ui.define([
"./_AnnotationHelperExpression"
], function (Expression) {
"use strict";
var rBadChars = /[\\{}:]/, // @see sap.ui.base.BindingParser: rObject, rBindingChars
rCount = /\/\$count$/,
rPaths = /\$(?:(?:Annotation)|(?:(?:Navigation)?Property))?Path/,
rSplitPathSegment = /^(.+?\/(\$(?:Annotation)?Path))(\/?)(.*)$/,
rUnsupportedPathSegments = /\$(?:Navigation)?PropertyPath/,
/**
* @classdesc
* A collection of methods which help to consume <a href=
* "https://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part3-csdl.html#_Vocabulary_and_Annotation"
* >OData V4 annotations</a> in XML template views. Every context argument must belong to an
* {@link sap.ui.model.odata.v4.ODataMetaModel} instance.
*
* @alias sap.ui.model.odata.v4.AnnotationHelper
* @namespace
* @public
* @since 1.43.0
*/
AnnotationHelper = {
/**
* A function that helps to interpret OData V4 annotations.
*
* Unsupported or incorrect values are turned into a string nevertheless, but are
* indicated as such. In such a case, an error describing the problem is logged to the
* console. Proper escaping is used to make sure that data binding syntax is not
* corrupted.
*
* Example:
* <pre>
* <Text text="{meta>Value/@@sap.ui.model.odata.v4.AnnotationHelper.format}" />
* </pre>
*
* <h3>Supported Expressions</h3>
* <ul>
* <li> "14.4 Constant Expressions" for "edm:Bool", "edm:Date", "edm:DateTimeOffset",
* "edm:Decimal", "edm:Float", "edm:Guid", "edm:Int", "edm:TimeOfDay".
* <li> constant "14.4.11 Expression edm:String": This is turned into a fixed text
* (for example <code>"Width"</code>). String constants that contain a simple
* binding <code>"{@i18n>...}"</code> to the hard-coded model name "@i18n" with
* arbitrary path are not turned into a fixed text, but kept as a data binding
* expression; this allows local annotation files to refer to a resource bundle for
* internationalization.
* <li> dynamic "14.5.1 Comparison and Logical Operators": These are turned into
* expression bindings to perform the operations at runtime.
* <li> dynamic "14.5.3 Expression edm:Apply":
* <ul>
* <li> "14.5.3.1.1 Function odata.concat": This is turned into a data binding
* expression relative to an entity.
* <li> "14.5.3.1.2 Function odata.fillUriTemplate": This is turned into an
* expression binding to fill the template at runtime.
* <li> "14.5.3.1.3 Function odata.uriEncode": This is turned into an expression
* binding to encode the parameter at runtime.
* <li> Apply functions may be nested arbitrarily.
* </ul>
* <li> dynamic "14.5.5 Expression edm:Collection": This is turned into an expression
* binding to be evaluated at runtime. Elements can be conditionally added to the
* collection when using dynamic "14.5.6 Expression edm:If" as a direct child.
* <li> dynamic "14.5.6 Expression edm:If": This is turned into an expression
* binding to be evaluated at runtime. The expression is a conditional expression
* like <code>"{=condition ? expression1 : expression2}"</code>.
* <li> dynamic "14.5.10 Expression edm:Null": This is turned into a
* <code>null</code> value. It is ignored in <code>odata.concat</code>.
* <li> dynamic "14.5.12 Expression edm:Path" and "14.5.13 Expression
* edm:PropertyPath": These are turned into data bindings relative to an entity,
* including type information and constraints as available from metadata, for
* example
* <code>"{path : 'Name', type : 'sap.ui.model.odata.type.String', constraints :
* {'maxLength' : 255}, formatOptions : {'parseKeepsEmptyString' : true}}"</code>.
* Depending on the used type, some additional constraints and format options of
* this type are set:
* <ul>
* <li> Edm.DateTime: The "displayFormat" constraint is set to the value of the
* "sap:display-format" annotation of the referenced property.
* <li> Edm.Decimal: The "precision" and "scale" constraints are set to the values
* of the corresponding attributes of the referenced property. The "minimum",
* "maximum", "minimumExclusive", and "maximumExclusive" constraints are set to
* the values of the corresponding "Org.OData.Validation.V1" annotation of the
* referenced property; note that in this case only constant expressions are
* supported to determine the annotation value.
* <li> Edm.String: The "maxLength" constraint is set to the value of the
* corresponding attribute of the referenced property, and the "isDigitSequence"
* constraint is set to the value of the
* "com.sap.vocabularies.Common.v1.IsDigitSequence" annotation of the referenced
* property; note that in this case only constant expressions are supported to
* determine the annotation value. The "parseKeepsEmptyString" format option is
* set.
* </ul>
* Since 1.78.0, both "edm:Path" and "edm:PropertyPath" are also supported if
* <code>vRawValue</code> is the path itself, and not the object wrapping it.
* </ul>
*
* <h3>$AnnotationPath and $Path</h3>
* If <code>oDetails.context.getPath()</code> contains a single "$AnnotationPath" or
* "$Path" segment, the value corresponding to that segment is considered as a data
* binding path prefix whenever a dynamic "14.5.12 Expression edm:Path" or
* "14.5.13 Expression edm:PropertyPath" is turned into a data binding. Use
* {@link sap.ui.model.odata.v4.AnnotationHelper.resolve$Path} to avoid these prefixes
* in cases where they are not applicable.
*
* <h4>$AnnotationPath</h4>
* Example for "$AnnotationPath" in the context's path:
* <pre>
* <Annotations Target="com.sap.gateway.default.iwbep.tea_busi.v0001.EQUIPMENT">
* <Annotation Term="com.sap.vocabularies.UI.v1.Facets">
* <Collection>
* <Record Type="com.sap.vocabularies.UI.v1.ReferenceFacet">
* <PropertyValue Property="Target" AnnotationPath="EQUIPMENT_2_PRODUCT/@com.sap.vocabularies.Common.v1.QuickInfo" />
* </Record>
* </Collection>
* </Annotation>
* </Annotations>
* <Annotations Target="com.sap.gateway.default.iwbep.tea_busi_product.v0001.Product">
* <Annotation Term="com.sap.vocabularies.Common.v1.QuickInfo" Path="Name" />
* </Annotations>
* </pre>
* <pre>
* <Text text="{meta>/Equipments/@com.sap.vocabularies.UI.v1.Facets/0/Target/$AnnotationPath/@@sap.ui.model.odata.v4.AnnotationHelper.format}" />
* </pre>
* <code>format</code> returns a binding with path "EQUIPMENT_2_PRODUCT/Name".
*
* <h4>$Path</h4>
* Example for "$Path" in the context's path:
* <pre>
* <Annotations Target="com.sap.gateway.default.iwbep.tea_busi.v0001.EQUIPMENT">
* <Annotation Term="com.sap.vocabularies.UI.v1.LineItem">
* <Collection>
* <Record Type="com.sap.vocabularies.UI.v1.DataField">
* <PropertyValue Property="Value" Path="EQUIPMENT_2_PRODUCT/Name" />
* </Record>
* </Collection>
* </Annotation>
* </Annotations>
* <Annotations Target="com.sap.gateway.default.iwbep.tea_busi_product.v0001.Product/Name">
* <Annotation Term="com.sap.vocabularies.Common.v1.QuickInfo" Path="PRODUCT_2_SUPPLIER/Supplier_Name" />
* </Annotations>
* </pre>
* <pre>
* <Text text="{meta>/Equipments/@com.sap.vocabularies.UI.v1.LineItem/0/Value/$Path@com.sap.vocabularies.Common.v1.QuickInfo@@sap.ui.model.odata.v4.AnnotationHelper.format}" />
* </pre>
* <code>format</code> returns a binding with path
* "EQUIPMENT_2_PRODUCT/PRODUCT_2_SUPPLIER/Supplier_Name".
*
* <h3>Annotations on an Operation or a Parameter</h3>
* Since 1.71.0, for annotations on an operation or a parameter, the binding parameter's
* name is stripped off any dynamic "14.5.12 Expression edm:Path" and
* "14.5.13 Expression edm:PropertyPath" where it might be used as a first segment.
* Since 1.76.0 this does not apply to annotations on a parameter.
* In the former case, we assume that the resulting data binding is
* relative to the parent context of the operation binding, that is, to the context
* representing the binding parameter itself.
* In the latter case, we assume that the resulting data binding is relative to the
* parameter context of the operation binding (see
* {@link sap.ui.model.odata.v4.ODataContextBinding#getParameterContext}).
*
* Example:
* <pre>
* <Action Name="ShipProduct" EntitySetPath="_it" IsBound="true" >
* <Parameter Name="_it" Type="name.space.Product" Nullable="false"/>
* <Parameter Name="City" Type="Edm.String"/>
* </Action>
* </pre>
* For the operation <code>ShipProduct</code> mentioned above, the following annotation
* targets an operation parameter and refers back to the binding parameter.
* <pre>
* <Annotations Target="name.space.ShipProduct(name.space.Product)/City">
* <Annotation Term="com.sap.vocabularies.Common.v1.Text" Path="_it/SupplierIdentifier"/>
* </Annotations>
* </pre>
*
* Using <code>AnnotationHelper.format</code> like
* <pre>
* <Text text="{meta>/Products/name.space.ShipProduct/$Parameter/City@com.sap.vocabularies.Common.v1.Text@@sap.ui.model.odata.v4.AnnotationHelper.format}" />
* </pre>
* results in
* <pre>
* <Text text="{path:'_it/SupplierIdentifier',type:'sap.ui.model.odata.type.Int32'}" />
* </pre>
* and the data binding evaluates to the <code>SupplierIdentifier</code> property of the
* entity the operation is called on.
*
* <h3>Operation Parameters</h3>
* Since 1.73.0, this function can be used on action or function parameters and results
* in a relative data binding, just like a "14.5.12 Expression edm:Path".
*
* Assume we have the following metadata for an unbound action "AcChangeTeamBudgetByID":
* <pre>
* <Action Name="AcChangeTeamBudgetByID">
* <Parameter Name="TeamID" Type="Edm.String" Nullable="false" MaxLength="10"/>
* <Parameter Name="Budget" Type="Edm.Decimal" Nullable="false" Precision="16" Scale="variable"/>
* </Action>
* </pre>
*
* Let <code>ChangeTeamBudgetByID</code> be the action import of this action. Using
* <code>AnnotationHelper.format</code> for the <code>TeamID</code> like
* <pre>
* <Text text="{meta>/ChangeTeamBudgetByID/TeamID@@sap.ui.model.odata.v4.AnnotationHelper.format}" />
* </pre>
* results in
* <pre>
* <Text text="{path:'TeamID',type:'sap.ui.model.odata.type.String',constraints:{'maxLength':10,'nullable':false},formatOptions:{'parseKeepsEmptyString':true}}" />
* </pre>
*
* <h3>Binding Parameters and Format Options</h3>
* Since 1.77.0, binding parameters and format options can be given. The usage
* <pre>
* <Input value="{meta>/ChangeTeamBudgetByID/Budget@@sap.ui.model.odata.v4.AnnotationHelper.format($($$noPatch : true$), $(groupingEnabled : false$))}" />
* </pre>
* results in a data binding with the given binding parameters and format options. Note
* how, for an object notation, curly brackets must be replaced by <code>$(</code> and
* <code>$)</code> respectively. Use <code>null</code>, not <code>undefined</code>, in
* case no binding parameters are needed.
*
* <h3>Structural Properties</h3>
* Since 1.78.0, this function can be used on a structural property and results in a
* relative data binding, just like a "14.5.12 Expression edm:Path". The usage
* <pre>
* <Input value="{meta>/Department/Name@@sap.ui.model.odata.v4.AnnotationHelper.format}"/>
* </pre>
* results in
* <pre>
* < Input value="{path:'Name',type:'sap.ui.model.odata.type.String',constraints:{'maxLength':40,'nullable':false},formatOptions:{'parseKeepsEmptyString':true}}"/>
* </pre>
*
* @param {any} vRawValue
* The raw value from the meta model
* @param {object} oDetails
* The details object
* @param {any[]} [oDetails.arguments]
* Optional arguments: first an optional map of binding parameters, then an optional
* map of format options; both will be added to each resulting data binding
* @param {sap.ui.model.Context} oDetails.context
* Points to the given raw value, that is
* <code>oDetails.context.getProperty("") === vRawValue</code>
* @param {object} [oDetails.overload]
* The single operation overload that was targeted by annotations on an operation or
* a parameter; needed to strip off the binding parameter's name from any dynamic
* "14.5.12 Expression edm:Path" and "14.5.13 Expression edm:PropertyPath" where it
* might be used as a first segment (since 1.71.0). This does not apply to annotations
* on a parameter (since 1.76.0).
* @returns {string|Promise}
* A data binding, or a fixed text, or a sequence thereof, or a <code>Promise</code>
* resolving with that string, for example if not all type information is already
* available
* @throws {Error}
* If <code>oDetails.context.getPath()</code> contains a "$PropertyPath" or a
* "$NavigationPropertyPath" segment, or if it contains more than one
* "$AnnotationPath" or "$Path" segment
*
* @public
* @see sap.ui.model.odata.v4.AnnotationHelper.resolve$Path
* @see sap.ui.model.odata.v4.AnnotationHelper.value
* @since 1.63.0
*/
format : function (vRawValue, oDetails) {
var aMatches,
oModel = oDetails.context.getModel(),
sPath = oDetails.context.getPath();
function getExpression(sPrefix) {
if (sPath.endsWith("/")) {
// cut off trailing slash, happens with computed annotations
sPath = sPath.slice(0, -1);
}
return Expression.getExpression({
asExpression : false,
complexBinding : true,
formatOptions : oDetails.arguments && oDetails.arguments[1],
ignoreAsPrefix : oDetails.overload && oDetails.overload.$IsBound
&& !sPath.includes("/$Parameter/")
? oDetails.overload.$Parameter[0].$Name + "/"
: "",
model : oModel,
parameters : oDetails.arguments && oDetails.arguments[0],
path : sPath,
prefix : sPrefix, // prefix for computing paths
value : vRawValue,
// ensure that type information is available in sub paths of the
// expression even if for that sub path no complex binding is needed,
// e.g. see sap.ui.model.odata.v4_AnnotationHelperExpression.operator
$$valueAsPromise : true
});
}
if (sPath.endsWith("/$Path")) {
sPath = sPath.slice(0, -6);
vRawValue = {$Path : vRawValue};
} else if (sPath.endsWith("/$PropertyPath")) {
sPath = sPath.slice(0, -14);
vRawValue = {$PropertyPath : vRawValue};
}
aMatches = rUnsupportedPathSegments.exec(sPath);
if (aMatches) {
throw new Error("Unsupported path segment " + aMatches[0] + " in " + sPath);
}
// aMatches[0] - sPath, e.g. "/Equipments/@UI.LineItem/4/Value/$Path@Common.Label"
// aMatches[1] - prefix of sPath including $Path or $AnnotationPath, e.g.
// "/Equipments/@UI.LineItem/4/Value/$Path"
// aMatches[2] - "$AnnotationPath" or "$Path"
// aMatches[3] - optional "/" after "$AnnotationPath" or "$Path"
// aMatches[4] - rest of sPath, e.g. "@Common.Label"
aMatches = rSplitPathSegment.exec(sPath);
if (aMatches && sPath.length > aMatches[1].length) {
if (rSplitPathSegment.test(aMatches[4])) {
throw new Error("Only one $Path or $AnnotationPath segment is supported: "
+ sPath);
}
return oModel.fetchObject(aMatches[1]).then(function (sPathValue) {
var bIsAnnotationPath = aMatches[2] === "$AnnotationPath",
sPrefix = bIsAnnotationPath
? sPathValue.split("@")[0]
: sPathValue,
i;
if (!bIsAnnotationPath && aMatches[3]) {
sPrefix += "/";
} else if (!sPrefix.endsWith("/")) {
i = sPrefix.lastIndexOf("/");
sPrefix = i < 0 ? "" : sPrefix.slice(0, i + 1);
}
return getExpression(sPrefix);
});
}
return getExpression("");
},
/**
* Returns a data binding according to the result of
* {@link sap.ui.model.odata.v4.AnnotationHelper.getNavigationPath}.
*
* @param {string} sPath
* The path value from the meta model, for example
* "ToSupplier/@com.sap.vocabularies.Communication.v1.Address" or
* "@com.sap.vocabularies.UI.v1.FieldGroup#Dimensions"
* @returns {string}
* A data binding according to the result of
* {@link sap.ui.model.odata.v4.AnnotationHelper.getNavigationPath}, for example
* "{ToSupplier}" or ""
* @throws {Error}
* If the result of {@link sap.ui.model.odata.v4.AnnotationHelper.getNavigationPath}
* contains segments which are not valid OData identifiers and violate the data
* binding syntax
*
* @public
* @since 1.43.0
*/
getNavigationBinding : function (sPath) {
sPath = AnnotationHelper.getNavigationPath(sPath);
if (rBadChars.test(sPath)) {
throw new Error("Invalid OData identifier: " + sPath);
}
return sPath ? "{" + sPath + "}" : sPath;
},
/**
* A function that helps to interpret OData V4 annotations. It knows about the syntax
* of the path value used by the following dynamic expressions:
* <ul>
* <li> "14.5.2 Expression edm:AnnotationPath"
* <li> "14.5.11 Expression edm:NavigationPropertyPath"
* <li> "14.5.12 Expression edm:Path"
* <li> "14.5.13 Expression edm:PropertyPath"
* </ul>
* It returns the path of structural and navigation properties from the given path
* value, but removes "$count", types casts, term casts, and annotations on navigation
* properties.
*
* @param {string} sPath
* The path value from the meta model, for example
* "ToSupplier/@com.sap.vocabularies.Communication.v1.Address" or
* "@com.sap.vocabularies.UI.v1.FieldGroup#Dimensions"
* @returns {string}
* The path of structural and navigation properties, for example "ToSupplier" or ""
*
* @public
* @since 1.43.0
*/
getNavigationPath : function (sPath) {
var iIndexOfAt;
if (!sPath || sPath[0] === "@") {
return "";
}
if (rCount.test(sPath)) {
return sPath.slice(0, -7);
}
iIndexOfAt = sPath.indexOf("@");
if (iIndexOfAt > -1) {
sPath = sPath.slice(0, iIndexOfAt);
}
if (sPath[sPath.length - 1] === "/") {
sPath = sPath.slice(0, -1);
}
if (sPath.includes(".")) {
sPath = sPath.split("/")
.filter(function (sSegment) { // remove type casts
return !sSegment.includes(".");
}).join("/");
}
return sPath;
},
/**
* Determines which type of value list exists for the given property.
*
* @param {any} vRawValue
* The raw value from the meta model; must be either a property or a path pointing to
* a property (relative to <code>oDetails.schemaChildName</code>)
* @param {object} oDetails
* The details object
* @param {boolean} [oDetails.$$valueAsPromise]
* Whether a <code>Promise</code> may be returned if the needed metadata is not yet
* loaded (since 1.57.0)
* @param {sap.ui.model.Context} oDetails.context
* Points to the given path, that is
* <code>oDetails.context.getProperty("") === vRawValue</code>
* @param {string} oDetails.schemaChildName
* The qualified name of the schema child where the computed annotation has been
* found, for example "name.space.EntityType"
* @returns {sap.ui.model.odata.v4.ValueListType|Promise}
* The type of the value list or a <code>Promise</code> resolving with the type of the
* value list or rejected, if the property cannot be found in the metadata
* @throws {Error}
* If the property cannot be found in the metadata, or if
* <code>$$valueAsPromise</code> is not set to <code>true</code> and the metadata is
* not yet loaded
*
* @public
* @since 1.47.0
*/
getValueListType : function (vRawValue, oDetails) {
var sPath = typeof vRawValue === "string"
? "/" + oDetails.schemaChildName + "/" + vRawValue
: oDetails.context.getPath();
return oDetails.$$valueAsPromise
? oDetails.context.getModel().fetchValueListType(sPath).unwrap()
: oDetails.context.getModel().getValueListType(sPath);
},
/**
* A function that helps to interpret OData V4 annotations. It knows about the syntax
* of the path value used by the following dynamic expressions:
* <ul>
* <li> "14.5.2 Expression edm:AnnotationPath"
* <li> "14.5.11 Expression edm:NavigationPropertyPath"
* <li> "14.5.12 Expression edm:Path"
* <li> "14.5.13 Expression edm:PropertyPath"
* </ul>
* It returns the information whether the given path ends with "$count" or with a
* multi-valued structural or navigation property. Term casts and annotations on
* navigation properties are ignored.
*
* Example:
* <pre>
* <template:if test="{facet>Target/$AnnotationPath@@sap.ui.model.odata.v4.AnnotationHelper.isMultiple}">
* </pre>
*
* @param {string} sPath
* The path value from the meta model, for example
* "ToSupplier/@com.sap.vocabularies.Communication.v1.Address" or
* "@com.sap.vocabularies.UI.v1.FieldGroup#Dimensions"
* @param {object} oDetails
* The details object
* @param {boolean} [oDetails.$$valueAsPromise]
* Whether a <code>Promise</code> may be returned if the needed metadata is not yet
* loaded (since 1.57.0)
* @param {sap.ui.model.Context} oDetails.context
* Points to the given path, that is
* <code>oDetails.context.getProperty("") === sPath</code>
* @param {string} oDetails.schemaChildName
* The qualified name of the schema child where the computed annotation has been
* found, for example "name.space.EntityType"
* @returns {boolean|Promise}
* <code>true</code> if the given path ends with "$count" or with a multi-valued
* structural or navigation property, <code>false</code> otherwise. If
* <code>oDetails.$$valueAsPromise</code> is <code>true</code> a <code>Promise</code>
* may be returned resolving with the boolean value.
*
* @public
* @since 1.43.0
*/
isMultiple : function (sPath, oDetails) {
var iIndexOfAt;
// Whether the given value is exactly <code>true</code>
function isTrue(vValue) {
return vValue === true;
}
if (!sPath || sPath[0] === "@") {
return false;
}
if (rCount.test(sPath)) {
return true;
}
iIndexOfAt = sPath.indexOf("@");
if (iIndexOfAt > -1) {
sPath = sPath.slice(0, iIndexOfAt);
}
if (sPath[sPath.length - 1] !== "/") {
sPath += "/";
}
sPath = "/" + oDetails.schemaChildName + "/" + sPath + "$isCollection";
return oDetails.$$valueAsPromise
? oDetails.context.getModel().fetchObject(sPath).then(isTrue).unwrap()
: oDetails.context.getObject(sPath) === true;
},
/**
* Returns the value for the label of a
* <code>com.sap.vocabularies.UI.v1.DataFieldAbstract</code> from the meta model. If no
* <code>Label</code> property is available, but the data field has a <code>Value</code>
* property with an <code>edm:Path</code> expression as value, the label will be taken
* from the <code>com.sap.vocabularies.Common.v1.Label</code> annotation of the path's
* target property.
*
* Example:
* <pre>
* <Label text="{meta>@@sap.ui.model.odata.v4.AnnotationHelper.label}" />
* </pre>
*
* @param {any} vRawValue
* The raw value from the meta model
* @param {object} oDetails
* The details object
* @param {boolean} [oDetails.$$valueAsPromise]
* Whether a <code>Promise</code> may be returned if the needed metadata is not yet
* loaded (since 1.57.0)
* @param {sap.ui.model.Context} oDetails.context
* Points to the given raw value, that is
* <code>oDetails.context.getProperty("") === vRawValue</code>
* @returns {string|Promise|undefined}
* A data binding or a fixed text or a sequence thereof or <code>undefined</code>. If
* <code>oDetails.$$valueAsPromise</code> is <code>true</code> a <code>Promise</code>
* may be returned resolving with the value for the label.
*
* @public
* @since 1.49.0
*/
label : function (vRawValue, oDetails) {
var oNewContext;
if (vRawValue.Label) {
return AnnotationHelper.value(vRawValue.Label, {
context : oDetails.context.getModel()
.createBindingContext("Label", oDetails.context)
});
}
if (vRawValue.Value && vRawValue.Value.$Path) {
oNewContext = oDetails.context.getModel().createBindingContext(
"Value/$Path@com.sap.vocabularies.Common.v1.Label", oDetails.context);
if (oDetails.$$valueAsPromise) {
return oNewContext.getModel().fetchObject("", oNewContext)
.then(function (oRawValue0) {
return AnnotationHelper.value(oRawValue0, {
context : oNewContext
});
}).unwrap();
}
return AnnotationHelper.value(oNewContext.getObject(""), {
context : oNewContext
});
}
},
/**
* Helper function for a <code>template:with</code> instruction that returns an
* equivalent to the given context's path, without "$AnnotationPath",
* "$NavigationPropertyPath", "$Path", and "$PropertyPath" segments.
*
* @param {sap.ui.model.Context} oContext
* A context which belongs to an {@link sap.ui.model.odata.v4.ODataMetaModel}
* @returns {string}
* An equivalent to the given context's path, without the mentioned segments
* @throws {Error}
* If one of the mentioned segments has a non-string value and thus the path cannot
* be resolved
*
* @public
* @see sap.ui.model.odata.v4.AnnotationHelper.format
* @since 1.63.0
*/
resolve$Path : function (oContext) {
var iEndOfPath, // after $*Path
iIndexOfAt, // first "@" before $*Path
iIndexOfPath, // begin of $*Path
iLastIndexOfSlash, // last "/" before iIndexOfAt
aMatches,
sPath = oContext.getPath(),
sPrefix,
vValue;
for (;;) {
aMatches = sPath.match(rPaths);
if (!aMatches) {
return sPath;
}
iIndexOfPath = aMatches.index;
iEndOfPath = iIndexOfPath + aMatches[0].length;
sPrefix = sPath.slice(0, iEndOfPath);
vValue = oContext.getModel().getObject(sPrefix);
if (typeof vValue !== "string") {
throw new Error("Cannot resolve " + sPrefix
+ " due to unexpected value " + vValue);
}
sPrefix = sPath.slice(0, iIndexOfPath);
iIndexOfAt = sPrefix.indexOf("@");
iLastIndexOfSlash = sPrefix.lastIndexOf("/", iIndexOfAt);
if (iLastIndexOfSlash === 0) { // do not cut off entity set
sPrefix = sPrefix.slice(0, iIndexOfAt);
if (iIndexOfAt > 1 && vValue) {
sPrefix += "/";
}
} else { // cut off property, but end with slash
sPrefix = sPrefix.slice(0, iLastIndexOfSlash + 1);
}
sPath = sPrefix + vValue + sPath.slice(iEndOfPath);
}
},
/**
* A function that helps to interpret OData V4 annotations.
*
* Unsupported or incorrect values are turned into a string nevertheless, but are
* indicated as such. In such a case, an error describing the problem is logged to the
* console.
*
* Example:
* <pre>
* <Text text="{meta>Value/@@sap.ui.model.odata.v4.AnnotationHelper.value}" />
* </pre>
*
* <h3>Supported Expressions</h3>
* <ul>
* <li> "14.4 Constant Expressions" for "edm:Bool", "edm:Date", "edm:DateTimeOffset",
* "edm:Decimal", "edm:Float", "edm:Guid", "edm:Int", "edm:TimeOfDay".
* <li> constant "14.4.11 Expression edm:String": This is turned into a fixed text
* (for example <code>"Width"</code>). String constants that contain a simple
* binding <code>"{@i18n>...}"</code> to the hard-coded model name "@i18n" with
* arbitrary path are not turned into a fixed text, but kept as a data binding
* expression; this allows local annotation files to refer to a resource bundle for
* internationalization.
* <li> dynamic "14.5.1 Comparison and Logical Operators": These are turned into
* expression bindings to perform the operations at runtime.
* <li> dynamic "14.5.3 Expression edm:Apply":
* <ul>
* <li> "14.5.3.1.1 Function odata.concat": This is turned into a data binding
* expression.
* <li> "14.5.3.1.2 Function odata.fillUriTemplate": This is turned into an
* expression binding to fill the template at runtime.
* <li> "14.5.3.1.3 Function odata.uriEncode": This is turned into an expression
* binding to encode the parameter at runtime.
* <li> Apply functions may be nested arbitrarily.
* </ul>
* <li> dynamic "14.5.5 Expression edm:Collection": This is turned into an expression
* binding to be evaluated at runtime. Elements can be conditionally added to the
* collection when using dynamic "14.5.6 Expression edm:If" as a direct child.
* <li> dynamic "14.5.6 Expression edm:If": This is turned into an expression
* binding to be evaluated at runtime. The expression is a conditional expression
* like <code>"{=condition ? expression1 : expression2}"</code>.
* <li> dynamic "14.5.10 Expression edm:Null": This is turned into a
* <code>null</code> value. It is ignored in <code>odata.concat</code>.
* <li> dynamic "14.5.12 Expression edm:Path" and "14.5.13 Expression
* edm:PropertyPath": These are turned into simple data bindings, for example
* <code>"{Name}"</code>. Since 1.78.0, both are also supported if
* <code>vRawValue</code> is the path itself, and not the object wrapping it.
* </ul>
*
* <h3>Annotations on an Operation or a Parameter</h3>
* Since 1.71.0, for annotations on an operation or a parameter, the binding parameter's
* name is stripped off any dynamic "14.5.12 Expression edm:Path" and
* "14.5.13 Expression edm:PropertyPath" where it might be used as a first segment.
* Since 1.76.0 this does not apply to annotations on a parameter.
* In the former case, we assume that the resulting data binding is
* relative to the parent context of the operation binding, that is, to the context
* representing the binding parameter itself.
* In the latter case, we assume that the resulting data binding is relative to the
* parameter context of the operation binding (see
* {@link sap.ui.model.odata.v4.ODataContextBinding#getParameterContext}).
*
* Example:
* <pre>
* <Action Name="ShipProduct" EntitySetPath="_it" IsBound="true" >
* <Parameter Name="_it" Type="name.space.Product" Nullable="false"/>
* <Parameter Name="City" Type="Edm.String"/>
* </Action>
* </pre>
* For the operation <code>ShipProduct</code> mentioned above, the following annotation
* targets an operation parameter and refers back to the binding parameter.
* <pre>
* <Annotations Target="name.space.ShipProduct(name.space.Product)/City">
* <Annotation Term="com.sap.vocabularies.Common.v1.Text" Path="_it/SupplierIdentifier"/>
* </Annotations>
* </pre>
*
* Using <code>AnnotationHelper.value</code> like
* <pre>
* <Text text="{meta>/Products/name.space.ShipProduct/$Parameter/City@com.sap.vocabularies.Common.v1.Text@@sap.ui.model.odata.v4.AnnotationHelper.value}" />
* </pre>
* results in
* <pre>
* <Text text="{_it/SupplierIdentifier}" />
* </pre>
* and the data binding evaluates to the <code>SupplierIdentifier</code> property of the
* entity the operation is called on.
*
* <h3>Operation Parameters</h3>
* Since 1.73.0, this function can be used on action or function parameters and results
* in a relative data binding, just like a "14.5.12 Expression edm:Path".
*
* Assume we have the following metadata for an unbound action "AcChangeTeamBudgetByID":
* <pre>
* <Action Name="AcChangeTeamBudgetByID">
* <Parameter Name="TeamID" Type="Edm.String" Nullable="false" MaxLength="10"/>
* <Parameter Name="Budget" Type="Edm.Decimal" Nullable="false" Precision="16" Scale="variable"/>
* </Action>
* </pre>
*
* Let <code>ChangeTeamBudgetByID</code> be the action import of this action. Using
* <code>AnnotationHelper.value</code> for the <code>TeamID</code> like
* <pre>
* <Text text="{meta>/ChangeTeamBudgetByID/TeamID@@sap.ui.model.odata.v4.AnnotationHelper.value}" />
* </pre>
* results in
* <pre>
* <Text text="{TeamID}" />
* </pre>
*
* <h3>Binding Parameters</h3>
* Since 1.77.0, binding parameters can be given. The usage
* <pre>
* <Input value="{meta>/ChangeTeamBudgetByID/Budget@@sap.ui.model.odata.v4.AnnotationHelper.value($($$noPatch : true$))}" />
* </pre>
* results in a data binding with the given binding parameters. Note how, for an object
* notation, curly brackets must be replaced by <code>$(</code> and <code>$)</code>
* respectively.
*
* <h3>Structural Properties</h3>
* Since 1.78.0, this function can be used on a structural property and results in a
* relative data binding, just like a "14.5.12 Expression edm:Path". The usage
* <pre>
* <Input value="{meta>/Department/Name@@sap.ui.model.odata.v4.AnnotationHelper.value}"/>
* </pre>
* results in
* <pre>
* < Input value="{Name}"/>
* </pre>
*
* @param {any} vRawValue
* The raw value from the meta model
* @param {object} oDetails
* The details object
* @param {any[]} [oDetails.arguments]
* Optional arguments: an optional map of binding parameters; this will be added to
* each resulting data binding
* @param {sap.ui.model.Context} oDetails.context
* Points to the given raw value, that is
* <code>oDetails.context.getProperty("") === vRawValue</code>
* @param {object} [oDetails.overload]
* The single operation overload that was targeted by annotations on an operation or
* a parameter; needed to strip off the binding parameter's name from any dynamic
* "14.5.12 Expression edm:Path" and "14.5.13 Expression edm:PropertyPath" where it
* might be used as a first segment (since 1.72.0). This does not apply to annotations
* on a parameter (since 1.76.0).
* @returns {string}
* A data binding or a fixed text or a sequence thereof
*
* @public
* @see sap.ui.model.odata.v4.AnnotationHelper.format
* @since 1.43.0
*/
value : function (vRawValue, oDetails) {
var sPath = oDetails.context.getPath();
if (sPath.endsWith("/")) {
// cut off trailing slash, happens with computed annotations
sPath = sPath.slice(0, -1);
} else if (sPath.endsWith("/$Path")) {
sPath = sPath.slice(0, -6);
vRawValue = {$Path : vRawValue};
} else if (sPath.endsWith("/$PropertyPath")) {
sPath = sPath.slice(0, -14);
vRawValue = {$PropertyPath : vRawValue};
}
return Expression.getExpression({
asExpression : false,
complexBinding : false,
ignoreAsPrefix : oDetails.overload && oDetails.overload.$IsBound
&& !sPath.includes("/$Parameter/")
? oDetails.overload.$Parameter[0].$Name + "/"
: "",
model : oDetails.context.getModel(),
parameters : oDetails.arguments && oDetails.arguments[0],
path : sPath,
prefix : "",
value : vRawValue,
$$valueAsPromise : oDetails.$$valueAsPromise
});
}
};
return AnnotationHelper;
}, /* bExport= */ true);