@sap/cds-compiler 4.3.2 → 4.4.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 (81) hide show
  1. package/CHANGELOG.md +29 -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 +22 -22
  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 +74 -38
  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 +252 -0
  29. package/lib/edm/annotations/edmJson.js +994 -0
  30. package/lib/edm/annotations/genericTranslation.js +75 -421
  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 +5 -2
  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,10 @@ 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');
10
11
 
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
12
  /** ************************************************************************************************
169
13
  * csn2annotationEdm
170
14
  *
@@ -174,20 +18,20 @@ Object.entries(vocabularyDefinitions).forEach(([n, v]) => {
174
18
  */
175
19
  function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
176
20
  Edm = undefined, options = undefined, messageFunctions = undefined, mergedVocDefs = vocabularyDefinitions ) {
177
- // global variable where we store all the generated annotations
178
- const gAnnosArray = [];
21
+ const gAnnosArray = []; // global variable where we store all the generated annotations
22
+ const usedExperimentalTerms = {}; // take note of all experimental annos that have been used
23
+ const usedDeprecatedTerms = {}; // take note of all deprecated annos that have been used
179
24
 
25
+ const { v } = options;
180
26
  const { message } = messageFunctions;
181
-
27
+ const { handleEdmJson } = getEdmJsonHandler(Edm, options, messageFunctions, handleTerm);
182
28
  const [ userDefinedTermDict, allKnownVocabularies ] = createUserDefinedTermDictionary();
183
29
 
30
+
184
31
  allKnownVocabularies.push(...Object.keys(mergedVocDefs));
185
32
  allKnownVocabularies.sort((a, b) => b.length - a.length);
186
33
  const whatsMyTermNamespace = anno => allKnownVocabularies.reduce((rc, ns) => (!rc && anno && anno.startsWith(`@${ns}.`) ? ns : rc), undefined);
187
34
 
188
- // Static dynamic expression dictionary, loaded with Edm creators
189
- const [ dynamicExpressions, dynamicExpressionNames ] = initEdmJson();
190
-
191
35
  // annotation preprocessing
192
36
  preprocessAnnotations.preprocessAnnotations(reqDefs, serviceName, options);
193
37
 
@@ -209,63 +53,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
209
53
  { name: serviceName, '#': 'service' } );
210
54
  }
211
55
 
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
56
  // Copy annotations from origin to parameter entity if it's
270
57
  // qualified with #$parameters or if its applicable to an EntitySet or Singleton
271
58
  forEachDefinition(reqDefs, (object) => {
@@ -514,6 +301,12 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
514
301
  if (knownAnnos.length === 0)
515
302
  return;
516
303
  }
304
+ if (isBetaEnabled(options, 'annotationExpressions')) {
305
+ knownAnnos.forEach((knownAnno) => {
306
+ if (knownAnno.search(/\.\$edmJson\./g) < 0)
307
+ carrier[knownAnno] = xpr2edmJson(carrier, knownAnno, location, options, messageFunctions);
308
+ });
309
+ }
517
310
 
518
311
  const prefixTree = createPrefixTree();
519
312
 
@@ -803,13 +596,18 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
803
596
  steps.splice(i, steps.length - i, steps.slice(i).join('.'));
804
597
  }
805
598
  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);
599
+ // A voc annotation has two steps (Namespace+Name),
600
+ // any further steps need to be rendered separately
601
+ if (innerAnnotation.startsWith('sap.')) {
602
+ steps.push(`@${innerAnnotation}`);
603
+ }
604
+ else {
605
+ const innerAnnoSteps = innerAnnotation.split('.');
606
+ const tailSteps = innerAnnoSteps.splice(2, innerAnnoSteps.length - 2);
607
+ // prepend annotation prefix (path) to tail steps
608
+ tailSteps.splice(0, 0, `@${innerAnnoSteps.join('.')}`);
609
+ steps.push(...tailSteps);
610
+ }
813
611
  }
814
612
  mergePathStepsIntoPrefixTree(prefixTreeP, steps, 0);
815
613
  }
@@ -1496,142 +1294,6 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
1496
1294
  }
1497
1295
 
1498
1296
 
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
1297
  /**
1636
1298
  * translate vocabulary definitions into a userDefinedTermDict
1637
1299
  * with the same structure as the global jsonDictionary that
@@ -1763,65 +1425,57 @@ function csn2annotationEdm( reqDefs, csnVocabularies, serviceName,
1763
1425
  }
1764
1426
  }
1765
1427
 
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
- };
1428
+ //-------------------------------------------------------------------------------------------------
1429
+ // Dictionary access
1430
+ //-------------------------------------------------------------------------------------------------
1816
1431
 
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) ];
1432
+ // called to look-up a term in the dictionary
1433
+ // in addition: - note usage of the respective vocabulary
1434
+ // - issue a warning if the term is flagged as "experimental"
1435
+ function getDictTerm( termName, msg ) {
1436
+ const dict = options.dictReplacement || oDataDictionary; // tests can set different dictionary via options
1437
+ const dictTerm = (dict.terms[termName] ||
1438
+ userDefinedTermDict.terms[`${serviceName}.${termName}`] ||
1439
+ userDefinedTermDict.terms[termName]);
1440
+ // register vocabulary usage if possible
1441
+ const vocName = termName.slice(0, termName.indexOf('.'));
1442
+ const myVocDef = mergedVocDefs[vocName];
1443
+ if (myVocDef && !myVocDef.$ignore)
1444
+ myVocDef.used = true;
1445
+ else if (dictTerm?.$myServiceRoot &&
1446
+ userDefinedTermDict.xrefs[dictTerm?.$myServiceRoot])
1447
+ userDefinedTermDict.xrefs[dictTerm.$myServiceRoot].used = true;
1448
+ if (dictTerm) {
1449
+ // issue message for usage of experimental Terms, but only once per Term
1450
+ if (dictTerm.$experimental && !usedExperimentalTerms[termName]) {
1451
+ message('odata-anno-dict', msg.location, { anno: msg.anno(), '#': 'experimental' });
1452
+ usedExperimentalTerms[termName] = true;
1453
+ }
1454
+ if (dictTerm.$deprecated && !usedDeprecatedTerms[termName]) {
1455
+ message('odata-anno-def', msg.location,
1456
+ { anno: msg.anno(), depr: dictTerm.$deprecationText, '#': 'deprecated' });
1457
+ usedDeprecatedTerms[termName] = true;
1458
+ }
1459
+ }
1460
+ return dictTerm;
1461
+ }
1462
+ // called to look-up a type in the dictionary
1463
+ // in addition, note usage of the respective vocabulary
1464
+ function getDictType( typeName ) {
1465
+ const dict = options.dictReplacement || oDataDictionary; // tests can set different dictionary via options
1466
+ const dictType = (dict.types[typeName] ||
1467
+ userDefinedTermDict.types[`${serviceName}.${typeName}`] ||
1468
+ userDefinedTermDict.types[typeName]);
1469
+ if (dictType) {
1470
+ // register usage of vocabulary
1471
+ const vocName = typeName.slice(0, typeName.indexOf('.'));
1472
+ const myVocDef = mergedVocDefs[vocName];
1473
+ if (myVocDef && !myVocDef.$ignore)
1474
+ myVocDef.used = true;
1475
+ }
1476
+ return dictType;
1824
1477
  }
1478
+
1825
1479
  //-------------------------------------------------------------------------------------------------
1826
1480
  //-------------------------------------------------------------------------------------------------
1827
1481
  //-------------------------------------------------------------------------------------------------