@sap/cds-compiler 4.3.2 → 4.4.2

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 (81) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/lib/api/main.js +14 -24
  3. package/lib/api/options.js +1 -0
  4. package/lib/api/trace.js +38 -0
  5. package/lib/base/location.js +46 -1
  6. package/lib/base/message-registry.js +68 -16
  7. package/lib/base/messages.js +8 -3
  8. package/lib/checks/.eslintrc.json +1 -0
  9. package/lib/checks/actionsFunctions.js +1 -1
  10. package/lib/checks/annotationsOData.js +2 -2
  11. package/lib/checks/selectItems.js +4 -1
  12. package/lib/compiler/assert-consistency.js +3 -2
  13. package/lib/compiler/base.js +1 -1
  14. package/lib/compiler/builtins.js +25 -1
  15. package/lib/compiler/checks.js +6 -5
  16. package/lib/compiler/define.js +12 -10
  17. package/lib/compiler/extend.js +44 -23
  18. package/lib/compiler/finalize-parse-cdl.js +1 -1
  19. package/lib/compiler/generate.js +70 -53
  20. package/lib/compiler/kick-start.js +7 -5
  21. package/lib/compiler/populate.js +31 -22
  22. package/lib/compiler/propagator.js +6 -2
  23. package/lib/compiler/resolve.js +52 -17
  24. package/lib/compiler/shared.js +85 -39
  25. package/lib/compiler/tweak-assocs.js +64 -23
  26. package/lib/compiler/utils.js +40 -23
  27. package/lib/edm/.eslintrc.json +2 -0
  28. package/lib/edm/EdmPrimitiveTypeDefinitions.js +260 -0
  29. package/lib/edm/annotations/edmJson.js +994 -0
  30. package/lib/edm/annotations/genericTranslation.js +82 -423
  31. package/lib/edm/annotations/vocabularyDefinitions.js +160 -0
  32. package/lib/edm/csn2edm.js +12 -5
  33. package/lib/edm/edm.js +14 -73
  34. package/lib/edm/edmPreprocessor.js +6 -0
  35. package/lib/gen/Dictionary.json +187 -16
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +1 -1
  38. package/lib/gen/languageLexer.interp +1 -1
  39. package/lib/gen/languageLexer.js +1129 -671
  40. package/lib/gen/languageParser.js +4285 -4283
  41. package/lib/json/from-csn.js +13 -18
  42. package/lib/json/to-csn.js +11 -6
  43. package/lib/language/antlrParser.js +0 -1
  44. package/lib/language/docCommentParser.js +1 -1
  45. package/lib/language/errorStrategy.js +95 -30
  46. package/lib/language/genericAntlrParser.js +21 -1
  47. package/lib/main.js +13 -3
  48. package/lib/model/csnRefs.js +42 -8
  49. package/lib/model/csnUtils.js +14 -2
  50. package/lib/model/enrichCsn.js +33 -5
  51. package/lib/model/revealInternalProperties.js +5 -0
  52. package/lib/modelCompare/compare.js +76 -14
  53. package/lib/modelCompare/utils/filter.js +19 -12
  54. package/lib/optionProcessor.js +2 -0
  55. package/lib/render/.eslintrc.json +1 -1
  56. package/lib/render/manageConstraints.js +1 -0
  57. package/lib/render/toHdbcds.js +3 -0
  58. package/lib/render/toRename.js +3 -1
  59. package/lib/render/toSql.js +46 -92
  60. package/lib/render/utils/common.js +76 -0
  61. package/lib/render/utils/delta.js +17 -3
  62. package/lib/sql-identifier.js +1 -1
  63. package/lib/transform/db/.eslintrc.json +1 -0
  64. package/lib/transform/db/applyTransformations.js +30 -4
  65. package/lib/transform/db/associations.js +22 -10
  66. package/lib/transform/db/backlinks.js +6 -2
  67. package/lib/transform/db/expansion.js +2 -2
  68. package/lib/transform/db/transformExists.js +13 -39
  69. package/lib/transform/draft/db.js +14 -3
  70. package/lib/transform/draft/odata.js +5 -18
  71. package/lib/transform/effective/associations.js +46 -15
  72. package/lib/transform/effective/main.js +7 -2
  73. package/lib/transform/effective/misc.js +43 -24
  74. package/lib/transform/effective/queries.js +20 -22
  75. package/lib/transform/effective/types.js +6 -2
  76. package/lib/transform/forOdata.js +10 -3
  77. package/lib/transform/localized.js +1 -1
  78. package/lib/transform/parseExpr.js +73 -21
  79. package/lib/transform/translateAssocsToJoins.js +22 -15
  80. package/lib/utils/term.js +2 -2
  81. package/package.json +2 -1
