@sapui5/sap.fe.core 1.98.0 → 1.99.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/package.json +3 -2
  2. package/src/sap/fe/core/.library +1 -1
  3. package/src/sap/fe/core/AnnotationHelper.js +377 -405
  4. package/src/sap/fe/core/AnnotationHelper.ts +385 -0
  5. package/src/sap/fe/core/AppComponent.js +3 -2
  6. package/src/sap/fe/core/AppStateHandler.js +229 -181
  7. package/src/sap/fe/core/AppStateHandler.ts +171 -0
  8. package/src/sap/fe/core/BaseController.js +3 -2
  9. package/src/sap/fe/core/BusyLocker.js +105 -121
  10. package/src/sap/fe/core/BusyLocker.ts +98 -0
  11. package/src/sap/fe/core/CommonUtils.js +2073 -2399
  12. package/src/sap/fe/core/CommonUtils.ts +2078 -0
  13. package/src/sap/fe/core/ExtensionAPI.js +3 -2
  14. package/src/sap/fe/core/PageController.js +84 -125
  15. package/src/sap/fe/core/PageController.ts +101 -0
  16. package/src/sap/fe/core/RouterProxy.js +986 -823
  17. package/src/sap/fe/core/RouterProxy.ts +838 -0
  18. package/src/sap/fe/core/Synchronization.js +51 -35
  19. package/src/sap/fe/core/Synchronization.ts +29 -0
  20. package/src/sap/fe/core/TemplateComponent.js +173 -164
  21. package/src/sap/fe/core/TemplateComponent.ts +166 -0
  22. package/src/sap/fe/core/TemplateModel.js +97 -54
  23. package/src/sap/fe/core/TemplateModel.ts +63 -0
  24. package/src/sap/fe/core/TransactionHelper.js +1576 -1664
  25. package/src/sap/fe/core/TransactionHelper.ts +1706 -0
  26. package/src/sap/fe/core/actions/draft.js +559 -608
  27. package/src/sap/fe/core/actions/draft.ts +593 -0
  28. package/src/sap/fe/core/actions/messageHandling.js +545 -514
  29. package/src/sap/fe/core/actions/messageHandling.ts +532 -0
  30. package/src/sap/fe/core/actions/nonDraft.js +17 -19
  31. package/src/sap/fe/core/actions/nonDraft.ts +12 -0
  32. package/src/sap/fe/core/actions/operations.js +1074 -1215
  33. package/src/sap/fe/core/actions/operations.ts +1162 -0
  34. package/src/sap/fe/core/actions/sticky.js +102 -105
  35. package/src/sap/fe/core/actions/sticky.ts +102 -0
  36. package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.js +3 -2
  37. package/src/sap/fe/core/controllerextensions/EditFlow.js +54 -211
  38. package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +3 -2
  39. package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +279 -11
  40. package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +8 -6
  41. package/src/sap/fe/core/controllerextensions/InternalRouting.js +47 -45
  42. package/src/sap/fe/core/controllerextensions/KPIManagement.js +2 -3
  43. package/src/sap/fe/core/controllerextensions/KPIManagement.ts +29 -26
  44. package/src/sap/fe/core/controllerextensions/MassEdit.js +101 -8
  45. package/src/sap/fe/core/controllerextensions/MessageHandler.js +17 -8
  46. package/src/sap/fe/core/controllerextensions/PageReady.js +2 -2
  47. package/src/sap/fe/core/controllerextensions/PageReady.ts +12 -8
  48. package/src/sap/fe/core/controllerextensions/Paginator.js +3 -2
  49. package/src/sap/fe/core/controllerextensions/Placeholder.js +3 -2
  50. package/src/sap/fe/core/controllerextensions/Routing.js +9 -4
  51. package/src/sap/fe/core/controllerextensions/RoutingListener.js +3 -2
  52. package/src/sap/fe/core/controllerextensions/Share.js +3 -2
  53. package/src/sap/fe/core/controllerextensions/SideEffects.js +3 -3
  54. package/src/sap/fe/core/controllerextensions/SideEffects.ts +18 -18
  55. package/src/sap/fe/core/controllerextensions/ViewState.js +3 -2
  56. package/src/sap/fe/core/controls/CommandExecution.js +3 -2
  57. package/src/sap/fe/core/controls/ConditionalWrapper.js +3 -2
  58. package/src/sap/fe/core/controls/CustomQuickViewPage.js +3 -2
  59. package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +3 -2
  60. package/src/sap/fe/core/controls/FieldWrapper.js +11 -22
  61. package/src/sap/fe/core/controls/FilterBar.js +3 -2
  62. package/src/sap/fe/core/controls/FormElementWrapper.js +3 -2
  63. package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +3 -2
  64. package/src/sap/fe/core/controls/NonComputedVisibleKeyFieldsDialog.fragment.xml +1 -0
  65. package/src/sap/fe/core/controls/filterbar/FilterContainer.js +3 -2
  66. package/src/sap/fe/core/controls/filterbar/VisualFilter.js +5 -4
  67. package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +3 -2
  68. package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +3 -2
  69. package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +3 -2
  70. package/src/sap/fe/core/converters/ConverterContext.js +2 -2
  71. package/src/sap/fe/core/converters/ConverterContext.ts +4 -3
  72. package/src/sap/fe/core/converters/ManifestSettings.js +1 -1
  73. package/src/sap/fe/core/converters/ManifestSettings.ts +1 -0
  74. package/src/sap/fe/core/converters/ManifestWrapper.js +39 -26
  75. package/src/sap/fe/core/converters/ManifestWrapper.ts +9 -0
  76. package/src/sap/fe/core/converters/MetaModelConverter.js +11 -8
  77. package/src/sap/fe/core/converters/MetaModelConverter.ts +16 -16
  78. package/src/sap/fe/core/converters/TemplateConverter.js +1 -1
  79. package/src/sap/fe/core/converters/TemplateConverter.ts +2 -2
  80. package/src/sap/fe/core/converters/common/AnnotationConverter.js +17 -13
  81. package/src/sap/fe/core/converters/controls/Common/Action.js +2 -2
  82. package/src/sap/fe/core/converters/controls/Common/Action.ts +2 -2
  83. package/src/sap/fe/core/converters/controls/Common/Chart.js +5 -3
  84. package/src/sap/fe/core/converters/controls/Common/Chart.ts +11 -3
  85. package/src/sap/fe/core/converters/controls/Common/DataVisualization.js +3 -3
  86. package/src/sap/fe/core/converters/controls/Common/DataVisualization.ts +2 -2
  87. package/src/sap/fe/core/converters/controls/Common/Form.js +4 -2
  88. package/src/sap/fe/core/converters/controls/Common/Form.ts +16 -2
  89. package/src/sap/fe/core/converters/controls/Common/Table.js +250 -146
  90. package/src/sap/fe/core/converters/controls/Common/Table.ts +300 -167
  91. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +115 -56
  92. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +120 -74
  93. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +467 -32
  94. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +483 -54
  95. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.js +6 -6
  96. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.ts +9 -15
  97. package/src/sap/fe/core/converters/helpers/Aggregation.js +13 -5
  98. package/src/sap/fe/core/converters/helpers/Aggregation.ts +20 -5
  99. package/src/sap/fe/core/converters/helpers/IssueManager.js +4 -1
  100. package/src/sap/fe/core/converters/helpers/IssueManager.ts +3 -0
  101. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +4 -4
  102. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +3 -3
  103. package/src/sap/fe/core/converters/templates/ListReportConverter.js +13 -6
  104. package/src/sap/fe/core/converters/templates/ListReportConverter.ts +26 -15
  105. package/src/sap/fe/core/designtime/AppComponent.designtime.js +3 -2
  106. package/src/sap/fe/core/formatters/CriticalityFormatter.js +1 -1
  107. package/src/sap/fe/core/formatters/CriticalityFormatter.ts +1 -1
  108. package/src/sap/fe/core/formatters/FPMFormatter.js +1 -1
  109. package/src/sap/fe/core/formatters/FPMFormatter.ts +4 -10
  110. package/src/sap/fe/core/formatters/KPIFormatter.js +1 -1
  111. package/src/sap/fe/core/formatters/KPIFormatter.ts +3 -1
  112. package/src/sap/fe/core/formatters/TableFormatter.js +39 -28
  113. package/src/sap/fe/core/formatters/TableFormatter.ts +43 -28
  114. package/src/sap/fe/core/formatters/ValueFormatter.js +1 -1
  115. package/src/sap/fe/core/formatters/ValueFormatter.ts +7 -6
  116. package/src/sap/fe/core/fpm/Component.js +3 -2
  117. package/src/sap/fe/core/helpers/AppStartupHelper.js +2 -2
  118. package/src/sap/fe/core/helpers/AppStartupHelper.ts +7 -4
  119. package/src/sap/fe/core/helpers/BindingExpression.js +314 -355
  120. package/src/sap/fe/core/helpers/BindingExpression.ts +317 -391
  121. package/src/sap/fe/core/helpers/ClassSupport.js +27 -15
  122. package/src/sap/fe/core/helpers/ClassSupport.ts +31 -20
  123. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +63 -59
  124. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.ts +56 -0
  125. package/src/sap/fe/core/helpers/EditState.js +81 -84
  126. package/src/sap/fe/core/helpers/EditState.ts +81 -0
  127. package/src/sap/fe/core/helpers/ExcelFormatHelper.js +62 -48
  128. package/src/sap/fe/core/helpers/ExcelFormatHelper.ts +49 -0
  129. package/src/sap/fe/core/helpers/FPMHelper.js +52 -56
  130. package/src/sap/fe/core/helpers/FPMHelper.ts +62 -0
  131. package/src/sap/fe/core/helpers/KeepAliveHelper.js +3 -4
  132. package/src/sap/fe/core/helpers/KeepAliveHelper.ts +9 -9
  133. package/src/sap/fe/core/helpers/MassEditHelper.js +27 -18
  134. package/src/sap/fe/core/helpers/ModelHelper.js +229 -225
  135. package/src/sap/fe/core/helpers/ModelHelper.ts +227 -0
  136. package/src/sap/fe/core/helpers/PasteHelper.js +210 -132
  137. package/src/sap/fe/core/helpers/PasteHelper.ts +196 -0
  138. package/src/sap/fe/core/helpers/SemanticDateOperators.js +332 -313
  139. package/src/sap/fe/core/helpers/SemanticDateOperators.ts +330 -0
  140. package/src/sap/fe/core/helpers/SemanticKeyHelper.js +66 -67
  141. package/src/sap/fe/core/helpers/SemanticKeyHelper.ts +73 -0
  142. package/src/sap/fe/core/helpers/StableIdHelper.js +4 -7
  143. package/src/sap/fe/core/helpers/StableIdHelper.ts +2 -6
  144. package/src/sap/fe/core/jsx-runtime/jsx.js +1 -1
  145. package/src/sap/fe/core/jsx-runtime/jsx.ts +1 -1
  146. package/src/sap/fe/core/library.js +4 -3
  147. package/src/sap/fe/core/library.support.js +3 -2
  148. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +1 -1
  149. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +1 -1
  150. package/src/sap/fe/core/messagebundle.properties +23 -10
  151. package/src/sap/fe/core/messagebundle_ar.properties +9 -0
  152. package/src/sap/fe/core/messagebundle_bg.properties +9 -0
  153. package/src/sap/fe/core/messagebundle_ca.properties +9 -0
  154. package/src/sap/fe/core/messagebundle_cs.properties +10 -1
  155. package/src/sap/fe/core/messagebundle_cy.properties +9 -0
  156. package/src/sap/fe/core/messagebundle_da.properties +9 -0
  157. package/src/sap/fe/core/messagebundle_de.properties +9 -0
  158. package/src/sap/fe/core/messagebundle_el.properties +9 -0
  159. package/src/sap/fe/core/messagebundle_en.properties +9 -0
  160. package/src/sap/fe/core/messagebundle_en_GB.properties +9 -0
  161. package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +9 -0
  162. package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +9 -0
  163. package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +9 -0
  164. package/src/sap/fe/core/messagebundle_es.properties +9 -0
  165. package/src/sap/fe/core/messagebundle_es_MX.properties +9 -0
  166. package/src/sap/fe/core/messagebundle_et.properties +9 -0
  167. package/src/sap/fe/core/messagebundle_fi.properties +9 -0
  168. package/src/sap/fe/core/messagebundle_fr.properties +13 -4
  169. package/src/sap/fe/core/messagebundle_fr_CA.properties +15 -6
  170. package/src/sap/fe/core/messagebundle_hi.properties +15 -6
  171. package/src/sap/fe/core/messagebundle_hr.properties +15 -6
  172. package/src/sap/fe/core/messagebundle_hu.properties +9 -0
  173. package/src/sap/fe/core/messagebundle_id.properties +10 -1
  174. package/src/sap/fe/core/messagebundle_it.properties +9 -0
  175. package/src/sap/fe/core/messagebundle_iw.properties +9 -0
  176. package/src/sap/fe/core/messagebundle_ja.properties +9 -0
  177. package/src/sap/fe/core/messagebundle_kk.properties +15 -6
  178. package/src/sap/fe/core/messagebundle_ko.properties +9 -0
  179. package/src/sap/fe/core/messagebundle_lt.properties +9 -0
  180. package/src/sap/fe/core/messagebundle_lv.properties +15 -6
  181. package/src/sap/fe/core/messagebundle_ms.properties +14 -5
  182. package/src/sap/fe/core/messagebundle_nl.properties +9 -0
  183. package/src/sap/fe/core/messagebundle_no.properties +9 -0
  184. package/src/sap/fe/core/messagebundle_pl.properties +9 -0
  185. package/src/sap/fe/core/messagebundle_pt.properties +9 -0
  186. package/src/sap/fe/core/messagebundle_pt_PT.properties +9 -0
  187. package/src/sap/fe/core/messagebundle_ro.properties +9 -0
  188. package/src/sap/fe/core/messagebundle_ru.properties +9 -0
  189. package/src/sap/fe/core/messagebundle_sh.properties +11 -2
  190. package/src/sap/fe/core/messagebundle_sk.properties +9 -0
  191. package/src/sap/fe/core/messagebundle_sl.properties +15 -6
  192. package/src/sap/fe/core/messagebundle_sv.properties +9 -0
  193. package/src/sap/fe/core/messagebundle_th.properties +9 -0
  194. package/src/sap/fe/core/messagebundle_tr.properties +9 -0
  195. package/src/sap/fe/core/messagebundle_uk.properties +9 -0
  196. package/src/sap/fe/core/messagebundle_vi.properties +9 -0
  197. package/src/sap/fe/core/messagebundle_zh_CN.properties +9 -0
  198. package/src/sap/fe/core/messagebundle_zh_TW.properties +15 -6
  199. package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +1 -1
  200. package/src/sap/fe/core/services/AsyncComponentServiceFactory.ts +3 -1
  201. package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +269 -202
  202. package/src/sap/fe/core/services/CacheHandlerServiceFactory.ts +212 -0
  203. package/src/sap/fe/core/services/EnvironmentServiceFactory.js +1 -1
  204. package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +7 -6
  205. package/src/sap/fe/core/services/NavigationServiceFactory.js +406 -300
  206. package/src/sap/fe/core/services/NavigationServiceFactory.ts +316 -0
  207. package/src/sap/fe/core/services/ResourceModelServiceFactory.js +149 -81
  208. package/src/sap/fe/core/services/ResourceModelServiceFactory.ts +80 -0
  209. package/src/sap/fe/core/services/RoutingServiceFactory.js +987 -899
  210. package/src/sap/fe/core/services/RoutingServiceFactory.ts +898 -0
  211. package/src/sap/fe/core/services/ShellServicesFactory.js +1 -1
  212. package/src/sap/fe/core/services/ShellServicesFactory.ts +15 -10
  213. package/src/sap/fe/core/services/SideEffectsServiceFactory.js +35 -81
  214. package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +46 -94
  215. package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +461 -487
  216. package/src/sap/fe/core/services/TemplatedViewServiceFactory.ts +453 -0
  217. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +10 -8
  218. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.ts +8 -0
  219. package/src/sap/fe/core/support/AnnotationIssue.support.js +15 -3
  220. package/src/sap/fe/core/support/AnnotationIssue.support.ts +16 -2
  221. package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.js +2 -2
  222. package/src/sap/fe/core/support/CollectionFacetUnsupportedLevel.support.ts +1 -1
  223. package/src/sap/fe/core/templating/FieldControlHelper.js +8 -8
  224. package/src/sap/fe/core/templating/FieldControlHelper.ts +25 -7
  225. package/src/sap/fe/core/templating/FilterHelper.js +138 -72
  226. package/src/sap/fe/core/templating/FilterHelper.ts +139 -71
  227. package/src/sap/fe/core/templating/UIFormatters.js +31 -1
  228. package/src/sap/fe/core/templating/UIFormatters.ts +33 -2
  229. package/src/sap/fe/core/type/Email.js +1 -1
  230. package/src/sap/fe/core/type/Email.ts +4 -6
  231. package/ui5.yaml +0 -3