@@ -5,166 +5,11 @@ const edmUtils = require('../edmUtils.js');
5
5
  const oDataDictionary = require('../../gen/Dictionary.json');
6
6
  const preprocessAnnotations = require('./preprocessAnnotations.js');
7
7
  const { forEachDefinition } = require('../../model/csnUtils');
8
- const { forEach } = require('../../utils/objectUtils');
9
8
  const { isBetaEnabled, setProp } = require('../../base/model.js');
9
+ const { xpr2edmJson, getEdmJsonHandler } = require('./edmJson.js');
10
+ const { vocabularyDefinitions } = require('./vocabularyDefinitions.js');
11
+ const { EdmPathTypeMap } = require('../EdmPrimitiveTypeDefinitions.js');
10
12
 
11
- /*
12
- OASIS: https://github.com/oasis-tcs/odata-vocabularies/tree/master/vocabularies
13
- Aggregation (published)
14
- Authorization (published)
15
- Capabilities (published)
16
- Core (published)
17
- JSON (published)
18
- Measures (published)
19
- Repeatability (published)
20
- Temporal (published)
21
- Validation (published)
22
-
23
- SAP: https://github.com/SAP/odata-vocabularies/tree/master/vocabularies
24
- Analytics (published)
25
- CodeList (published)
26
- Common (published)
27
- Communication (published)
28
- DataIntegration (published)
29
- Graph (published, experimental)
30
- Hierarchy (published, experimental)
31
- HTML5 (published, experimental)
32
- ODM (published, experimental)
33
- Offline (experimental)
34
- PDF (published)
35
- PersonalData (published)
36
- Session (published)
37
- UI (published)
38
- */
39
-
40
- const vocabularyDefinitions = {
41
- Aggregation: {
42
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Aggregation.V1.xml' },
43
- inc: { Alias: 'Aggregation', Namespace: 'Org.OData.Aggregation.V1' },
44
- int: { filename: 'Aggregation.xml' },
45
- },
46
- Analytics: {
47
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Analytics.xml' },
48
- inc: { Alias: 'Analytics', Namespace: 'com.sap.vocabularies.Analytics.v1' },
49
- int: { filename: 'Analytics.xml' },
50
- },
51
- Authorization: {
52
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Authorization.V1.xml' },
53
- inc: { Alias: 'Authorization', Namespace: 'Org.OData.Authorization.V1' },
54
- int: { filename: 'Authorization.xml' },
55
- },
56
- Capabilities: {
57
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Capabilities.V1.xml' },
58
- inc: { Alias: 'Capabilities', Namespace: 'Org.OData.Capabilities.V1' },
59
- int: { filename: 'Capabilities.xml' },
60
- },
61
- CodeList: {
62
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/CodeList.xml' },
63
- inc: { Alias: 'CodeList', Namespace: 'com.sap.vocabularies.CodeList.v1' },
64
- int: { filename: 'CodeList.xml' },
65
- },
66
- Common: {
67
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Common.xml' },
68
- inc: { Alias: 'Common', Namespace: 'com.sap.vocabularies.Common.v1' },
69
- int: { filename: 'Common.xml' },
70
- },
71
- Communication: {
72
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Communication.xml' },
73
- inc: { Alias: 'Communication', Namespace: 'com.sap.vocabularies.Communication.v1' },
74
- int: { filename: 'Communication.xml' },
75
- },
76
- Core: {
77
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml' },
78
- inc: { Alias: 'Core', Namespace: 'Org.OData.Core.V1' },
79
- int: { filename: 'Core.xml' },
80
- },
81
- DataIntegration: {
82
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/DataIntegration.xml' },
83
- inc: { Alias: 'DataIntegration', Namespace: 'com.sap.vocabularies.DataIntegration.v1' },
84
- int: { filename: 'DataIntegration.xml' },
85
- },
86
- Graph: {
87
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Graph.xml' },
88
- inc: { Alias: 'Graph', Namespace: 'com.sap.vocabularies.Graph.v1' },
89
- int: { filename: 'Graph.xml' },
90
- },
91
- Hierarchy: {
92
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Hierarchy.xml' },
93
- inc: { Alias: 'Hierarchy', Namespace: 'com.sap.vocabularies.Hierarchy.v1' },
94
- int: { filename: 'Hierarchy.xml' },
95
- },
96
- HTML5: {
97
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/HTML5.xml' },
98
- inc: { Alias: 'HTML5', Namespace: 'com.sap.vocabularies.HTML5.v1' },
99
- int: { filename: 'HTML5.xml' },
100
- },
101
- JSON: {
102
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.JSON.V1.xml' },
103
- inc: { Alias: 'JSON', Namespace: 'Org.OData.JSON.V1' },
104
- int: { filename: 'JSON.xml' },
105
- },
106
- Measures: {
107
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Measures.V1.xml' },
108
- inc: { Alias: 'Measures', Namespace: 'Org.OData.Measures.V1' },
109
- int: { filename: 'Measures.xml' },
110
- },
111
- ODM: {
112
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/ODM.xml' },
113
- inc: { Alias: 'ODM', Namespace: 'com.sap.vocabularies.ODM.v1' },
114
- int: { filename: 'ODM.xml' },
115
- },
116
- Offline: {
117
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Offline.xml' },
118
- inc: { Alias: 'Offline', Namespace: 'com.sap.vocabularies.Offline.v1' },
119
- int: { filename: 'Offline.xml' },
120
- },
121
- PDF: {
122
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/PDF.xml' },
123
- inc: { Alias: 'PDF', Namespace: 'com.sap.vocabularies.PDF.v1' },
124
- int: { filename: 'PDF.xml' },
125
- },
126
- PersonalData: {
127
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/PersonalData.xml' },
128
- inc: { Alias: 'PersonalData', Namespace: 'com.sap.vocabularies.PersonalData.v1' },
129
- int: { filename: 'PersonalData.xml' },
130
- },
131
- Repeatability: {
132
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Repeatability.V1.xml' },
133
- inc: { Alias: 'Repeatability', Namespace: 'Org.OData.Repeatability.V1' },
134
- int: { filename: 'Repeatability.xml' },
135
- },
136
- Session: {
137
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/Session.xml' },
138
- inc: { Alias: 'Session', Namespace: 'com.sap.vocabularies.Session.v1' },
139
- int: { filename: 'Session.xml' },
140
- },
141
- UI: {
142
- ref: { Uri: 'https://sap.github.io/odata-vocabularies/vocabularies/UI.xml' },
143
- inc: { Alias: 'UI', Namespace: 'com.sap.vocabularies.UI.v1' },
144
- int: { filename: 'UI.xml' },
145
- },
146
- Validation: {
147
- ref: { Uri: 'https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Validation.V1.xml' },
148
- inc: { Alias: 'Validation', Namespace: 'Org.OData.Validation.V1' },
149
- int: { filename: 'Validation.xml' },
150
- },
151
- /* unvalidated vocabularies below here:
152
- A vocabulary is unvalidated if it doesn't have an int.filename property as this indicates that
153
- the vocabulary is added to the validation dictionary
154
- Example:
155
- 'Org.Snafu.V1': {
156
- 'ref': { Uri: 'https://snafu.org/snafu.xml' },
157
- 'inc': { Alias: 'Snafu', Namespace: 'Org.Snafu.V1' },
158
- },
159
- */
160
- };
161
-
162
- /* create inverted voc definitions list to allow addressing full qualified vocabularies
163
- Object.entries(vocabularyDefinitions).forEach(([n, v]) => {
164
- if(!vocabularyDefinitions[v.inc.Namespace])
165
- vocabularyDefinitions[v.inc.Namespace] = vocabularyDefinitions[n];
166
- });
167
- */
168
13
  /** ************************************************************************************************
169
14
  * csn2annotationEdm
170
15
  *
@@ -174,20 +19,20 @@ Object.entries(vocabularyDefinitions).forEach(([n, v]) => {
174
19
  */
175
20
  function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
176
21
  Edm = undefined, options = undefined, messageFunctions = undefined, mergedVocDefs = vocabularyDefinitions ) {
177
- // global variable where we store all the generated annotations
178
- const gAnnosArray = [];
22
+ const gAnnosArray = []; // global variable where we store all the generated annotations
23
+ const usedExperimentalTerms = {}; // take note of all experimental annos that have been used
24
+ const usedDeprecatedTerms = {}; // take note of all deprecated annos that have been used
179
25
 
26
+ const { v } = options;
180
27
  const { message } = messageFunctions;
181
-
28
+ const { handleEdmJson } = getEdmJsonHandler(Edm, options, messageFunctions, handleTerm);
182
29
  const [ userDefinedTermDict, allKnownVocabularies ] = createUserDefinedTermDictionary();
183
30
 
31
+
184
32
  allKnownVocabularies.push(...Object.keys(mergedVocDefs));
185
33
  allKnownVocabularies.sort((a, b) => b.length - a.length);
186
34
  const whatsMyTermNamespace = anno => allKnownVocabularies.reduce((rc, ns) => (!rc && anno && anno.startsWith(`@${ns}.`) ? ns : rc), undefined);
187
35
 
188
- // Static dynamic expression dictionary, loaded with Edm creators
189
- const [ dynamicExpressions, dynamicExpressionNames ] = initEdmJson();
190
-
191
36
  // annotation preprocessing
192
37
  preprocessAnnotations.preprocessAnnotations(reqDefs, serviceName, options);
193
38
 
@@ -209,63 +54,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
209
54
  { name: serviceName, '#': 'service' } );