@@ -28,7 +28,7 @@ import { ApplyAnnotationExpression, PathAnnotationExpression } from "@sap-ux/voc
28
28
  import { EntitySet } from "@sap-ux/vocabularies-types/dist/Converter";
29
29
  import { resolveEnumValue } from "./AnnotationEnum";
30
30
 
31
- type PrimitiveType = string | number | boolean | object | null | undefined;
31
+ type PrimitiveType = string | number | bigint | boolean | object | null | undefined;
32
32
 
33
33
  type BaseExpression<T> = {
34
34
  _type: string;
@@ -181,14 +181,7 @@ export const unresolveableExpression: UnresolveableBindingExpression = {
181
181
  };
182
182
 
183
183
  function escapeXmlAttribute(inputString: string) {
184
- return inputString.replace(/[']/g, function(c: string) {
185
- switch (c) {
186
- case "'":
187
- return "\\'";
188
- default:
189
- return c;
190
- }
191
- });
184
+ return inputString.replace(/'/g, "\\'");
192
185
  }
193
186
 
194
187
  export function hasUnresolveableExpression(...expressions: Expression<any>[]): boolean {
@@ -236,7 +229,7 @@ export function _checkExpressionsAreEqual<T>(a: Expression<T>, b: Expression<T>)
236
229
 
237
230
  case "Comparison":
238
231
  return (
239
- a.operator == (b as ComparisonExpression).operator &&
232
+ a.operator === (b as ComparisonExpression).operator &&
240
233
  _checkExpressionsAreEqual(a.operand1, (b as ComparisonExpression).operand1) &&
241
234
  _checkExpressionsAreEqual(a.operand2, (b as ComparisonExpression).operand2)
242
235
  );
@@ -297,6 +290,7 @@ export function _checkExpressionsAreEqual<T>(a: Expression<T>, b: Expression<T>)
297
290
  case "Ref":
298
291
  return a.ref === (b as ReferenceExpression).ref;
299
292
  }
293
+ return false;
300
294
  }
301
295
 
302
296
  /**
@@ -328,21 +322,15 @@ function flattenSetExpression(expression: SetExpression): SetExpression {
328
322
  * @returns {boolean} `true` if the set of expressions contains an expression and its negation
329
323
  */
330
324
  function hasOppositeExpressions(expressions: Expression<boolean>[]): boolean {
331
- if (expressions.length < 2) {
332
- return false;
333
- }
334
-
335
- let i = expressions.length;
336
- while (i--) {
337
- const expression = expressions[i];
338
- const negatedExpression = not(expression);
339
- for (let j = 0; j < i; j++) {
340
- if (_checkExpressionsAreEqual(expressions[j], negatedExpression)) {
325
+ const negatedExpressions = expressions.map(not);
326
+ return expressions.some((expression, index) => {
327
+ for (let i = index + 1; i < negatedExpressions.length; i++) {
328
+ if (_checkExpressionsAreEqual(expression, negatedExpressions[i])) {
341
329
  return true;
342
330
  }
343
331
  }
344
- }
345
- return false;
332
+ return false;
333
+ });
346
334
  }
347
335
 
348
336
  /**
@@ -364,9 +352,9 @@ export function and(...operands: ExpressionOrPrimitive<boolean>[]): Expression<b
364
352
  if (hasUnresolveableExpression(...expressions)) {
365
353
  return unresolveableExpression;
366
354
  }
367
- let isStaticFalse: boolean = false;
355
+ let isStaticFalse = false;
368
356
  const nonTrivialExpression = expressions.filter(expression => {
369
- if (isConstant(expression) && !expression.value) {
357
+ if (isFalse(expression)) {
370
358
  isStaticFalse = true;
371
359
  }
372
360
  return !isConstant(expression);
@@ -375,9 +363,7 @@ export function and(...operands: ExpressionOrPrimitive<boolean>[]): Expression<b
375
363
  return constant(false);
376
364
  } else if (nonTrivialExpression.length === 0) {
377
365
  // Resolve the constant then
378
- const isValid = expressions.reduce((isValid, expression) => {
379
- return isValid && isConstant(expression) && expression.value;
380
- }, true);
366
+ const isValid = expressions.reduce((result, expression) => result && isTrue(expression), true);
381
367
  return constant(isValid);
382
368
  } else if (nonTrivialExpression.length === 1) {
383
369
  return nonTrivialExpression[0];
@@ -410,9 +396,9 @@ export function or(...operands: ExpressionOrPrimitive<boolean>[]): Expression<bo
410
396
  if (hasUnresolveableExpression(...expressions)) {
411
397
  return unresolveableExpression;
412
398
  }
413
- let isStaticTrue: boolean = false;
399
+ let isStaticTrue = false;
414
400
  const nonTrivialExpression = expressions.filter(expression => {
415
- if (isConstant(expression) && expression.value) {
401
+ if (isTrue(expression)) {
416
402
  isStaticTrue = true;
417
403
  }
418
404
  return !isConstant(expression) || expression.value;
@@ -421,9 +407,7 @@ export function or(...operands: ExpressionOrPrimitive<boolean>[]): Expression<bo
421
407
  return constant(true);
422
408
  } else if (nonTrivialExpression.length === 0) {
423
409
  // Resolve the constant then
424
- const isValid = expressions.reduce((isValid, expression) => {
425
- return isValid && isConstant(expression) && expression.value;
426
- }, true);
410
+ const isValid = expressions.reduce((result, expression) => result && isTrue(expression), true);
427
411
  return constant(isValid);
428
412
  } else if (nonTrivialExpression.length === 1) {
429
413
  return nonTrivialExpression[0];
@@ -468,26 +452,26 @@ export function not(operand: ExpressionOrPrimitive<boolean>): Expression<boolean
468
452
  // Create the reverse comparison
469
453
  switch (operand.operator) {
470
454
  case "!==":
471
- return equal(operand.operand1, operand.operand2);
455
+ return { ...operand, operator: "===" };
472
456
  case "<":
473
- return greaterOrEqual(operand.operand1, operand.operand2);
457
+ return { ...operand, operator: ">=" };
474
458
  case "<=":
475
- return greaterThan(operand.operand1, operand.operand2);
459
+ return { ...operand, operator: ">" };
476
460
  case "===":
477
- return notEqual(operand.operand1, operand.operand2);
461
+ return { ...operand, operator: "!==" };
478
462
  case ">":
479
- return lessOrEqual(operand.operand1, operand.operand2);
463
+ return { ...operand, operator: "<=" };
480
464
  case ">=":
481
- return lessThan(operand.operand1, operand.operand2);
465
+ return { ...operand, operator: "<" };
482
466
  }
483
467
  } else if (operand._type === "Not") {
484
468
  return operand.operand;
485
- } else {
486
- return {
487
- _type: "Not",
488
- operand: operand
489
- };
490
469
  }
470
+
471
+ return {
472
+ _type: "Not",
473
+ operand: operand
474
+ };
491
475
  }
492
476
 
493
477
  /**
@@ -559,19 +543,16 @@ export function constant<T extends PrimitiveType>(value: T): ConstantExpression<
559
543
  if (typeof value === "object" && value !== null && value !== undefined) {
560
544
  if (Array.isArray(value)) {
561
545
  constantValue = value.map(wrapPrimitive) as T;
562
- } else if (isPrimitiveObject(value as object)) {
546
+ } else if (isPrimitiveObject(value)) {
563
547
  constantValue = value.valueOf() as T;
564
548
  } else {
565
- const val = value as { [name: string]: ExpressionOrPrimitive<any> };
566
- const obj = Object.keys(val).reduce((obj, key) => {
567
- const value = wrapPrimitive(val[key]);
568
- if (value._type !== "Constant" || value.value !== undefined) {
569
- obj[key] = value;
549
+ constantValue = Object.entries(value).reduce((plainExpression, [key, val]) => {
550
+ const wrappedValue = wrapPrimitive(val);
551
+ if (wrappedValue._type !== "Constant" || wrappedValue.value !== undefined) {
552
+ plainExpression[key] = wrappedValue;
570
553
  }
571
- return obj;
572
- }, {} as PlainExpressionObject);
573
-
574
- constantValue = obj as T;
554
+ return plainExpression;
555
+ }, {} as PlainExpressionObject) as T;
575
556
  }
576
557
  } else {
577
558
  constantValue = value;
@@ -598,16 +579,10 @@ export function resolveBindingString<T extends PrimitiveType>(
598
579
  value: value
599
580
  };
600
581
  }
582
+ } else if (targetType === "boolean" && typeof value === "string" && (value === "true" || value === "false")) {
583
+ return constant(value === "true") as ConstantExpression<T>;
601
584
  } else {
602
- switch (targetType) {
603
- case "boolean":
604
- if (typeof value === "string" && (value === "true" || value === "false")) {
605
- return constant(value === "true") as ConstantExpression<T>;
606
- }
607
- return constant(value) as ConstantExpression<T>;
608
- default:
609
- return constant(value) as ConstantExpression<T>;
610
- }
585
+ return constant(value) as ConstantExpression<T>;
611
586
  }
612
587
  }
613
588
 
@@ -616,11 +591,11 @@ export function resolveBindingString<T extends PrimitiveType>(
616
591
  *
617
592
  * @see fn
618
593
  *
619
- * @param ref Reference
594
+ * @param reference Reference
620
595
  * @returns {ReferenceExpression} The object reference binding part
621
596
  */
622
- export function ref(ref: string | null): ReferenceExpression {
623
- return { _type: "Ref", ref };
597
+ export function ref(reference: string | null): ReferenceExpression {
598
+ return { _type: "Ref", ref: reference };
624
599
  }
625
600
 
626
601
  /**
@@ -662,6 +637,14 @@ export function isConstant<T extends PrimitiveType>(maybeConstant: ExpressionOrP
662
637
  return typeof maybeConstant !== "object" || (maybeConstant as BaseExpression<T>)._type === "Constant";
663
638
  }
664
639
 
640
+ function isTrue(expression: Expression<PrimitiveType>) {
641
+ return isConstant(expression) && expression.value === true;
642
+ }
643
+
644
+ function isFalse(expression: Expression<PrimitiveType>) {
645
+ return isConstant(expression) && expression.value === false;
646
+ }
647
+
665
648
  /**
666
649
  * Checks if the expression or value provided is binding or not.
667
650
  *
@@ -709,97 +692,97 @@ function isPrimitiveObject(objectType: object): boolean {
709
692
  }
710
693
  }
711
694
  /**
712
- * Check if the passed annotation expression is a ComplexAnnotationExpression.
695
+ * Check if the passed annotation annotationValue is a ComplexAnnotationExpression.
713
696
  *
714
697
  * @template T The target type
715
- * @param annotationExpression The annotation expression to evaluate
698
+ * @param annotationValue The annotation annotationValue to evaluate
716
699
  * @returns {boolean} `true` if the object is a {ComplexAnnotationExpression}
717
700
  */
718
- function isComplexAnnotationExpression<T>(
719
- annotationExpression: PropertyAnnotationValue<T>
720
- ): annotationExpression is ComplexAnnotationExpression<T> {
721
- return typeof annotationExpression === "object" && !isPrimitiveObject(annotationExpression as object);
701
+ function isComplexAnnotationExpression<T>(annotationValue: PropertyAnnotationValue<T>): annotationValue is ComplexAnnotationExpression<T> {
702
+ return typeof annotationValue === "object" && !isPrimitiveObject(annotationValue as object);
722
703
  }
723
704
 
724
705
  /**
725
- * Generate the corresponding expression for a given annotation expression.
706
+ * Generate the corresponding annotationValue for a given annotation annotationValue.
726
707
  *
727
708
  * @template T The target type
728
- * @param annotationExpression The source annotation expression
709
+ * @param annotationValue The source annotation annotationValue
729
710
  * @param visitedNavigationPaths The path from the root entity set
730
- * @param defaultValue Default value if the annotationExpression is undefined
711
+ * @param defaultValue Default value if the annotationValue is undefined
731
712
  * @param pathVisitor A function to modify the resulting path
732
- * @returns {Expression<T>} The expression equivalent to that annotation expression
713
+ * @returns {Expression<T>} The annotationValue equivalent to that annotation annotationValue
733
714
  */
734
715
  export function annotationExpression<T extends PrimitiveType>(
735
- annotationExpression: PropertyAnnotationValue<T>,
716
+ annotationValue: PropertyAnnotationValue<T>,
736
717
  visitedNavigationPaths: string[] = [],
737
718
  defaultValue?: ExpressionOrPrimitive<T>,
738
719
  pathVisitor?: Function
739
720
  ): Expression<T> {
740
- if (annotationExpression === undefined) {
721
+ if (annotationValue === undefined) {
741
722
  return wrapPrimitive(defaultValue as T);
742
723
  }
743
- if (!isComplexAnnotationExpression(annotationExpression)) {
744
- return constant(annotationExpression);
745
- } else {
746
- switch (annotationExpression.type) {
747
- case "Path":
748
- return bindingExpression(annotationExpression.path, undefined, visitedNavigationPaths, pathVisitor);
749
- case "If":
750
- return annotationIfExpression(annotationExpression.If, visitedNavigationPaths, pathVisitor);
751
- case "Not":
752
- return not(parseAnnotationCondition(annotationExpression.Not, visitedNavigationPaths, pathVisitor)) as Expression<T>;
753
- case "Eq":
754
- return equal(
755
- parseAnnotationCondition(annotationExpression.Eq[0], visitedNavigationPaths, pathVisitor),
756
- parseAnnotationCondition(annotationExpression.Eq[1], visitedNavigationPaths, pathVisitor)
757
- ) as Expression<T>;
758
- case "Ne":
759
- return notEqual(
760
- parseAnnotationCondition(annotationExpression.Ne[0], visitedNavigationPaths, pathVisitor),
761
- parseAnnotationCondition(annotationExpression.Ne[1], visitedNavigationPaths, pathVisitor)
762
- ) as Expression<T>;
763
- case "Gt":
764
- return greaterThan(
765
- parseAnnotationCondition(annotationExpression.Gt[0], visitedNavigationPaths, pathVisitor),
766
- parseAnnotationCondition(annotationExpression.Gt[1], visitedNavigationPaths, pathVisitor)
767
- ) as Expression<T>;
768
- case "Ge":
769
- return greaterOrEqual(
770
- parseAnnotationCondition(annotationExpression.Ge[0], visitedNavigationPaths, pathVisitor),
771
- parseAnnotationCondition(annotationExpression.Ge[1], visitedNavigationPaths, pathVisitor)
772
- ) as Expression<T>;
773
- case "Lt":
774
- return lessThan(
775
- parseAnnotationCondition(annotationExpression.Lt[0], visitedNavigationPaths, pathVisitor),
776
- parseAnnotationCondition(annotationExpression.Lt[1], visitedNavigationPaths, pathVisitor)
777
- ) as Expression<T>;
778
- case "Le":
779
- return lessOrEqual(
780
- parseAnnotationCondition(annotationExpression.Le[0], visitedNavigationPaths, pathVisitor),
781
- parseAnnotationCondition(annotationExpression.Le[1], visitedNavigationPaths, pathVisitor)
782
- ) as Expression<T>;
783
- case "Or":
784
- return or(
785
- ...(annotationExpression.Or.map(function(orCondition) {
786
- return parseAnnotationCondition(orCondition, visitedNavigationPaths, pathVisitor);
787
- }) as Expression<boolean>[])
788
- ) as Expression<T>;
789
- case "And":
790
- return and(
791
- ...(annotationExpression.And.map(function(andCondition) {
792
- return parseAnnotationCondition(andCondition, visitedNavigationPaths, pathVisitor);
793
- }) as Expression<boolean>[])
794
- ) as Expression<T>;
795
- case "Apply":
796
- return annotationApplyExpression(
797
- annotationExpression as ApplyAnnotationExpression<string>,
798
- visitedNavigationPaths,
799
- pathVisitor
800
- ) as Expression<T>;
801
- }
724
+
725
+ if (!isComplexAnnotationExpression(annotationValue)) {
726
+ return constant(annotationValue);
802
727
  }
728
+
729
+ switch (annotationValue.type) {
730
+ case "Path":
731
+ return bindingExpression(annotationValue.path, undefined, visitedNavigationPaths, pathVisitor);
732
+ case "If":
733
+ return annotationIfExpression(annotationValue.If, visitedNavigationPaths, pathVisitor);
734
+ case "Not":
735
+ return not(parseAnnotationCondition(annotationValue.Not, visitedNavigationPaths, pathVisitor)) as Expression<T>;
736
+ case "Eq":
737
+ return equal(
738
+ parseAnnotationCondition(annotationValue.Eq[0], visitedNavigationPaths, pathVisitor),
739
+ parseAnnotationCondition(annotationValue.Eq[1], visitedNavigationPaths, pathVisitor)
740
+ ) as Expression<T>;
741
+ case "Ne":
742
+ return notEqual(
743
+ parseAnnotationCondition(annotationValue.Ne[0], visitedNavigationPaths, pathVisitor),
744
+ parseAnnotationCondition(annotationValue.Ne[1], visitedNavigationPaths, pathVisitor)
745
+ ) as Expression<T>;
746
+ case "Gt":
747
+ return greaterThan(
748
+ parseAnnotationCondition(annotationValue.Gt[0], visitedNavigationPaths, pathVisitor),
749
+ parseAnnotationCondition(annotationValue.Gt[1], visitedNavigationPaths, pathVisitor)
750
+ ) as Expression<T>;
751
+ case "Ge":
752
+ return greaterOrEqual(
753
+ parseAnnotationCondition(annotationValue.Ge[0], visitedNavigationPaths, pathVisitor),
754
+ parseAnnotationCondition(annotationValue.Ge[1], visitedNavigationPaths, pathVisitor)
755
+ ) as Expression<T>;
756
+ case "Lt":
757
+ return lessThan(
758
+ parseAnnotationCondition(annotationValue.Lt[0], visitedNavigationPaths, pathVisitor),
759
+ parseAnnotationCondition(annotationValue.Lt[1], visitedNavigationPaths, pathVisitor)
760
+ ) as Expression<T>;
761
+ case "Le":
762
+ return lessOrEqual(
763
+ parseAnnotationCondition(annotationValue.Le[0], visitedNavigationPaths, pathVisitor),
764
+ parseAnnotationCondition(annotationValue.Le[1], visitedNavigationPaths, pathVisitor)
765
+ ) as Expression<T>;
766
+ case "Or":
767
+ return or(
768
+ ...(annotationValue.Or.map(function(orCondition) {
769
+ return parseAnnotationCondition(orCondition, visitedNavigationPaths, pathVisitor);
770
+ }) as Expression<boolean>[])
771
+ ) as Expression<T>;
772
+ case "And":
773
+ return and(
774
+ ...(annotationValue.And.map(function(andCondition) {
775
+ return parseAnnotationCondition(andCondition, visitedNavigationPaths, pathVisitor);
776
+ }) as Expression<boolean>[])
777
+ ) as Expression<T>;
778
+ case "Apply":
779
+ return annotationApplyExpression(
780
+ annotationValue as ApplyAnnotationExpression<string>,
781
+ visitedNavigationPaths,
782
+ pathVisitor
783
+ ) as Expression<T>;
784
+ }
785
+ return unresolveableExpression;
803
786
  }
804
787
 
805
788
  /**
@@ -889,41 +872,40 @@ function parseAnnotationCondition<T extends PrimitiveType>(
889
872
  );
890
873
  } else if (annotationValue.hasOwnProperty("$EnumMember")) {
891
874
  return constant(resolveEnumValue((annotationValue as any).$EnumMember) as T);
892
- } else {
893
- return constant(false as T);
894
875
  }
876
+ return constant(false as T);
895
877
  }
896
878
 
897
879
  /**
898
880
  * Process the {IfAnnotationExpressionValue} into an expression.
899
881
  *
900
882
  * @template T The target type
901
- * @param annotationIfExpression An If expression returning the type T
883
+ * @param annotationValue An If expression returning the type T
902
884
  * @param visitedNavigationPaths The path from the root entity set
903
885
  * @param pathVisitor A function to modify the resulting path
904
886
  * @returns {Expression<T>} The equivalent ifElse expression
905
887
  */
906
888
  export function annotationIfExpression<T extends PrimitiveType>(
907
- annotationIfExpression: IfAnnotationExpressionValue<T>,
889
+ annotationValue: IfAnnotationExpressionValue<T>,
908
890
  visitedNavigationPaths: string[] = [],
909
891
  pathVisitor?: Function
910
892
  ): Expression<T> {
911
893
  return ifElse(
912
- parseAnnotationCondition(annotationIfExpression[0], visitedNavigationPaths, pathVisitor),
913
- parseAnnotationCondition(annotationIfExpression[1] as any, visitedNavigationPaths, pathVisitor),
914
- parseAnnotationCondition(annotationIfExpression[2] as any, visitedNavigationPaths, pathVisitor)
894
+ parseAnnotationCondition(annotationValue[0], visitedNavigationPaths, pathVisitor),
895
+ parseAnnotationCondition(annotationValue[1] as any, visitedNavigationPaths, pathVisitor),
896
+ parseAnnotationCondition(annotationValue[2] as any, visitedNavigationPaths, pathVisitor)
915
897
  );
916
898
  }
917
899
 
918
900
  export function annotationApplyExpression(
919
- annotationApplyExpression: ApplyAnnotationExpression<string>,
901
+ applyExpression: ApplyAnnotationExpression<string>,
920
902
  visitedNavigationPaths: string[] = [],
921
903
  pathVisitor?: Function
922
904
  ): Expression<string> {
923
- switch (annotationApplyExpression.Function) {
905
+ switch (applyExpression.Function) {
924
906
  case "odata.concat":
925
907
  return concat(
926
- ...annotationApplyExpression.Apply.map((applyParam: any) => {
908
+ ...applyExpression.Apply.map((applyParam: any) => {
927
909
  let applyParamConverted = applyParam;
928
910
  if (applyParam.hasOwnProperty("$Path")) {
929
911
  applyParamConverted = {
@@ -945,8 +927,8 @@ export function annotationApplyExpression(
945
927
  return annotationExpression(applyParamConverted, visitedNavigationPaths, undefined, pathVisitor);
946
928
  })
947
929
  );
948
- break;
949
930
  }
931
+ return unresolveableExpression;
950
932
  }
951
933
 
952
934
  /**
@@ -969,10 +951,6 @@ function comparison<T extends PrimitiveType>(
969
951
  return unresolveableExpression;
970
952
  }
971
953
  if (isConstant(leftExpression) && isConstant(rightExpression)) {
972
- if (leftExpression.value === undefined || rightExpression.value === undefined) {
973
- return constant(leftExpression.value === rightExpression.value);
974
- }
975
-
976
954
  switch (operator) {
977
955
  case "!==":
978
956
  return constant(leftExpression.value !== rightExpression.value);
@@ -985,7 +963,6 @@ function comparison<T extends PrimitiveType>(
985
963
  case ">=":
986
964
  return constant(leftExpression.value >= rightExpression.value);
987
965
  case "===":
988
- default:
989
966
  return constant(leftExpression.value === rightExpression.value);
990
967
  }
991
968
  } else {
@@ -998,13 +975,13 @@ function comparison<T extends PrimitiveType>(
998
975
  }
999
976
  }
1000
977
 
1001
- export function length(bindingExpression: BindingExpressionExpression<any> | UnresolveableBindingExpression): Expression<number> {
1002
- if (bindingExpression._type === "Unresolveable") {
1003
- return bindingExpression;
978
+ export function length(expression: BindingExpressionExpression<any> | UnresolveableBindingExpression): Expression<number> {
979
+ if (expression._type === "Unresolveable") {
980
+ return expression;
1004
981
  }
1005
982
  return {
1006
983
  _type: "Length",
1007
- bindingExpression
984
+ bindingExpression: expression
1008
985
  };
1009
986
  }
1010
987
 
@@ -1029,32 +1006,35 @@ export function equal<T extends PrimitiveType>(
1029
1006
  return constant(true);
1030
1007
  }
1031
1008
 
1032
- // ((a === c) === true) => (a === c)
1033
- if (leftExpression._type === "Comparison" && isConstant(rightExpression) && rightExpression.value === true) {
1034
- return leftExpression;
1035
- } else if (rightExpression._type === "Comparison" && isConstant(leftExpression) && leftExpression.value === true) {
1036
- // (true === (a === c)) => (a === c)
1037
- return rightExpression;
1038
- } else if (leftExpression._type === "Comparison" && isConstant(rightExpression) && rightExpression.value === true) {
1039
- // ((a === c) === false) => !(a === c)
1040
- return not(leftExpression);
1041
- } else if (leftExpression._type === "IfElse" && _checkExpressionsAreEqual(leftExpression.onTrue, rightExpression)) {
1042
- // (if(xxxx) { aaa } else { bbb } ) === aaa )
1043
- return or(leftExpression.condition, equal(leftExpression.onFalse, rightExpression));
1044
- } else if (leftExpression._type === "IfElse" && _checkExpressionsAreEqual(leftExpression.onFalse, rightExpression)) {
1045
- return or(not(leftExpression.condition), equal(leftExpression.onTrue, rightExpression));
1046
- } else if (
1047
- leftExpression._type === "IfElse" &&
1048
- isConstant(leftExpression.onTrue) &&
1049
- isConstant(rightExpression) &&
1050
- isConstant(leftExpression.onFalse) &&
1051
- !_checkExpressionsAreEqual(leftExpression.onTrue, rightExpression) &&
1052
- !_checkExpressionsAreEqual(leftExpression.onFalse, rightExpression)
1053
- ) {
1054
- return constant(false);
1009
+ function reduce(left: Expression<T>, right: Expression<T>) {
1010
+ if (left._type === "Comparison" && isTrue(right)) {
1011
+ // compare(a, b) === true ~~> compare(a, b)
1012
+ return left;
1013
+ } else if (left._type === "Comparison" && isFalse(right)) {
1014
+ // compare(a, b) === false ~~> !compare(a, b)
1015
+ return not(left);
1016
+ } else if (left._type === "IfElse" && _checkExpressionsAreEqual(left.onTrue, right)) {
1017
+ // (if (x) { a } else { b }) === a ~~> x || (b === a)
1018
+ return or(left.condition, equal(left.onFalse, right));
1019
+ } else if (left._type === "IfElse" && _checkExpressionsAreEqual(left.onFalse, right)) {
1020
+ // (if (x) { a } else { b }) === b ~~> !x || (a === b)
1021
+ return or(not(left.condition), equal(left.onTrue, right));
1022
+ } else if (
1023
+ left._type === "IfElse" &&
1024
+ isConstant(left.onTrue) &&
1025
+ isConstant(left.onFalse) &&
1026
+ isConstant(right) &&
1027
+ !_checkExpressionsAreEqual(left.onTrue, right) &&
1028
+ !_checkExpressionsAreEqual(left.onFalse, right)
1029
+ ) {
1030
+ return constant(false);
1031
+ }
1032
+ return undefined;
1055
1033
  }
1056
1034
 
1057
- return comparison("===", leftExpression, rightExpression);
1035
+ // exploit symmetry: a === b <~> b === a
1036
+ const reduced = reduce(leftExpression, rightExpression) ?? reduce(rightExpression, leftExpression);
1037
+ return reduced ?? comparison("===", leftExpression, rightExpression);
1058
1038
  }
1059
1039
 
1060
1040
  /**
@@ -1069,36 +1049,7 @@ export function notEqual<T extends PrimitiveType>(
1069
1049
  leftOperand: ExpressionOrPrimitive<T>,
1070
1050
  rightOperand: ExpressionOrPrimitive<T>
1071
1051
  ): Expression<boolean> {
1072
- const leftExpression = wrapPrimitive(leftOperand);
1073
- const rightExpression = wrapPrimitive(rightOperand);
1074
-
1075
- if (_checkExpressionsAreEqual(leftExpression, rightExpression)) {
1076
- return constant(false);
1077
- }
1078
-
1079
- // ((a === c) !== true) => !(a === c)
1080
- if (leftExpression._type === "Comparison" && isConstant(rightExpression) && rightExpression.value === true) {
1081
- return not(leftExpression);
1082
- } else if (leftExpression._type === "Comparison" && isConstant(rightExpression) && rightExpression.value === true) {
1083
- // ((a === c) !== false) => (a === c)
1084
- return leftExpression;
1085
- } else if (leftExpression._type === "IfElse" && _checkExpressionsAreEqual(leftExpression.onTrue, rightExpression)) {
1086
- return and(not(leftExpression.condition), notEqual(leftExpression.onFalse, rightExpression));
1087
- } else if (leftExpression._type === "IfElse" && _checkExpressionsAreEqual(leftExpression.onFalse, rightExpression)) {
1088
- return and(leftExpression.condition, notEqual(leftExpression.onTrue, rightExpression));
1089
- } else if (
1090
- leftExpression._type === "IfElse" &&
1091
- isConstant(leftExpression.onTrue) &&
1092
- isConstant(rightExpression) &&
1093
- isConstant(leftExpression.onFalse) &&
1094
- !_checkExpressionsAreEqual(leftExpression.onTrue, rightExpression) &&
1095
- !_checkExpressionsAreEqual(leftExpression.onFalse, rightExpression)
1096
- ) {
1097
- // If the left expression is an if else where both onTrue and onFalse are not equals to the right expression -> simplify as true
1098
- return constant(true);
1099
- }
1100
-
1101
- return comparison("!==", leftExpression, rightExpression);
1052
+ return not(equal(leftOperand, rightOperand));
1102
1053
  }
1103
1054
 
1104
1055
  /**
@@ -1203,61 +1154,40 @@ export function ifElse<T extends PrimitiveType>(
1203
1154
  onFalseExpression = onFalseExpression.onFalse;
1204
1155
  }
1205
1156
 
1206
- // inline nested if-else expressions: condition
1207
- if (conditionExpression._type === "IfElse") {
1208
- if (
1209
- isConstant(conditionExpression.onFalse) &&
1210
- !conditionExpression.onFalse.value &&
1211
- isConstant(conditionExpression.onTrue) &&
1212
- conditionExpression.onTrue.value
1213
- ) {
1214
- // ifElse(ifElse(X, true, false), a, b) ==> ifElse(X, a, b)
1215
- conditionExpression = conditionExpression.condition;
1216
- } else if (
1217
- isConstant(conditionExpression.onFalse) &&
1218
- conditionExpression.onFalse.value &&
1219
- isConstant(conditionExpression.onTrue) &&
1220
- !conditionExpression.onTrue.value
1221
- ) {
1222
- // ifElse(ifElse(X, false, true), a, b) ==> ifElse(not(X), a, b)
1223
- conditionExpression = not(conditionExpression.condition);
1224
- } else if (
1225
- isConstant(conditionExpression.onTrue) &&
1226
- !conditionExpression.onTrue.value &&
1227
- !isConstant(conditionExpression.onFalse)
1228
- ) {
1229
- // ifElse(ifElse(X, false, a), b, c) ==> ifElse(and(not(X), a), b, c)
1230
- conditionExpression = and(not(conditionExpression.condition), conditionExpression.onFalse);
1231
- }
1232
- }
1233
-
1234
- // again swap branches if needed (in case one of the optimizations above led to a negated condition)
1235
- if (conditionExpression._type === "Not") {
1236
- // ifElse(not(X), a, b) --> ifElse(X, b, a)
1237
- [onTrueExpression, onFalseExpression] = [onFalseExpression, onTrueExpression];
1238
- conditionExpression = not(conditionExpression);
1239
- }
1240
-
1241
- // compute expression result for constant conditions
1157
+ // (if true then a else b) ~~> a
1158
+ // (if false then a else b) ~~> b
1242
1159
  if (isConstant(conditionExpression)) {
1243
1160
  return conditionExpression.value ? onTrueExpression : onFalseExpression;
1244
1161
  }
1245
1162
 
1246
- // compute expression result if onTrue and onFalse branches are equal
1163
+ // if (isConstantBoolean(onTrueExpression) || isConstantBoolean(onFalseExpression)) {
1164
+ // return or(and(condition, onTrueExpression as Expression<boolean>), and(not(condition), onFalseExpression as Expression<boolean>)) as Expression<T>
1165
+ // }
1166
+ // (if X then a else a) ~~> a
1247
1167
  if (_checkExpressionsAreEqual(onTrueExpression, onFalseExpression)) {
1248
1168
  return onTrueExpression;
1249
1169
  }
1250
1170
 
1251
- // If either trueExpression or falseExpression is a value equals to false the expression can be simplified
1252
- // If(Condition) Then XXX Else False -> Condition && XXX
1253
- if (isConstant(onFalseExpression) && onFalseExpression.value === false) {
1171
+ // if X then a else false ~~> X && a
1172
+ if (isFalse(onFalseExpression)) {
1254
1173
  return and(conditionExpression, onTrueExpression as Expression<boolean>) as Expression<T>;
1255
1174
  }
1256
- // If(Condition) Then False Else XXX -> !Condition && XXX
1257
- if (isConstant(onTrueExpression) && onTrueExpression.value === false) {
1175
+
1176
+ // if X then a else true ~~> !X || a
1177
+ if (isTrue(onFalseExpression)) {
1178
+ return or(not(conditionExpression), onTrueExpression as Expression<boolean>) as Expression<T>;
1179
+ }
1180
+
1181
+ // if X then false else a ~~> !X && a
1182
+ if (isFalse(onTrueExpression)) {
1258
1183
  return and(not(conditionExpression), onFalseExpression as Expression<boolean>) as Expression<T>;
1259
1184
  }
1260
1185
 
1186
+ // if X then true else a ~~> X || a
1187
+ if (isTrue(onTrueExpression)) {
1188
+ return or(conditionExpression, onFalseExpression as Expression<boolean>) as Expression<T>;
1189
+ }
1190
+
1261
1191
  return {
1262
1192
  _type: "IfElse",
1263
1193
  condition: conditionExpression,
@@ -1408,17 +1338,17 @@ export function addTypeInformation<T, U extends Fn<T>>(
1408
1338
  /**
1409
1339
  * Function call, optionally with arguments.
1410
1340
  *
1411
- * @param fn Function name or reference to function
1341
+ * @param func Function name or reference to function
1412
1342
  * @param parameters Arguments
1413
1343
  * @param on Object to call the function on
1414
1344
  * @returns {FunctionExpression<T>} Expression representing the function call (not the result of the function call!)
1415
1345
  */
1416
1346
  export function fn<T, U extends FunctionOrName<T>>(
1417
- fn: U,
1347
+ func: U,
1418
1348
  parameters: WrappedTuple<FunctionParameters<T, U>>,
1419
1349
  on?: ExpressionOrPrimitive<object>
1420
1350
  ): FunctionExpression<T> {
1421
- const functionName = typeof fn === "string" ? fn : (fn as Fn<T>).__functionName;
1351
+ const functionName = typeof func === "string" ? func : (func as Fn<T>).__functionName;
1422
1352
  return {
1423
1353
  _type: "Function",
1424
1354
  obj: on !== undefined ? wrapPrimitive(on) : undefined,
@@ -1464,31 +1394,26 @@ export function transformRecursively<T extends PrimitiveType | unknown>(
1464
1394
  inExpression: Expression<T>,
1465
1395
  expressionType: ExpressionType,
1466
1396
  transformFunction: TransformFunction,
1467
- includeAllExpression: boolean = false
1397
+ includeAllExpression = false
1468
1398
  ): Expression<T> {
1469
1399
  let expression = inExpression;
1470
1400
  switch (expression._type) {
1471
1401
  case "Function":
1472
- expression.parameters = expression.parameters.map(expression =>
1473
- transformRecursively(expression, expressionType, transformFunction, includeAllExpression)
1402
+ case "Formatter":
1403
+ expression.parameters = expression.parameters.map(parameter =>
1404
+ transformRecursively(parameter, expressionType, transformFunction, includeAllExpression)
1474
1405
  );
1475
1406
  break;
1476
1407
  case "Concat":
1477
- expression.expressions = expression.expressions.map(expression =>
1478
- transformRecursively(expression, expressionType, transformFunction, includeAllExpression)
1408
+ expression.expressions = expression.expressions.map(subExpression =>
1409
+ transformRecursively(subExpression, expressionType, transformFunction, includeAllExpression)
1479
1410
  );
1480
1411
  break;
1481
1412
  case "ComplexType":
1482
- expression.bindingParameters = expression.bindingParameters.map(expression =>
1483
- transformRecursively(expression, expressionType, transformFunction, includeAllExpression)
1484
- );
1485
- break;
1486
- case "Formatter":
1487
- expression.parameters = expression.parameters.map(expression =>
1488
- transformRecursively(expression, expressionType, transformFunction, includeAllExpression)
1413
+ expression.bindingParameters = expression.bindingParameters.map(bindingParameter =>
1414
+ transformRecursively(bindingParameter, expressionType, transformFunction, includeAllExpression)
1489
1415
  );
1490
1416
  break;
1491
-
1492
1417
  case "IfElse":
1493
1418
  const onTrue = transformRecursively(expression.onTrue, expressionType, transformFunction, includeAllExpression);
1494
1419
  const onFalse = transformRecursively(expression.onFalse, expressionType, transformFunction, includeAllExpression);
@@ -1508,8 +1433,8 @@ export function transformRecursively<T extends PrimitiveType | unknown>(
1508
1433
  break;
1509
1434
  case "Set":
1510
1435
  if (includeAllExpression) {
1511
- expression.operands = expression.operands.map(expression =>
1512
- transformRecursively(expression, expressionType, transformFunction, includeAllExpression)
1436
+ expression.operands = expression.operands.map(operand =>
1437
+ transformRecursively(operand, expressionType, transformFunction, includeAllExpression)
1513
1438
  );
1514
1439
  }
1515
1440
  break;
@@ -1624,7 +1549,7 @@ function compileBindingForBinding<T extends PrimitiveType>(
1624
1549
  ) {
1625
1550
  // This is now a complex binding definition, let's prepare for it
1626
1551
  const complexBindingDefinition = {
1627
- path: `${expressionForBinding.modelName ? expressionForBinding.modelName + ">" : ""}${expressionForBinding.path}`,
1552
+ path: compileBindingPath(expressionForBinding),
1628
1553
  type: expressionForBinding.type,
1629
1554
  targetType: expressionForBinding.targetType,
1630
1555
  parameters: expressionForBinding.parameters,
@@ -1637,12 +1562,38 @@ function compileBindingForBinding<T extends PrimitiveType>(
1637
1562
  }
1638
1563
  return outBinding;
1639
1564
  } else if (embeddedInBinding) {
1640
- return `${embeddedSeparator}{${expressionForBinding.modelName ? expressionForBinding.modelName + ">" : ""}${
1641
- expressionForBinding.path
1642
- }}`;
1565
+ return `${embeddedSeparator}{${compileBindingPath(expressionForBinding)}}`;
1643
1566
  } else {
1644
- return `{${expressionForBinding.modelName ? expressionForBinding.modelName + ">" : ""}${expressionForBinding.path}}`;
1567
+ return `{${compileBindingPath(expressionForBinding)}}`;
1568
+ }
1569
+ }
1570
+
1571
+ function compileComplexTypeExpression<T extends PrimitiveType>(expression: ComplexTypeExpression<T>) {
1572
+ if (expression.bindingParameters.length === 1) {
1573
+ return `{${compilePathParameter(expression.bindingParameters[0], true)}, type: '${expression.type}'}`;
1574
+ }
1575
+
1576
+ let outputEnd;
1577
+ // this code is based on sap.ui.model.odata.v4._AnnotationHelperExpression.fetchCurrencyOrUnit
1578
+ switch (expression.type) {
1579
+ case "sap.ui.model.odata.type.Unit":
1580
+ outputEnd = `,{mode:'OneTime',path:'/##@@requestUnitsOfMeasure',targetType:'any'}],type:'sap.ui.model.odata.type.Unit'`;
1581
+ break;
1582
+ case "sap.ui.model.odata.type.Currency":
1583
+ outputEnd = `,{mode:'OneTime',path:'/##@@requestCurrencyCodes',targetType:'any'}],type:'sap.ui.model.odata.type.Currency'`;
1584
+ break;
1585
+ default:
1586
+ outputEnd = `], type: '${expression.type}'`;
1645
1587
  }
1588
+ if (hasElements(expression.formatOptions)) {
1589
+ outputEnd += `, formatOptions: ${compileBinding(expression.formatOptions)}`;
1590
+ }
1591
+ if (hasElements(expression.parameters)) {
1592
+ outputEnd += `, parameters: ${compileBinding(expression.parameters)}`;
1593
+ }
1594
+ outputEnd += "}";
1595
+
1596
+ return `{mode:'TwoWay', parts:[${expression.bindingParameters.map((param: any) => compilePathParameter(param)).join(",")}${outputEnd}`;
1646
1597
  }
1647
1598
 
1648
1599
  /**
@@ -1683,10 +1634,11 @@ export function compileBinding<T extends PrimitiveType>(
1683
1634
  ): BindingExpression<string> {
1684
1635
  const expr = wrapPrimitive(expression);
1685
1636
  const embeddedSeparator = keepTargetType ? "$" : "%";
1686
- let outProperty = "";
1637
+
1687
1638
  switch (expr._type) {
1688
1639
  case "Unresolveable":
1689
1640
  return undefined;
1641
+
1690
1642
  case "Constant":
1691
1643
  return compileConstant(expr, embeddedInBinding, isNullable);
1692
1644
 
@@ -1698,42 +1650,20 @@ export function compileBinding<T extends PrimitiveType>(
1698
1650
  return expr.obj === undefined
1699
1651
  ? `${expr.fn}(${argumentString})`
1700
1652
  : `${compileBinding(expr.obj, true)}.${expr.fn}(${argumentString})`;
1653
+
1701
1654
  case "EmbeddedExpressionBinding":
1702
- if (embeddedInBinding) {
1703
- return `(${expr.value.substr(2, expr.value.length - 3)})`;
1704
- } else {
1705
- return `${expr.value}`;
1706
- }
1655
+ return embeddedInBinding ? `(${expr.value.substr(2, expr.value.length - 3)})` : `${expr.value}`;
1656
+
1707
1657
  case "EmbeddedBinding":
1708
- if (embeddedInBinding) {
1709
- return `${embeddedSeparator}${expr.value}`;
1710
- } else {
1711
- return `${expr.value}`;
1712
- }
1658
+ return embeddedInBinding ? `${embeddedSeparator}${expr.value}` : `${expr.value}`;
1659
+
1713
1660
  case "DefaultBinding":
1714
1661
  case "Binding":
1715
1662
  return compileBindingForBinding(expr, embeddedInBinding, embeddedSeparator);
1716
1663
 
1717
1664
  case "Comparison":
1718
- const isOperand1Complex = needParenthesis(expr.operand1);
1719
- const isOperand2Complex = needParenthesis(expr.operand2);
1720
- let comparisonPart = "";
1721
- if (isOperand1Complex) {
1722
- comparisonPart += "(";
1723
- }
1724
- comparisonPart += `${compileBinding(expr.operand1, true)}`;
1725
- if (isOperand1Complex) {
1726
- comparisonPart += ")";
1727
- }
1728
- comparisonPart += ` ${expr.operator} `;
1729
- if (isOperand2Complex) {
1730
- comparisonPart += "(";
1731
- }
1732
- comparisonPart += `${compileBinding(expr.operand2, true)}`;
1733
- if (isOperand2Complex) {
1734
- comparisonPart += ")";
1735
- }
1736
- return wrapBindingExpression(comparisonPart, embeddedInBinding);
1665
+ const comparisonExpression = compileComparisonExpression(expr);
1666
+ return wrapBindingExpression(comparisonExpression, embeddedInBinding);
1737
1667
 
1738
1668
  case "IfElse":
1739
1669
  const ifElseExpression = `${compileBinding(expr.condition, true)} ? ${compileBinding(expr.onTrue, true)} : ${compileBinding(
@@ -1763,53 +1693,48 @@ export function compileBinding<T extends PrimitiveType>(
1763
1693
  return wrapBindingExpression(truthyExpression, embeddedInBinding);
1764
1694
 
1765
1695
  case "Formatter":
1766
- if (expr.parameters.length === 1) {
1767
- outProperty += `{${compilePathParameter(expr.parameters[0], true)}, formatter: '${expr.fn}'}`;
1768
- } else {
1769
- outProperty += `{parts:[${expr.parameters.map((param: any) => compilePathParameter(param)).join(",")}], formatter: '${
1770
- expr.fn
1771
- }'}`;
1772
- }
1773
- if (embeddedInBinding) {
1774
- outProperty = `\$${outProperty}`;
1775
- }
1776
- return outProperty;
1696
+ const formatterExpression = compileFormatterExpression(expr);
1697
+ return embeddedInBinding ? `\$${formatterExpression}` : formatterExpression;
1698
+
1777
1699
  case "ComplexType":
1778
- if (expr.bindingParameters.length === 1) {
1779
- outProperty += `{${compilePathParameter(expr.bindingParameters[0], true)}, type: '${expr.type}'}`;
1780
- } else {
1781
- let outputEnd;
1782
- // this code is based on sap.ui.model.odata.v4._AnnotationHelperExpression.fetchCurrencyOrUnit
1783
- switch (expr.type) {
1784
- case "sap.ui.model.odata.type.Unit":
1785
- outputEnd = `,{mode:'OneTime',path:'/##@@requestUnitsOfMeasure',targetType:'any'}],type:'sap.ui.model.odata.type.Unit'`;
1786
- break;
1787
- case "sap.ui.model.odata.type.Currency":
1788
- outputEnd = `,{mode:'OneTime',path:'/##@@requestCurrencyCodes',targetType:'any'}],type:'sap.ui.model.odata.type.Currency'`;
1789
- break;
1790
- default:
1791
- outputEnd = `], type: '${expr.type}'`;
1792
- }
1793
- if (expr.formatOptions && Object.keys(expr.formatOptions).length > 0) {
1794
- outputEnd += `, formatOptions: ${compileBinding(expr.formatOptions)}`;
1795
- }
1796
- if (expr.parameters && Object.keys(expr.parameters).length > 0) {
1797
- outputEnd += `, parameters: ${compileBinding(expr.parameters)}`;
1798
- }
1799
- outputEnd += "}";
1800
- outProperty += `{mode:'TwoWay', parts:[${expr.bindingParameters
1801
- .map((param: any) => compilePathParameter(param))
1802
- .join(",")}${outputEnd}`;
1803
- }
1804
- if (embeddedInBinding) {
1805
- outProperty = `\$${outProperty}`;
1806
- }
1807
- return outProperty;
1700
+ const complexTypeExpression = compileComplexTypeExpression(expr);
1701
+ return embeddedInBinding ? `\$${complexTypeExpression}` : complexTypeExpression;
1702
+
1808
1703
  default:
1809
1704
  return "";
1810
1705
  }
1811
1706
  }
1812
1707
 
1708
+ /**
1709
+ * Compile a comparison expression.
1710
+ *
1711
+ * @param expression The comparison expression.
1712
+ * @returns The compiled expression. Needs wrapping before it can be used as an expression binding.
1713
+ */
1714
+ function compileComparisonExpression(expression: ComparisonExpression) {
1715
+ function compileOperand(operand: Expression<any>) {
1716
+ const compiledOperand = compileBinding(operand, true) ?? "undefined";
1717
+ return wrapBindingExpression(compiledOperand, true, needParenthesis(operand));
1718
+ }
1719
+
1720
+ return `${compileOperand(expression.operand1)} ${expression.operator} ${compileOperand(expression.operand2)}`;
1721
+ }
1722
+
1723
+ /**
1724
+ * Compile a formatter expression.
1725
+ *
1726
+ * @param expression The formatter expression.
1727
+ * @returns The compiled expression.
1728
+ */
1729
+ function compileFormatterExpression<T extends PrimitiveType>(expression: FormatterExpression<T>) {
1730
+ if (expression.parameters.length === 1) {
1731
+ return `{${compilePathParameter(expression.parameters[0], true)}, formatter: '${expression.fn}'}`;
1732
+ } else {
1733
+ const parts = expression.parameters.map(param => compilePathParameter(param));
1734
+ return `{parts: [${parts.join(", ")}], formatter: '${expression.fn}'}`;
1735
+ }
1736
+ }
1737
+
1813
1738
  /**
1814
1739
  * Compile the path parameter of a formatter call.
1815
1740
  *
@@ -1817,53 +1742,54 @@ export function compileBinding<T extends PrimitiveType>(
1817
1742
  * @param singlePath Whether there is one or multiple path to consider
1818
1743
  * @returns {string} The string snippet to include in the overall binding definition
1819
1744
  */
1820
- function compilePathParameter(expression: Expression<any>, singlePath: boolean = false): string {
1745
+ function compilePathParameter(expression: Expression<any>, singlePath = false): string {
1821
1746
  let outValue = "";
1822
- switch (expression._type) {
1823
- case "Constant":
1824
- switch (typeof expression.value) {
1825
- case "number":
1826
- case "bigint":
1827
- outValue = `value: ${expression.value.toString()}`;
1828
- break;
1829
- case "string":
1830
- outValue = `value: '${escapeXmlAttribute(expression.value.toString())}'`;
1831
- break;
1832
- case "boolean":
1833
- outValue = `value: '${expression.value.toString()}'`;
1834
- break;
1835
- default:
1836
- outValue = "value: ''";
1837
- break;
1838
- }
1839
- if (singlePath) {
1840
- return outValue;
1841
- }
1842
- return `{${outValue}}`;
1747
+ if (expression._type === "Constant") {
1748
+ switch (typeof expression.value) {
1749
+ case "number":
1750
+ case "bigint":
1751
+ outValue = `value: ${expression.value.toString()}`;
1752
+ break;
1753
+ case "string":
1754
+ outValue = `value: '${escapeXmlAttribute(expression.value.toString())}'`;
1755
+ break;
1756
+ case "boolean":
1757
+ outValue = `value: '${expression.value.toString()}'`;
1758
+ break;
1759
+ default:
1760
+ outValue = "value: ''";
1761
+ break;
1762
+ }
1763
+ } else if (expression._type === "DefaultBinding" || expression._type === "Binding") {
1764
+ outValue = `path: '${compileBindingPath(expression)}'`;
1843
1765
 
1844
- case "DefaultBinding":
1845
- case "Binding":
1846
- outValue = `path:'${expression.modelName ? `${expression.modelName}>` : ""}${expression.path}'`;
1766
+ outValue += expression.type ? `, type: '${expression.type}'` : `, targetType: 'any'`;
1847
1767
 
1848
- if (expression.type) {
1849
- outValue += `, type : '${expression.type}'`;
1850
- } else {
1851
- outValue += `, targetType : 'any'`;
1852
- }
1853
- if (expression.constraints && Object.keys(expression.constraints).length > 0) {
1854
- outValue += `, constraints: ${compileBinding(expression.constraints)}`;
1855
- }
1856
- if (expression.formatOptions && Object.keys(expression.formatOptions).length > 0) {
1857
- outValue += `, formatOptions: ${compileBinding(expression.formatOptions)}`;
1858
- }
1859
- if (expression.parameters && Object.keys(expression.parameters).length > 0) {
1860
- outValue += `, parameters: ${compileBinding(expression.parameters)}`;
1861
- }
1862
- if (singlePath) {
1863
- return outValue;
1864
- }
1865
- return `{${outValue}}`;
1866
- default:
1867
- return "";
1768
+ if (hasElements(expression.constraints)) {
1769
+ outValue += `, constraints: ${compileBinding(expression.constraints)}`;
1770
+ }
1771
+ if (hasElements(expression.formatOptions)) {
1772
+ outValue += `, formatOptions: ${compileBinding(expression.formatOptions)}`;
1773
+ }
1774
+ if (hasElements(expression.parameters)) {
1775
+ outValue += `, parameters: ${compileBinding(expression.parameters)}`;
1776
+ }
1777
+ } else {
1778
+ return "";
1868
1779
  }
1780
+ return singlePath ? outValue : `{${outValue}}`;
1781
+ }
1782
+
1783
+ function hasElements(obj: any) {
1784
+ return obj && Object.keys(obj).length > 0;
1785
+ }
1786
+
1787
+ /**
1788
+ * Compile a binding expression path.
1789
+ *
1790
+ * @param expression The expression to compile.
1791
+ * @returns The compiled path.
1792
+ */
1793
+ function compileBindingPath<T extends PrimitiveType>(expression: BindingExpressionExpression<T> | DefaultBindingExpressionExpression<T>) {
1794
+ return `${expression.modelName ? expression.modelName + ">" : ""}${expression.path}`;
1869
1795
  }