210
55
  }
211
56
 
212
- // provide functions for dictionary lookup
213
- // use closure to avoid making "dict" and "experimental" global variables
214
- const { getDictTerm, getDictType } = (function createDictGetters() {
215
- const dict = options.dictReplacement || oDataDictionary; // tests can set different dictionary via options
216
- const experimental = {}; // take note of all experimental annos that have been used
217
- const deprecated = {}; // take note of all deprecated annos that have been used
218
-
219
- return {
220
- // called to look-up a term in the dictionary
221
- // in addition: - note usage of the respective vocabulary
222
- // - issue a warning if the term is flagged as "experimental"
223
- getDictTerm(termName, msg) {
224
- const dictTerm = (dict.terms[termName] ||
225
- userDefinedTermDict.terms[`${serviceName}.${termName}`] ||
226
- userDefinedTermDict.terms[termName]);
227
- // register vocabulary usage if possible
228
- const vocName = termName.slice(0, termName.indexOf('.'));
229
- const myVocDef = mergedVocDefs[vocName];
230
- if (myVocDef && !myVocDef.$ignore)
231
- myVocDef.used = true;
232
- else if (dictTerm?.$myServiceRoot &&
233
- userDefinedTermDict.xrefs[dictTerm?.$myServiceRoot])
234
- userDefinedTermDict.xrefs[dictTerm.$myServiceRoot].used = true;
235
- if (dictTerm) {
236
- // issue message for usage of experimental Terms, but only once per Term
237
- if (dictTerm.$experimental && !experimental[termName]) {
238
- message('odata-anno-dict', msg.location, { anno: msg.anno(), '#': 'experimental' });
239
- experimental[termName] = true;
240
- }
241
- if (dictTerm.$deprecated && !deprecated[termName]) {
242
- message('odata-anno-def', msg.location,
243
- { anno: msg.anno(), depr: dictTerm.$deprecationText, '#': 'deprecated' });
244
- deprecated[termName] = true;
245
- }
246
- }
247
- return dictTerm;
248
- },
249
- // called to look-up a type in the dictionary
250
- // in addition, note usage of the respective vocabulary
251
- getDictType(typeName) {
252
- const dictType = (dict.types[typeName] ||
253
- userDefinedTermDict.types[`${serviceName}.${typeName}`] ||
254
- userDefinedTermDict.types[typeName]);
255
- if (dictType) {
256
- // register usage of vocabulary
257
- const vocName = typeName.slice(0, typeName.indexOf('.'));
258
- const myVocDef = mergedVocDefs[vocName];
259
- if (myVocDef && !myVocDef.$ignore)
260
- myVocDef.used = true;
261
- }
262
- return dictType;
263
- },
264
- };
265
- }());
266
-
267
- const { v } = options;
268
-
269
57
  // Copy annotations from origin to parameter entity if it's
270
58
  // qualified with #$parameters or if its applicable to an EntitySet or Singleton
271
59
  forEachDefinition(reqDefs, (object) => {
@@ -514,6 +302,12 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
514
302
  if (knownAnnos.length === 0)
515
303
  return;
516
304
  }
305
+ if (isBetaEnabled(options, 'annotationExpressions')) {
306
+ knownAnnos.forEach((knownAnno) => {
307
+ if (knownAnno.search(/\.\$edmJson\./g) < 0)
308
+ carrier[knownAnno] = xpr2edmJson(carrier, knownAnno, location, options, messageFunctions);
309
+ });
310
+ }
517
311
 
518
312
  const prefixTree = createPrefixTree();
519
313
 
@@ -803,13 +597,18 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
803
597
  steps.splice(i, steps.length - i, steps.slice(i).join('.'));
804
598
  }
805
599
  if (innerAnnotation) {
806
- // A voc annotation has two steps (Namespace+Name),
807
- // any further steps need to be rendered separately
808
- const innerAnnoSteps = innerAnnotation.split('.');
809
- const tailSteps = innerAnnoSteps.splice(2, innerAnnoSteps.length - 2);
810
- // prepend annotation prefix (path) to tail steps
811
- tailSteps.splice(0, 0, `@${innerAnnoSteps.join('.')}`);
812
- steps.push(...tailSteps);
600
+ // A voc annotation has two steps (Namespace+Name),
601
+ // any further steps need to be rendered separately
602
+ if (innerAnnotation.startsWith('sap.')) {
603
+ steps.push(`@${innerAnnotation}`);
604
+ }
605
+ else {
606
+ const innerAnnoSteps = innerAnnotation.split('.');
607
+ const tailSteps = innerAnnoSteps.splice(2, innerAnnoSteps.length - 2);
608
+ // prepend annotation prefix (path) to tail steps
609
+ tailSteps.splice(0, 0, `@${innerAnnoSteps.join('.')}`);
610
+ steps.push(...tailSteps);
611
+ }
813
612
  }
814
613
  mergePathStepsIntoPrefixTree(prefixTreeP, steps, 0);
815
614
  }
@@ -1142,8 +941,12 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
1142
941
  // note: expr can also be provided if an enum/complex type/collection is expected
1143
942
  function handleExpression( value, dTypeName ) {
1144
943
  let typeName = 'Path';
1145
- if ( [ 'Edm.AnnotationPath', 'Edm.ModelElementPath', 'Edm.NavigationPropertyPath', 'Edm.PropertyPath', 'Edm.Path' ].includes(dTypeName) )
1146
- typeName = dTypeName.split('.')[1];
944
+ if ( EdmPathTypeMap[dTypeName] ) {
945
+ if (dTypeName === 'Edm.AnyPropertyPath')
946
+ typeName = 'PropertyPath';
947
+ else
948
+ typeName = dTypeName.split('.')[1];
949
+ }
1147
950
 
1148
951
  if (value) {
1149
952
  // replace all occurrences of '.' by '/' up to first '@'
@@ -1496,142 +1299,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
1496
1299
  }
1497
1300
 
1498
1301
 
1499
- // Not everything that can occur in OData annotations can be expressed with
1500
- // corresponding constructs in cds annotations. For these special cases
1501
- // we have a kind of "inline assembler" mode, i.e. you can in cds provide
1502
- // as annotation value a json snippet that looks like the final edm-json.
1503
- // See example in test/odataAnnotations/smallTests/edmJson_noReverse_ok
1504
- // and test3/ODataBackends/DynExpr
1505
-
1506
- function handleEdmJson( obj, msg, exprDef = undefined ) {
1507
- let edmNode;
1508
- if (obj == null)
1509
- return edmNode;
1510
-
1511
- const dynExprs = edmUtils.intersect(dynamicExpressionNames, Object.keys(obj));
1512
-
1513
- if (dynExprs.length > 1) {
1514
- message('odata-anno-value', msg.location,
1515
- { anno: msg.anno(), rawvalues: dynExprs, '#': 'multexpr' });
1516
- return edmNode;
1517
- }
1518
-
1519
- if (dynExprs.length === 0) {
1520
- if (typeof obj === 'object' && !Array.isArray(obj) && Object.keys(obj).length === 1) {
1521
- const k = Object.keys(obj)[0];
1522
- const val = obj[k];
1523
- edmNode = new Edm.ValueThing(v, k[0] === '$' ? k.slice(1) : k, val );
1524
- edmNode.setJSON( { [edmNode.kind]: val } );
1525
- }
1526
- // This thing is either a record or a collection or a literal
1527
- else if (Array.isArray(obj)) {
1528
- // EDM JSON doesn't mention annotations on collections
1529
- edmNode = new Edm.Collection(v);
1530
- obj.forEach(o => edmNode.append(handleEdmJson(o, msg)));
1531
- }
1532
- else if (typeof obj === 'object') {
1533
- edmNode = new Edm.Record(v);
1534
- const annos = Object.create(null);
1535
- const props = Object.create(null);
1536
- Object.entries(obj).forEach(([ k, val ]) => {
1537
- if (k === '@type') {
1538
- edmNode.setJSON({ Type: val });
1539
- // try to shorten full qualified type URI to short type name
1540
- const parts = val.split('#');
1541
- const shortTypeName = parts[parts.length - 1];
1542
- edmNode.setXml({ Type: shortTypeName });
1543
- }
1544
- else {
1545
- let child;
1546
- const [ head, tail ] = k.split('@');
1547
- if (tail) {
1548
- child = handleTerm(tail, val, msg);
1549
- }
1550
- else {
1551
- child = new Edm.PropertyValue(v, head);
1552
- child.append(handleEdmJson(val, msg));
1553
- }
1554
- if (child) {
1555
- if (tail && head.length) {
1556
- if (!annos[head])
1557
- annos[head] = [ child ];
1558
- else
1559
- annos[head].push(child);
1560
- }
1561
- else {
1562
- if (head.length)
1563
- props[head] = child;
1564
- edmNode.append(child);
1565
- }
1566
- }
1567
- }
1568
- });
1569
- // add collected annotations to record members
1570
- Object.entries(annos).forEach(([ n, val ]) => {
1571
- if (props[n])
1572
- props[n].prepend(...val);
1573
- });
1574
- }
1575
- else { // literal
1576
- edmNode = new Edm.ValueThing(v,
1577
- exprDef && exprDef.valueThingName || getXmlTypeName(obj), obj);
1578
- // typename for static expression rendering
1579
- edmNode.setJSON( { [getJsonTypeName(obj)]: obj } );
1580
- }
1581
- }
1582
- else {
1583
- // name of special property determines element kind
1584
- exprDef = dynamicExpressions[dynExprs[0]];
1585
- edmNode = exprDef.create(obj);
1586
-
1587
- // iterate over each obj.property and translate expression into EDM
1588
- forEach(obj, (name, val) => {
1589
- if (exprDef) {
1590
- if (exprDef.anno && name[0] === '@') {
1591
- edmNode.append(handleTerm(name.slice(1), val, msg));
1592
- }
1593
- else if (exprDef.attr && exprDef.attr.includes(name)) {
1594
- if (name[0] === '$')
1595
- edmNode.setEdmAttribute(name.slice(1), val);
1596
- }
1597
- else if (exprDef.jsonAttr && exprDef.jsonAttr.includes(name)) {
1598
- if (name[0] === '$')
1599
- edmNode.setJSON( { [name.slice(1)]: val });
1600
- }
1601
- else if (exprDef.children) {
1602
- if (Array.isArray(val)) {
1603
- val.forEach((a) => {
1604
- edmNode.append(handleEdmJson(a, msg, exprDef));
1605
- });
1606
- }
1607
- else {
1608
- edmNode.append(handleEdmJson(val, msg, exprDef));
1609
- }
1610
- }
1611
- }
1612
- });
1613
- }
1614
- return edmNode;
1615
-
1616
- function getXmlTypeName( val ) {
1617
- let typeName = 'String';
1618
- if (typeof val === 'boolean')
1619
- typeName = 'Bool';
1620
-
1621
- else if (typeof val === 'number')
1622
- typeName = Number.isInteger(val) ? 'Int' : 'Decimal';
1623
-
1624
- return typeName;
1625
- }
1626
-
1627
- function getJsonTypeName( val ) {
1628
- const typeName = getXmlTypeName(val);
1629
- if (typeName === 'Int')
1630
- return 'Edm.Int32';
1631
- return `Edm.${typeName}`;
1632
- }
1633
- }
1634
-
1635
1302
  /**
1636
1303
  * translate vocabulary definitions into a userDefinedTermDict
1637
1304
  * with the same structure as the global jsonDictionary that
@@ -1763,65 +1430,57 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
1763
1430
  }
1764
1431
  }
1765
1432
 
1766
- function initEdmJson() {
1767
- // Static dynamic expression dictionary, loaded with Edm creators
1768
- const dynamicExpressionsP = {
1769
- $And: { create: () => new Edm.Expr(v, 'And'), anno: true },
1770
- $Or: { create: () => new Edm.Expr(v, 'Or'), anno: true },
1771
- $Not: { create: () => new Edm.Expr(v, 'Not'), anno: true },
1772
- $Eq: { create: () => new Edm.Expr(v, 'Eq'), anno: true },
1773
- $Ne: { create: () => new Edm.Expr(v, 'Ne'), anno: true },
1774
- $Gt: { create: () => new Edm.Expr(v, 'Gt'), anno: true },
1775
- $Ge: { create: () => new Edm.Expr(v, 'Ge'), anno: true },
1776
- $Lt: { create: () => new Edm.Expr(v, 'Lt'), anno: true },
1777
- $Le: { create: () => new Edm.Expr(v, 'Le'), anno: true },
1778
- // valueThingName: 'EnumMember' Implicit Cast Rule String => Primitive Type is OK
1779
- $Has: { create: () => new Edm.Expr(v, 'Has'), anno: true },
1780
- $In: { create: () => new Edm.Expr(v, 'In'), anno: true },
1781
- $Add: { create: () => new Edm.Expr(v, 'Add'), anno: true },
1782
- $Sub: { create: () => new Edm.Expr(v, 'Sub'), anno: true },
1783
- $Neg: { create: () => new Edm.Expr(v, 'Neg'), anno: true },
1784
- $Mul: { create: () => new Edm.Expr(v, 'Mul'), anno: true },
1785
- $Div: { create: () => new Edm.Expr(v, 'Div'), anno: true },
1786
- $DivBy: { create: () => new Edm.Expr(v, 'DivBy'), anno: true },
1787
- $Mod: { create: () => new Edm.Expr(v, 'Mod'), anno: true },
1788
- $Apply: {
1789
- create: () => new Edm.Apply(v),
1790
- attr: [ '$Function' ],
1791
- anno: true,
1792
- },
1793
- $Cast: {
1794
- create: () => new Edm.Cast(v),
1795
- attr: [ '$Type' ],
1796
- jsonAttr: [ '$Collection' ],
1797
- anno: true,
1798
- },
1799
- $IsOf: {
1800
- create: () => new Edm.IsOf(v),
1801
- attr: [ '$Type' ],
1802
- anno: true,
1803
- },
1804
- $If: { create: () => new Edm.If(v), anno: true },
1805
- $LabeledElement: {
1806
- create: () => new Edm.LabeledElement(v),
1807
- attr: [ '$Name' ],
1808
- anno: true,
1809
- },
1810
- $LabeledElementReference: {
1811
- create: obj => new Edm.LabeledElementReference(v, obj.$LabeledElementReference),
1812
- },
1813
- $UrlRef: { create: () => new Edm.UrlRef(v), anno: true },
1814
- $Null: { create: () => new Edm.Null(v), anno: true, children: false },
1815
- };
1433
+ //-------------------------------------------------------------------------------------------------
1434
+ // Dictionary access
1435
+ //-------------------------------------------------------------------------------------------------
1816
1436
 
1817
- Object.entries(dynamicExpressionsP).forEach(([ k, dv ]) => {
1818
- if (!dv.name)
1819
- dv.name = k.slice(1);
1820
- if (dv.children === undefined)
1821
- dv.children = true;
1822
- });
1823
- return [ dynamicExpressionsP, Object.keys(dynamicExpressionsP) ];
1437
+ // called to look-up a term in the dictionary
1438
+ // in addition: - note usage of the respective vocabulary
1439
+ // - issue a warning if the term is flagged as "experimental"
1440
+ function getDictTerm( termName, msg ) {
1441
+ const dict = options.dictReplacement || oDataDictionary; // tests can set different dictionary via options
1442
+ const dictTerm = (dict.terms[termName] ||
1443
+ userDefinedTermDict.terms[`${serviceName}.${termName}`] ||
1444
+ userDefinedTermDict.terms[termName]);
1445
+ // register vocabulary usage if possible
1446
+ const vocName = termName.slice(0, termName.indexOf('.'));
1447
+ const myVocDef = mergedVocDefs[vocName];
1448
+ if (myVocDef && !myVocDef.$ignore)
1449
+ myVocDef.used = true;
1450
+ else if (dictTerm?.$myServiceRoot &&
1451
+ userDefinedTermDict.xrefs[dictTerm?.$myServiceRoot])
1452
+ userDefinedTermDict.xrefs[dictTerm.$myServiceRoot].used = true;
1453
+ if (dictTerm) {
1454
+ // issue message for usage of experimental Terms, but only once per Term
1455
+ if (dictTerm.$experimental && !usedExperimentalTerms[termName]) {
1456
+ message('odata-anno-dict', msg.location, { anno: msg.anno(), '#': 'experimental' });
1457
+ usedExperimentalTerms[termName] = true;
1458
+ }
1459
+ if (dictTerm.$deprecated && !usedDeprecatedTerms[termName]) {
1460
+ message('odata-anno-def', msg.location,
1461
+ { anno: msg.anno(), depr: dictTerm.$deprecationText, '#': 'deprecated' });
1462
+ usedDeprecatedTerms[termName] = true;
1463
+ }
1464
+ }
1465
+ return dictTerm;
1824
1466
  }
1467
+ // called to look-up a type in the dictionary
1468
+ // in addition, note usage of the respective vocabulary
1469
+ function getDictType( typeName ) {
1470
+ const dict = options.dictReplacement || oDataDictionary; // tests can set different dictionary via options
1471
+ const dictType = (dict.types[typeName] ||
1472
+ userDefinedTermDict.types[`${serviceName}.${typeName}`] ||
1473
+ userDefinedTermDict.types[typeName]);
1474
+ if (dictType) {
1475
+ // register usage of vocabulary
1476
+ const vocName = typeName.slice(0, typeName.indexOf('.'));
1477
+ const myVocDef = mergedVocDefs[vocName];
1478
+ if (myVocDef && !myVocDef.$ignore)
1479
+ myVocDef.used = true;
1480
+ }
1481
+ return dictType;
1482
+ }
1483
+
1825
1484
  //-------------------------------------------------------------------------------------------------
1826
1485
  //-------------------------------------------------------------------------------------------------
1827
1486
  //-------------------------------------------------------------------------------------------------