@sap/cds-compiler 2.12.0 → 2.15.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 (128) hide show
  1. package/CHANGELOG.md +221 -15
  2. package/bin/cdsc.js +125 -50
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +47 -84
  8. package/lib/api/options.js +5 -6
  9. package/lib/api/validate.js +6 -11
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +114 -18
  16. package/lib/base/messages.js +101 -90
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +177 -123
  19. package/lib/checks/annotationsOData.js +12 -33
  20. package/lib/checks/arrayOfs.js +1 -34
  21. package/lib/checks/cdsPersistence.js +2 -1
  22. package/lib/checks/enricher.js +17 -1
  23. package/lib/checks/invalidTarget.js +3 -1
  24. package/lib/checks/managedWithoutKeys.js +3 -1
  25. package/lib/checks/selectItems.js +4 -4
  26. package/lib/checks/sql-snippets.js +27 -26
  27. package/lib/checks/types.js +1 -1
  28. package/lib/checks/validator.js +6 -11
  29. package/lib/compiler/assert-consistency.js +6 -3
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +19 -6
  32. package/lib/compiler/checks.js +23 -60
  33. package/lib/compiler/cycle-detector.js +1 -1
  34. package/lib/compiler/define.js +1151 -0
  35. package/lib/compiler/extend.js +1000 -0
  36. package/lib/compiler/finalize-parse-cdl.js +237 -0
  37. package/lib/compiler/index.js +107 -39
  38. package/lib/compiler/kick-start.js +190 -0
  39. package/lib/compiler/moduleLayers.js +4 -4
  40. package/lib/compiler/populate.js +1227 -0
  41. package/lib/compiler/propagator.js +114 -46
  42. package/lib/compiler/resolve.js +1521 -0
  43. package/lib/compiler/shared.js +126 -65
  44. package/lib/compiler/tweak-assocs.js +535 -0
  45. package/lib/compiler/utils.js +197 -33
  46. package/lib/edm/.eslintrc.json +5 -0
  47. package/lib/edm/annotations/genericTranslation.js +38 -24
  48. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  49. package/lib/edm/csn2edm.js +219 -100
  50. package/lib/edm/edm.js +302 -230
  51. package/lib/edm/edmPreprocessor.js +554 -419
  52. package/lib/edm/edmUtils.js +138 -44
  53. package/lib/gen/Dictionary.json +100 -19
  54. package/lib/gen/language.checksum +1 -1
  55. package/lib/gen/language.interp +11 -1
  56. package/lib/gen/language.tokens +86 -83
  57. package/lib/gen/languageLexer.interp +10 -1
  58. package/lib/gen/languageLexer.js +860 -833
  59. package/lib/gen/languageLexer.tokens +78 -75
  60. package/lib/gen/languageParser.js +5765 -4480
  61. package/lib/json/csnVersion.js +10 -11
  62. package/lib/json/from-csn.js +15 -3
  63. package/lib/json/to-csn.js +126 -68
  64. package/lib/language/docCommentParser.js +4 -4
  65. package/lib/language/genericAntlrParser.js +123 -5
  66. package/lib/language/language.g4 +355 -156
  67. package/lib/language/multiLineStringParser.js +5 -5
  68. package/lib/main.d.ts +486 -59
  69. package/lib/main.js +41 -9
  70. package/lib/model/api.js +3 -1
  71. package/lib/model/csnRefs.js +252 -156
  72. package/lib/model/csnUtils.js +384 -297
  73. package/lib/model/enrichCsn.js +71 -29
  74. package/lib/model/revealInternalProperties.js +29 -8
  75. package/lib/model/sortViews.js +2 -1
  76. package/lib/modelCompare/compare.js +23 -18
  77. package/lib/optionProcessor.js +63 -26
  78. package/lib/render/manageConstraints.js +35 -32
  79. package/lib/render/toCdl.js +897 -947
  80. package/lib/render/toHdbcds.js +205 -257
  81. package/lib/render/toSql.js +264 -225
  82. package/lib/render/utils/common.js +136 -25
  83. package/lib/render/utils/sql.js +4 -3
  84. package/lib/render/utils/stringEscapes.js +111 -0
  85. package/lib/sql-identifier.js +1 -1
  86. package/lib/transform/.eslintrc.json +5 -0
  87. package/lib/transform/db/.eslintrc.json +3 -1
  88. package/lib/transform/db/applyTransformations.js +35 -12
  89. package/lib/transform/db/assertUnique.js +1 -1
  90. package/lib/transform/db/associations.js +104 -306
  91. package/lib/transform/db/cdsPersistence.js +2 -2
  92. package/lib/transform/db/constraints.js +58 -53
  93. package/lib/transform/db/expansion.js +60 -33
  94. package/lib/transform/db/flattening.js +582 -104
  95. package/lib/transform/db/groupByOrderBy.js +3 -1
  96. package/lib/transform/db/transformExists.js +66 -13
  97. package/lib/transform/db/views.js +11 -7
  98. package/lib/transform/draft/.eslintrc.json +38 -0
  99. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  100. package/lib/transform/draft/odata.js +227 -0
  101. package/lib/transform/forHanaNew.js +109 -208
  102. package/lib/transform/forOdataNew.js +59 -212
  103. package/lib/transform/localized.js +46 -26
  104. package/lib/transform/odata/toFinalBaseType.js +85 -11
  105. package/lib/transform/odata/typesExposure.js +147 -199
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +44 -33
  108. package/lib/transform/translateAssocsToJoins.js +3 -20
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +172 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/moduleResolve.js +13 -6
  114. package/lib/utils/objectUtils.js +30 -0
  115. package/package.json +1 -1
  116. package/share/messages/README.md +26 -0
  117. package/share/messages/message-explanations.json +2 -1
  118. package/share/messages/syntax-expected-integer.md +37 -0
  119. package/lib/compiler/definer.js +0 -2361
  120. package/lib/compiler/resolver.js +0 -3079
  121. package/lib/transform/odata/attachPath.js +0 -96
  122. package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
  123. package/lib/transform/odata/generateForeignKeyElements.js +0 -261
  124. package/lib/transform/odata/referenceFlattener.js +0 -290
  125. package/lib/transform/odata/sortByAssociationDependency.js +0 -105
  126. package/lib/transform/odata/structuralPath.js +0 -72
  127. package/lib/transform/odata/structureFlattener.js +0 -171
  128. package/lib/transform/universalCsnEnricher.js +0 -237
package/lib/edm/edm.js CHANGED
@@ -1,23 +1,91 @@
1
- // @ts-nocheck
2
-
3
1
  'use strict'
4
2
 
5
3
  const edmUtils = require('./edmUtils.js');
6
4
  const { isBuiltinType } = require('../model/csnUtils.js');
7
-
8
- module.exports = function (options, error) {
5
+ const { forEach } = require("../utils/objectUtils");
6
+
7
+ // facet definitions, optional could either be true or array of edm types
8
+ // remove indicates wether or not the canonic facet shall be removed when applying @odata.Type
9
+ const EdmTypeFacetMap = {
10
+ 'MaxLength': { v2: true, v4: true, remove: true, optional: true },
11
+ 'Precision': { v2: true, v4: true, remove: true, optional: true },
12
+ 'Scale': { v2: true, v4: true, remove: true, optional: true },
13
+ 'SRID': { v4: true, remove: true, optional: true },
14
+ //'FixedLength': { v2: true },
15
+ //'Collation': { v2: true },
16
+ //'Unicode': { v2: true, v4: true },
17
+ };
18
+ const EdmTypeFacetNames = Object.keys(EdmTypeFacetMap);
19
+
20
+ // Merged primitive type map with descriptions taken from V4 spec and filled up with V2 spec
21
+ const EdmPrimitiveTypeMap = {
22
+ 'Edm.Binary': { v2: true, v4: true, MaxLength: true, FixedLength: true, desc: 'Binary data' },
23
+ 'Edm.Boolean': { v2: true, v4: true, desc: 'Binary-valued logic' },
24
+ 'Edm.Byte': { v2: true, v4: true, desc: 'Unsigned 8-bit integer' },
25
+ 'Edm.Date': { v4: true, desc: 'Date without a time-zone offset' },
26
+ 'Edm.DateTime': { v2: true, Precision: true, desc: 'Date and time with values ranging from 12:00:00 midnight, January 1, 1753 A.D. through 11:59:59 P.M, December 31, 9999 A.D.' },
27
+ 'Edm.DateTimeOffset': { v2: true, v4: true, Precision: true, desc: 'Date and time with a time-zone offset, no leap seconds' },
28
+ 'Edm.Decimal': { v2: true, v4: true, Precision: true, Scale: true, desc: 'Numeric values with decimal representation' },
29
+ 'Edm.Double': { v2: true, v4: true, desc: 'IEEE 754 binary64 floating-point number (15-17 decimal digits)' },
30
+ 'Edm.Duration': { v4: true, Precision: true, desc: 'Signed duration in days, hours, minutes, and (sub)seconds' },
31
+ 'Edm.Guid': { v2: true, v4: true, desc: '16-byte (128-bit) unique identifier' },
32
+ 'Edm.Int16': { v2: true, v4: true, desc: 'Signed 16-bit integer' },
33
+ 'Edm.Int32': { v2: true, v4: true, desc: 'Signed 32-bit integer' },
34
+ 'Edm.Int64': { v2: true, v4: true, desc: 'Signed 64-bit integer' },
35
+ 'Edm.SByte': { v2: true, v4: true, desc: 'Signed 8-bit integer' },
36
+ 'Edm.Single': { v2: true, v4: true, desc: 'IEEE 754 binary32 floating-point number (6-9 decimal digits)' },
37
+ 'Edm.Stream': { v4: true, MaxLength: true, desc: 'Binary data stream' },
38
+ 'Edm.String': { v2: true, v4: true, MaxLength: true, FixedLength: true, Collation: true, Unicode: true, desc: 'Sequence of characters' },
39
+ 'Edm.Time': { v2: true, Precision: true, desc: 'time of day with values ranging from 0:00:00.x to 23:59:59.y, where x and y depend upon the precision' },
40
+ 'Edm.TimeOfDay': { v4: true, Precision: true, desc: 'Clock time 00:00-23:59:59.999999999999' },
41
+ 'Edm.Geography': { v4: true, SRID: true, desc: 'Abstract base type for all Geography types' },
42
+ 'Edm.GeographyPoint': { v4: true, SRID: true, desc: 'A point in a round-earth coordinate system' },
43
+ 'Edm.GeographyLineString': { v4: true, SRID: true, desc: 'Line string in a round-earth coordinate system' },
44
+ 'Edm.GeographyPolygon': { v4: true, SRID: true, desc: 'Polygon in a round-earth coordinate system' },
45
+ 'Edm.GeographyMultiPoint': { v4: true, SRID: true, desc: 'Collection of points in a round-earth coordinate system' },
46
+ 'Edm.GeographyMultiLineString': { v4: true, SRID: true, desc: 'Collection of line strings in a round-earth coordinate system' },
47
+ 'Edm.GeographyMultiPolygon': { v4: true, SRID: true, desc: 'Collection of polygons in a round-earth coordinate system' },
48
+ 'Edm.GeographyCollection': { v4: true, SRID: true, desc: 'Collection of arbitrary Geography values' },
49
+ 'Edm.Geometry': { v4: true, SRID: true, desc: 'Abstract base type for all Geometry types' },
50
+ 'Edm.GeometryPoint': { v4: true, SRID: true, desc: 'Point in a flat-earth coordinate system' },
51
+ 'Edm.GeometryLineString': { v4: true, SRID: true, desc: 'Line string in a flat-earth coordinate system' },
52
+ 'Edm.GeometryPolygon': { v4: true, SRID: true, descr: 'Polygon in a flat-earth coordinate system' },
53
+ 'Edm.GeometryMultiPoint': { v4: true, SRID: true, desc: 'Collection of points in a flat-earth coordinate system' },
54
+ 'Edm.GeometryMultiLineString': { v4: true, SRID: true, desc: 'Collection of line strings in a flat-earth coordinate system' },
55
+ 'Edm.GeometryMultiPolygon': { v4: true, SRID: true, desc: 'Collection of polygons in a flat-earth coordinate system' },
56
+ 'Edm.GeometryCollection': { v4: true, SRID: true, desc: 'Collection of arbitrary Geometry values' },
57
+ //'Edm.PrimitiveType': { v4: true, desc: 'Abstract meta type' },
58
+ //'Edm.Untyped': { v4: true, desc: 'Abstract void type' },
59
+ };
60
+
61
+ function getEdm(options, messageFunctions) {
62
+ const { error } = messageFunctions || { error: ()=>true, warning: ()=>true };
9
63
  class Node
10
64
  {
11
- constructor(v, attributes=Object.create(null), csn=undefined)
65
+ /**
66
+ * @param {boolean[]} v Versions in the form of [<v2>, <v4>].
67
+ * @param {object} attributes
68
+ * @param {CSN.Model} csn
69
+ */
70
+ constructor(v, attributes = Object.create(null), csn=undefined)
12
71
  {
13
72
  if(!attributes || typeof attributes !== 'object')
14
73
  error(null, 'Please debug me: attributes must be a dictionary');
15
74
  if(!Array.isArray(v))
16
75
  error(null, 'Please debug me: v is either undefined or not an array: ' + v);
17
- if(v.filter(v=>v).length != 1)
76
+ if(v.filter(v => v).length !== 1)
18
77
  error(null, 'Please debug me: exactly one version must be set');
19
- Object.assign(this, attributes);
20
- this.set({ _children: [], _xmlOnlyAttributes: Object.create(null), _jsonOnlyAttributes: Object.create(null), _v: v, _ignoreChildren: false });
78
+
79
+ // Common attributes of JSON and XML.
80
+ // Note: Can't assign attributes directly, due to the input object being modified.
81
+ // The caller re-uses the object for other nodes.
82
+ this._edmAttributes = Object.assign(Object.create(null), attributes);
83
+ this._xmlOnlyAttributes = Object.create(null);
84
+ this._jsonOnlyAttributes = Object.create(null);
85
+
86
+ this._children = [];
87
+ this._ignoreChildren = false;
88
+ this._v = v;
21
89
 
22
90
  if(this.v2)
23
91
  this.setSapVocabularyAsAttributes(csn);
@@ -30,44 +98,53 @@ module.exports = function (options, error) {
30
98
  return this.constructor.name
31
99
  }
32
100
 
33
- // set's additional properties that are invisible for the iterators
34
- set(attributes)
35
- {
36
- if(!attributes || typeof attributes !== 'object')
37
- error(null, 'Please debug me: attributes must be a dictionary');
38
- let newAttributes = Object.create(null);
39
- edmUtils.forAll(attributes, (value, p) => {
40
- newAttributes[p] = {
41
- value,
42
- configurable: true,
43
- enumerable: false,
44
- writable: true
45
- }
46
- });
47
- return Object.defineProperties(this, newAttributes)
101
+ /**
102
+ * Set the EDM(X) attribute on the Node.
103
+ * @param {string} key
104
+ * @param {any} value
105
+ */
106
+ setEdmAttribute(key, value) {
107
+ if(key !== undefined && key !== null && value !== undefined && value !== null)
108
+ this._edmAttributes[key] = value;
109
+ }
110
+
111
+ /**
112
+ * Remove the EDM(X) attribute on the Node.
113
+ * @param {string} name
114
+ */
115
+ removeEdmAttribute(name) {
116
+ if (name in this._edmAttributes)
117
+ delete this._edmAttributes[name];
48
118
  }
49
119
 
50
- // set properties that should only appear in the XML representation
120
+ /**
121
+ * Set properties that should only appear in the XML representation
122
+ * @param {object} attributes
123
+ * @return {any}
124
+ */
51
125
  setXml(attributes)
52
126
  {
53
- if(!attributes || typeof attributes !== 'object')
54
- error(null, 'Please debug me: attributes must be a dictionary');
55
127
  return Object.assign(this._xmlOnlyAttributes, attributes);
56
128
  }
57
129
 
58
- // set properties that should only appear in the JSON representation
59
- // today JSON attributes are not rendered in toJSONattributes()
130
+ /**
131
+ * Set properties that should only appear in the JSON representation.
132
+ * Today JSON attributes are not rendered in toJSONattributes()
133
+ *
134
+ * @param {object} attributes
135
+ * @return {any}
136
+ */
60
137
  setJSON(attributes)
61
138
  {
62
- if(!attributes || typeof attributes !== 'object')
63
- error(null, 'Please debug me: attributes must be a dictionary');
64
139
  return Object.assign(this._jsonOnlyAttributes, attributes);
65
140
  }
66
141
 
67
142
  prepend(...children)
68
143
  {
69
144
  this._children.splice(0, 0, ...children.filter(c => c));
145
+ return this;
70
146
  }
147
+
71
148
  append(...children)
72
149
  {
73
150
  // remove undefined entries
@@ -78,9 +155,9 @@ module.exports = function (options, error) {
78
155
  // virtual
79
156
  toJSON()
80
157
  {
81
- let json = Object.create(null);
158
+ const json = Object.create(null);
82
159
  // $kind Property MAY be omitted in JSON for performance reasons
83
- if(![ 'Property', 'EntitySet', 'ActionImport', 'FunctionImport', 'Singleton', 'Schema' ].includes(this.kind))
160
+ if(!(this.kind in Node.noJsonKinds))
84
161
  json['$Kind'] = this.kind;
85
162
 
86
163
  this.toJSONattributes(json);
@@ -91,7 +168,7 @@ module.exports = function (options, error) {
91
168
  // virtual
92
169
  toJSONattributes(json)
93
170
  {
94
- edmUtils.forAll(this, (v,p) => {
171
+ forEach(this._edmAttributes, (p, v) => {
95
172
  if (p !== 'Name')
96
173
  json[p[0] === '@' ? p : '$' + p] = v;
97
174
  });
@@ -101,9 +178,9 @@ module.exports = function (options, error) {
101
178
  // virtual
102
179
  toJSONchildren(json)
103
180
  {
104
- // any child with a Name should be added by it's name into the JSON object
181
+ // any child with a Name should be added by its name into the JSON object
105
182
  // all others must overload toJSONchildren()
106
- this._children.filter(c => c.Name).forEach(c => json[c.Name] = c.toJSON());
183
+ this._children.filter(c => c._edmAttributes.Name).forEach(c => json[c._edmAttributes.Name] = c.toJSON());
107
184
  }
108
185
 
109
186
  // virtual
@@ -112,9 +189,9 @@ module.exports = function (options, error) {
112
189
  let kind = this.kind;
113
190
  let head = indent + '<' + kind;
114
191
 
115
- if(kind=='Parameter' && this.Collection) {
116
- delete this.Collection;
117
- this.Type=`Collection(${this.Type})`;
192
+ if(kind==='Parameter' && this._edmAttributes.Collection) {
193
+ delete this._edmAttributes.Collection;
194
+ this._edmAttributes.Type=`Collection(${this._edmAttributes.Type})`;
118
195
  }
119
196
 
120
197
  head += this.toXMLattributes();
@@ -135,13 +212,13 @@ module.exports = function (options, error) {
135
212
  toXMLattributes()
136
213
  {
137
214
  let tmpStr = '';
138
- edmUtils.forAll(this, (v, p) => {
139
- if (typeof this[p] !== 'object')
140
- tmpStr += ' ' + p + '="' + edmUtils.escapeString(v) + '"'
215
+ forEach(this._edmAttributes, (p, v) => {
216
+ if (v !== undefined && typeof v !== 'object')
217
+ tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
141
218
  });
142
- edmUtils.forAll(this._xmlOnlyAttributes, (v,p) => {
143
- if (typeof v !== 'object')
144
- tmpStr += ' ' + p + '="' + edmUtils.escapeString(v) + '"'
219
+ forEach(this._xmlOnlyAttributes, (p, v) => {
220
+ if (v !== undefined && typeof v !== 'object')
221
+ tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
145
222
  });
146
223
  return tmpStr;
147
224
  }
@@ -170,13 +247,16 @@ module.exports = function (options, error) {
170
247
  }
171
248
  }
172
249
 
250
+ // $kind Property MAY be omitted in JSON for performance reasons
251
+ Node.noJsonKinds = {'Property':1, 'EntitySet':1, 'ActionImport':1, 'FunctionImport':1, 'Singleton':1, 'Schema':1};
252
+
173
253
  class Reference extends Node
174
254
  {
175
255
  constructor(v, details)
176
256
  {
177
257
  super(v, details);
178
258
  if(this.v2)
179
- this['xmlns:edmx'] = 'http://docs.oasis-open.org/odata/ns/edmx';
259
+ this._edmAttributes['xmlns:edmx'] = 'http://docs.oasis-open.org/odata/ns/edmx';
180
260
  }
181
261
 
182
262
  get kind() { return 'edmx:Reference' }
@@ -207,12 +287,12 @@ module.exports = function (options, error) {
207
287
  {
208
288
  constructor(v, ns, alias=undefined, serviceCsn=null, annotations=[], withEntityContainer=true)
209
289
  {
210
- let props = Object.create(null);
211
- props.Namespace = ns;
212
- if(alias != undefined)
290
+ const props = { Namespace: ns };
291
+ if(alias !== undefined)
213
292
  props.Alias = alias;
214
293
  super(v, props);
215
- this.set( { _annotations: annotations, _actions: Object.create(null) } );
294
+ this._annotations = annotations;
295
+ this._actions = Object.create(null);
216
296
  this.setXml( { xmlns: (this.v2) ? 'http://schemas.microsoft.com/ado/2008/09/edm' : 'http://docs.oasis-open.org/odata/ns/edm' } );
217
297
 
218
298
  if(this.v2 && serviceCsn)
@@ -227,17 +307,17 @@ module.exports = function (options, error) {
227
307
  // append for rendering, ok ec has Name
228
308
  this.append(ec);
229
309
  // set as attribute for later access...
230
- this.set({ _ec : ec })
310
+ this._ec = ec;
231
311
  }
232
312
  }
233
313
 
234
314
  // hold actions and functions in V4
235
315
  addAction(action)
236
316
  {
237
- if(this._actions[action.Name])
238
- this._actions[action.Name].push(action);
317
+ if(this._actions[action._edmAttributes.Name])
318
+ this._actions[action._edmAttributes.Name].push(action);
239
319
  else
240
- this._actions[action.Name] = [action];
320
+ this._actions[action._edmAttributes.Name] = [action];
241
321
  }
242
322
 
243
323
  setAnnotations(annotations)
@@ -249,7 +329,7 @@ module.exports = function (options, error) {
249
329
  innerXML(indent, what)
250
330
  {
251
331
  let xml = '';
252
- if(what=='metadata' || what=='all')
332
+ if(what==='metadata' || what==='all')
253
333
  {
254
334
  xml += super.innerXML(indent);
255
335
  edmUtils.forAll(this._actions, actionArray => {
@@ -257,11 +337,11 @@ module.exports = function (options, error) {
257
337
  xml += action.toXML(indent, what) + '\n'; });
258
338
  });
259
339
  }
260
- if(what=='annotations' || what=='all')
340
+ if(what==='annotations' || what==='all')
261
341
  {
262
342
  if(this._annotations.length > 0) {
263
- this._annotations.filter(a => a.Term).forEach(a => xml += a.toXML(indent) + '\n');
264
- this._annotations.filter(a => a.Target).forEach(a => xml += a.toXML(indent) + '\n');
343
+ this._annotations.filter(a => a._edmAttributes.Term).forEach(a => xml += a.toXML(indent) + '\n');
344
+ this._annotations.filter(a => a._edmAttributes.Target).forEach(a => xml += a.toXML(indent) + '\n');
265
345
  }
266
346
  }
267
347
  return xml;
@@ -270,7 +350,7 @@ module.exports = function (options, error) {
270
350
  // no $Namespace
271
351
  toJSONattributes(json)
272
352
  {
273
- edmUtils.forAll(this, (v,p) => {
353
+ edmUtils.forAll(this._edmAttributes, (v,p) => {
274
354
  if (p !== 'Name' && p !== 'Namespace')
275
355
  json[p[0] === '@' ? p : '$' + p] = v;
276
356
  });
@@ -281,13 +361,13 @@ module.exports = function (options, error) {
281
361
  // 'edmx:DataServices' should not appear in JSON
282
362
  super.toJSONchildren(json);
283
363
  if(this._annotations.length > 0) {
284
- this._annotations.filter(a => a.Term).forEach(a => {
364
+ this._annotations.filter(a => a._edmAttributes.Term).forEach(a => {
285
365
  Object.entries(a.toJSON()).forEach(([n, v]) => {
286
366
  json[n] = v;
287
367
  });
288
368
  });
289
369
  let json_Annotations = Object.create(null);
290
- this._annotations.filter(a => a.Target).forEach(a => json_Annotations[a.Target] = a.toJSON());
370
+ this._annotations.filter(a => a._edmAttributes.Target).forEach(a => json_Annotations[a._edmAttributes.Target] = a.toJSON());
291
371
  if(Object.keys(json_Annotations).length)
292
372
  json['$Annotations'] = json_Annotations;
293
373
  }
@@ -308,7 +388,7 @@ module.exports = function (options, error) {
308
388
  constructor(v)
309
389
  {
310
390
  super(v);
311
- this.set( { _schemas: Object.create(null) } );
391
+ this._schemas = Object.create(null);
312
392
 
313
393
  if(this.v2)
314
394
  this.setXml( { 'm:DataServiceVersion': '2.0' } )
@@ -327,7 +407,7 @@ module.exports = function (options, error) {
327
407
  toJSONchildren(json)
328
408
  {
329
409
  // 'edmx:DataServices' should not appear in JSON
330
- this._children.forEach(s => json[s.Namespace] = s.toJSON());
410
+ this._children.forEach(s => json[s._edmAttributes.Namespace] = s.toJSON());
331
411
  return json;
332
412
  }
333
413
  }
@@ -347,12 +427,15 @@ module.exports = function (options, error) {
347
427
  constructor(v, service)
348
428
  {
349
429
  super(v, { Version : (v[1]) ? '4.0' : '1.0' });
350
- this.set( { _service: service, _defaultRefs: [] } );
430
+ this._service = service;
431
+ this._defaultRefs = [];
351
432
 
352
- let xmlProps = Object.create(null);
433
+ const xmlProps = Object.create(null);
353
434
  if(this.v4)
354
435
  {
355
- xmlProps['xmlns:edmx'] = 'http://docs.oasis-open.org/odata/ns/edmx';
436
+ xmlProps['xmlns:edmx'] = 'http://docs.oasis-open.org/odata/ns/edmx';
437
+ xmlProps['xmlns:m'] = undefined;
438
+ xmlProps['xmlns:sap'] = undefined;
356
439
  }
357
440
  else
358
441
  {
@@ -365,19 +448,6 @@ module.exports = function (options, error) {
365
448
 
366
449
  get kind() { return 'edmx:Edmx' }
367
450
 
368
- hasAnnotations()
369
- {
370
- let rc = false;
371
- this._service._children.forEach(c =>
372
- { if(c._annotations.length > 0) rc = true; } )
373
- return rc;
374
- }
375
-
376
- getSchemaCount()
377
- {
378
- return this._service._children.length;
379
- }
380
-
381
451
  getAnnotations(schemaIndex=0)
382
452
  {
383
453
  if(this._service && this._service._children[schemaIndex])
@@ -397,12 +467,12 @@ module.exports = function (options, error) {
397
467
  let schema = this._service._children[0];
398
468
 
399
469
  let json = Object.create(null);
400
- json['$Version'] = this.Version;
401
- json['$EntityContainer'] = schema.Namespace + '.' + schema._ec.Name;
470
+ json['$Version'] = this._edmAttributes.Version;
471
+ json['$EntityContainer'] = schema._edmAttributes.Namespace + '.' + schema._ec._edmAttributes.Name;
402
472
 
403
473
  let reference_json = Object.create(null);
404
- this._defaultRefs.forEach(r => reference_json[r.Uri] = r.toJSON());
405
- this._children.forEach(r => reference_json[r.Uri] = r.toJSON());
474
+ this._defaultRefs.forEach(r => reference_json[r._edmAttributes.Uri] = r.toJSON());
475
+ this._children.forEach(r => reference_json[r._edmAttributes.Uri] = r.toJSON());
406
476
 
407
477
  if(Object.keys(reference_json).length)
408
478
  json['$Reference'] = reference_json;
@@ -415,9 +485,7 @@ module.exports = function (options, error) {
415
485
  // all(default), metadata, annotations
416
486
  toXML(what='all')
417
487
  {
418
- let rc = '<?xml version="1.0" encoding="utf-8"?>\n';
419
- rc += `${super.toXML('', what)}`;
420
- return rc;
488
+ return '<?xml version="1.0" encoding="utf-8"?>\n' + super.toXML('', what);
421
489
  }
422
490
 
423
491
  innerXML(indent, what)
@@ -434,33 +502,32 @@ module.exports = function (options, error) {
434
502
 
435
503
  class EntityContainer extends Node
436
504
  {
437
- constructor() {
438
- super(...arguments);
439
- this.set( { _registry: Object.create(null) } );
505
+ constructor(v, attributes, csn) {
506
+ super(v, attributes, csn);
507
+ this._registry = Object.create(null);
440
508
  }
441
509
  // use the _SetAttributes
442
510
  setSapVocabularyAsAttributes(csn)
443
511
  {
444
512
  super.setSapVocabularyAsAttributes(csn, true);
445
513
  }
514
+
446
515
  register(entry) {
447
- if(!this._registry[entry.Name])
448
- this._registry[entry.Name] = [entry];
516
+ if(!this._registry[entry._edmAttributes.Name])
517
+ this._registry[entry._edmAttributes.Name] = [entry];
449
518
  else
450
- this._registry[entry.Name].push(entry);
451
- super.append(entry);
519
+ this._registry[entry._edmAttributes.Name].push(entry);
520
+ this.append(entry);
452
521
  }
453
522
  }
454
523
 
455
524
 
456
-
457
525
  class Singleton extends Node
458
526
  {
459
527
  toJSONattributes(json)
460
528
  {
461
- edmUtils.forAll(this, (v,p) => {
462
- if (p !== 'Name')
463
- {
529
+ forEach(this._edmAttributes, (p, v) => {
530
+ if (p !== 'Name') {
464
531
  if(p === 'EntityType') // it's $Type in json
465
532
  json['$Type'] = v;
466
533
  else
@@ -473,7 +540,7 @@ module.exports = function (options, error) {
473
540
  toJSONchildren(json)
474
541
  {
475
542
  let json_navPropBinding = Object.create(null);
476
- this._children.forEach(npb => json_navPropBinding[npb.Path] = npb.Target);
543
+ this._children.forEach(npb => json_navPropBinding[npb._edmAttributes.Path] = npb._edmAttributes.Target);
477
544
  if(Object.keys(json_navPropBinding).length > 0)
478
545
  json['$NavigationPropertyBinding'] = json_navPropBinding;
479
546
 
@@ -481,7 +548,7 @@ module.exports = function (options, error) {
481
548
  }
482
549
 
483
550
  getDuplicateMessage() {
484
- return `EntityType "${this.EntityType}"`
551
+ return `EntityType "${this._edmAttributes.EntityType}"`
485
552
  }
486
553
  }
487
554
 
@@ -534,13 +601,13 @@ module.exports = function (options, error) {
534
601
  constructor(v, details)
535
602
  {
536
603
  super(v, details);
537
- this.set( { _returnType: undefined });
604
+ this._returnType = undefined;
538
605
  }
539
606
 
540
607
  innerXML(indent)
541
608
  {
542
609
  let xml = super.innerXML(indent);
543
- if(this._returnType != undefined)
610
+ if(this._returnType !== undefined)
544
611
  xml += this._returnType.toXML(indent) + '\n';
545
612
  return xml
546
613
  }
@@ -573,12 +640,12 @@ module.exports = function (options, error) {
573
640
  */
574
641
  class FunctionImport extends Node {
575
642
  getDuplicateMessage() {
576
- return `Function "${this.Name}"`
643
+ return `Function "${this._edmAttributes.Name}"`
577
644
  }
578
645
  } //ActionFunctionBase {}
579
646
  class ActionImport extends Node {
580
647
  getDuplicateMessage() {
581
- return `Action "${this.Name}"`
648
+ return `Action "${this._edmAttributes.Name}"`
582
649
  }
583
650
  }
584
651
 
@@ -586,75 +653,81 @@ module.exports = function (options, error) {
586
653
  {
587
654
  constructor(v, attributes, csn, typeName='Type')
588
655
  {
589
- if(!(csn instanceof Object || (typeof csn === 'object' && csn !== null)))
590
- error(null, 'Please debug me: csn must be an object');
591
-
592
656
  // ??? Is CSN still required? NavProp?
593
657
  super(v, attributes, csn);
594
- this.set({ _typeName: typeName });
595
-
596
- if(this[typeName] == undefined)
658
+ this._typeName = typeName;
659
+ this._scalarType = undefined;
660
+ if(this._edmAttributes[typeName] === undefined)
597
661
  {
598
662
  let typecsn = csn.type ? csn : (csn.items && csn.items.type ? csn.items : csn);
599
663
  // Complex/EntityType are derived from TypeBase
600
664
  // but have no type attribute in their CSN
601
665
  if(typecsn.type) { // this thing has a type
602
- // check wether this is a scalar type (or array of scalar type) or a named type
603
- let scalarType = undefined;
666
+ // check whether this is a scalar type (or array of scalar type) or a named type
604
667
  if(typecsn.items && typecsn.items.type &&
605
668
  isBuiltinType(typecsn.items.type)) {
606
- scalarType = typecsn.items;
669
+ this._scalarType = typecsn.items;
607
670
  }
608
671
  else if(isBuiltinType(typecsn.type)) {
609
- scalarType = typecsn;
672
+ this._scalarType = typecsn;
610
673
  }
611
- if(scalarType) {
612
- this[typeName] = csn._edmType;
674
+ if(this._scalarType) {
675
+ this._edmAttributes[typeName] = csn._edmType;
613
676
  // CDXCORE-CDXCORE-173 ignore type facets for Edm.Stream
614
677
  // cds-compiler/issues/7835: Only set length for Binary as long as it is
615
678
  // unclear how many bytes a string character represents.
616
679
  // We can't calculate an unambiguous byte stream length for DB dependent
617
680
  // multi-byte characters.
618
- if(!(this[typeName] === 'Edm.Stream' &&
619
- ![ /*'cds.String',*/ 'cds.Binary'].includes(scalarType.type)))
620
- edmUtils.addTypeFacets(this, scalarType);
681
+ if(!(this._edmAttributes[typeName] === 'Edm.Stream' &&
682
+ !( /*scalarType.type === 'cds.String' ||*/ this._scalarType.type === 'cds.Binary')))
683
+ edmUtils.addTypeFacets(this, this._scalarType);
684
+ // CDXCORE-245:
685
+ // map type to @odata.Type
686
+ // optionally add @odata { MaxLength, Precision, Scale, SRID } but only in combination with @odata.Type
687
+ const odataType = csn['@odata.Type'];
688
+ if(odataType)
689
+ {
690
+ const td = EdmPrimitiveTypeMap[odataType];
691
+ // If type is known, it must be available in the current version
692
+ // Reason: EDMX Importer may set `@odata.Type: 'Edm.DateTime'` on imported V2 services
693
+ // Not filtering out this incompatible type here in case of a V4 rendering would
694
+ // produce an unrecoverable error.
695
+ if(td && (td.v2 === this.v2 || td.v4 === this.v4)) {
696
+ this.setEdmAttribute(typeName, odataType);
697
+ EdmTypeFacetNames.forEach(facet => {
698
+ if(EdmTypeFacetMap[facet].remove)
699
+ this.removeEdmAttribute(facet);
700
+ if(td[facet] !== undefined &&
701
+ (EdmTypeFacetMap[facet].v2 === this.v2 ||
702
+ EdmTypeFacetMap[facet].v4 === this.v4)
703
+ ) {
704
+ this.setEdmAttribute(facet, csn['@odata.'+facet]);
705
+ }
706
+ });
707
+ }
708
+ }
621
709
  }
622
710
  else {
623
- this[typeName] = typecsn.type;
624
- }
625
- // CDXCORE-245:
626
- // map type to @odata.Type
627
- // optionally add @odata.MaxLength but only in combination with @odata.Type
628
- // In absence of checks restrict @odata.Type to 'Edm.String' and 'Edm.Int[16,32,64]'
629
- let odataType = csn['@odata.Type'];
630
- if(odataType === 'Edm.String')
631
- {
632
- this[typeName] = odataType;
633
- if(csn['@odata.MaxLength']) {
634
- this['MaxLength'] = csn['@odata.MaxLength'];
635
- }
636
- } else if(['Edm.Int16', 'Edm.Int32', 'Edm.Int64'].includes(odataType)) {
637
- this[typeName] = odataType;
711
+ this._edmAttributes[typeName] = typecsn.type;
638
712
  }
639
713
  }
640
714
  }
715
+
641
716
  // Set the collection property if this is either an element or a parameter
642
- if(csn.kind === undefined) {
643
- this.set({ _isCollection: csn._isCollection });
644
- }
717
+ this._isCollection = (csn.kind === undefined) ? csn._isCollection : false;
645
718
 
646
- if(options.whatsMySchemaName && this[typeName]) {
647
- let schemaName = options.whatsMySchemaName(this[typeName]);
719
+ if(options.whatsMySchemaName && this._edmAttributes[typeName]) {
720
+ let schemaName = options.whatsMySchemaName(this._edmAttributes[typeName]);
648
721
  if(schemaName && schemaName !== options.serviceName) {
649
- this[typeName] = this[typeName].replace(options.serviceName + '.', '');
722
+ this._edmAttributes[typeName] = this._edmAttributes[typeName].replace(options.serviceName + '.', '');
650
723
  }
651
724
  }
652
725
 
653
726
  // store undecorated type for JSON
654
- this.set( { _type : this[typeName] });
727
+ this._type = this._edmAttributes[typeName];
655
728
  // decorate for XML (not for Complex/EntityType)
656
729
  if(this._isCollection)
657
- this[typeName] = `Collection(${this[typeName]})`
730
+ this._edmAttributes[typeName] = `Collection(${this._edmAttributes[typeName]})`
658
731
  }
659
732
 
660
733
  toJSONattributes(json)
@@ -664,7 +737,7 @@ module.exports = function (options, error) {
664
737
  if(this._type !== 'Edm.String' && this._type) // Edm.String is default)
665
738
  json['$'+this._typeName] = this._type;
666
739
 
667
- edmUtils.forAll(this, (v,p) => {
740
+ edmUtils.forAll(this._edmAttributes, (v,p) => {
668
741
  if (p !== 'Name' && p !== this._typeName
669
742
  // remove this line if Nullable=true becomes default
670
743
  && !(p === 'Nullable' && v == false))
@@ -721,7 +794,9 @@ module.exports = function (options, error) {
721
794
  });
722
795
 
723
796
  if(csn.$edmKeyPaths && csn.$edmKeyPaths.length)
724
- this.set( { _keys: new Key(v, csn.$edmKeyPaths) } );
797
+ this._keys = new Key(v, csn.$edmKeyPaths);
798
+ else
799
+ this._keys = undefined;
725
800
  }
726
801
 
727
802
  innerXML(indent)
@@ -788,7 +863,7 @@ module.exports = function (options, error) {
788
863
  {
789
864
  toJSONattributes(json)
790
865
  {
791
- json[this.Name] = this.Value;
866
+ json[this._edmAttributes.Name] = this._edmAttributes.Value;
792
867
  return json;
793
868
  }
794
869
  }
@@ -798,7 +873,7 @@ module.exports = function (options, error) {
798
873
  constructor(v, attributes, csn)
799
874
  {
800
875
  super(v, attributes, csn);
801
- this.set({ _csn: csn });
876
+ this._csn = csn;
802
877
  if(this.v2)
803
878
  {
804
879
  let typecsn = csn.items || csn;
@@ -806,7 +881,7 @@ module.exports = function (options, error) {
806
881
  // see edmUtils.mapsCdsToEdmType => add sap:display-format annotation
807
882
  // only if Edm.DateTime is the result of a cast from Edm.Date
808
883
  // but not if Edm.DateTime is the result of a regular cds type mapping
809
- if(this.Type === 'Edm.DateTime'
884
+ if(this._edmAttributes.Type === 'Edm.DateTime'
810
885
  && (typecsn.type !== 'cds.DateTime' && typecsn.type !== 'cds.Timestamp'))
811
886
  this.setXml( { 'sap:display-format' : 'Date' } );
812
887
 
@@ -818,14 +893,14 @@ module.exports = function (options, error) {
818
893
  {
819
894
  // From the Spec: In OData 4.01 responses a collection-valued property MUST specify a value for the Nullable attribute.
820
895
  if(this._isCollection) {
821
- this.Nullable = !this.isNotNullable();
896
+ this._edmAttributes.Nullable = !this.isNotNullable();
822
897
  }
823
898
  // Nullable=true is default, mention Nullable=false only in XML
824
899
  // Nullable=false is default for EDM JSON representation 4.01
825
900
  // When a key explicitly (!) has 'notNull = false', it stays nullable
826
901
  else if(this.isNotNullable())
827
902
  {
828
- this.Nullable = false;
903
+ this._edmAttributes.Nullable = false;
829
904
  }
830
905
  }
831
906
 
@@ -842,7 +917,7 @@ module.exports = function (options, error) {
842
917
  {
843
918
  super.toJSONattributes(json);
844
919
  // mention all nullable elements explicitly, remove if Nullable=true becomes default
845
- if(this.Nullable === undefined || this.Nullable === true)
920
+ if(this._edmAttributes.Nullable === undefined || this._edmAttributes.Nullable === true)
846
921
  {
847
922
  json['$Nullable'] = true;
848
923
  }
@@ -864,9 +939,6 @@ module.exports = function (options, error) {
864
939
  {
865
940
  let json = Object.create(null);
866
941
  this.toJSONattributes(json);
867
- // !this._nullable if Nullable=true become default
868
- if(this._nullable)
869
- json['$Nullable'] = this._nullable;
870
942
  return json;
871
943
  }
872
944
  }
@@ -875,28 +947,16 @@ module.exports = function (options, error) {
875
947
  {
876
948
  constructor(v, attributes, csn)
877
949
  {
878
- // the annotations in this array shall become exposed as Property attributes in
879
- // the V2 metadata.xml
880
- // @ts-ignore
881
- Property.SAP_Annotation_Attribute_WhiteList = [
882
- '@sap.hierarchy.node.for', //-> sap:hierarchy-node-for
883
- '@sap.hierarchy.parent.node.for', // -> sap:hierarchy-parent-node-for
884
- '@sap.hierarchy.level.for', // -> sap:hierarchy-level-for
885
- '@sap.hierarchy.drill.state.for', // -> sap:hierarchy-drill-state-for
886
- '@sap.hierarchy.node.descendant.count.for' // -> sap:hierarchy-node-descendant-count-for
887
- ];
888
-
889
950
  super(v, attributes, csn);
890
951
  // TIPHANACDS-4180
891
952
  if(this.v2)
892
953
  {
893
954
  if(csn['@odata.etag'] == true || csn['@cds.etag'] == true)
894
- this.ConcurrencyMode='Fixed'
955
+ this._edmAttributes.ConcurrencyMode='Fixed'
895
956
 
896
957
  // translate the following @sap annos as xml attributes to the Property
897
- edmUtils.forAll(csn, (v, p) => {
898
- // @ts-ignore
899
- if (Property.SAP_Annotation_Attribute_WhiteList.includes(p))
958
+ forEach(csn, (p, v) => {
959
+ if (p in Property.SAP_Annotation_Attributes)
900
960
  this.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : v });
901
961
  });
902
962
  }
@@ -906,7 +966,7 @@ module.exports = function (options, error) {
906
966
  // added a @Core.ComputedDefaultValue for complex defaults
907
967
  if (csn.default && !csn['@Core.ComputedDefaultValue']) {
908
968
 
909
- let def = csn.default;
969
+ const def = csn.default;
910
970
  // if def has a value, it's a simple value
911
971
  let defVal = def.val;
912
972
  // if it's a simple value with signs, produce a string representation
@@ -927,9 +987,7 @@ module.exports = function (options, error) {
927
987
  Additionally: The attribute is named 'Default' in V2 and 'DefaultValue' in V4
928
988
  */
929
989
  if(this.v4)
930
- this[`Default${this.v4 ? 'Value' : ''}`] = ['cds.Boolean', 'cds.Binary', 'cds.LargeBinary', 'cds.Integer64', 'cds.Integer'].includes(csn.type)
931
- ? defVal
932
- : edmUtils.escapeString(defVal);
990
+ this._edmAttributes[`Default${this.v4 ? 'Value' : ''}`] = defVal;
933
991
  }
934
992
  }
935
993
  }
@@ -938,6 +996,17 @@ module.exports = function (options, error) {
938
996
  // static get isProperty() { return true }
939
997
  }
940
998
 
999
+ // the annotations in this array shall become exposed as Property attributes in
1000
+ // the V2 metadata.xml
1001
+ Property.SAP_Annotation_Attributes = {
1002
+ '@sap.hierarchy.node.for':1, // -> sap:hierarchy-node-for
1003
+ '@sap.hierarchy.parent.node.for':1, // -> sap:hierarchy-parent-node-for
1004
+ '@sap.hierarchy.level.for':1, // -> sap:hierarchy-level-for
1005
+ '@sap.hierarchy.drill.state.for':1, // -> sap:hierarchy-drill-state-for
1006
+ '@sap.hierarchy.node.descendant.count.for':1, // -> sap:hierarchy-node-descendant-count-for
1007
+ '@sap.parameter':1
1008
+ };
1009
+
941
1010
  class PropertyRef extends Node
942
1011
  {
943
1012
  constructor(v, Name, Alias) {
@@ -945,7 +1014,7 @@ module.exports = function (options, error) {
945
1014
  }
946
1015
 
947
1016
  toJSON() {
948
- return this.Alias ? { [this.Alias]:this.Name } : this.Name;
1017
+ return this._edmAttributes.Alias ? { [this._edmAttributes.Alias]:this._edmAttributes.Name } : this._edmAttributes.Name;
949
1018
  }
950
1019
  }
951
1020
 
@@ -956,12 +1025,12 @@ module.exports = function (options, error) {
956
1025
  super(v, attributes, csn);
957
1026
 
958
1027
  if(mode != null)
959
- this.Mode = mode;
1028
+ this._edmAttributes.Mode = mode;
960
1029
 
961
1030
  // V2 XML: Parameters that are not explicitly marked as Nullable or NotNullable in the CSN must become Nullable=true
962
1031
  // V2 XML Spec does only mention default Nullable=true for Properties not for Parameters so omitting Nullable=true let
963
1032
  // the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
964
- if(this.v2 && this.Nullable === undefined)
1033
+ if(this.v2 && this._edmAttributes.Nullable === undefined)
965
1034
  this.setXml({Nullable: true});
966
1035
  }
967
1036
 
@@ -969,7 +1038,7 @@ module.exports = function (options, error) {
969
1038
  {
970
1039
  // we need Name but NO $kind, can't use standard to JSON()
971
1040
  let json = Object.create(null);
972
- json['$Name'] = this.Name;
1041
+ json['$Name'] = this._edmAttributes.Name;
973
1042
  return this.toJSONattributes(json);
974
1043
  }
975
1044
  }
@@ -985,28 +1054,34 @@ module.exports = function (options, error) {
985
1054
  let [src, tgt] = edmUtils.determineMultiplicity(csn._constraints._partnerCsn || csn);
986
1055
  csn._constraints._multiplicity = csn._constraints._partnerCsn ? [tgt, src] : [src, tgt];
987
1056
 
988
- this.set( {
989
- _type: attributes.Type,
990
- _isCollection: this.isToMany(),
991
- _targetCsn: csn._target
992
- } );
1057
+ this._type = attributes.Type;
1058
+ this._isCollection = this.isToMany();
1059
+ this._targetCsn = csn._target;
993
1060
 
994
1061
  if (this.v4)
995
1062
  {
1063
+ if(options.isStructFormat && this._csn.key)
1064
+ this._edmAttributes.Nullable = false;
1065
+
996
1066
  // either csn has multiplicity or we have to use the multiplicity of the backlink
997
1067
  if(this._isCollection) {
998
- this.Type = `Collection(${attributes.Type})`
1068
+ this._edmAttributes.Type = `Collection(${attributes.Type})`
999
1069
  // attribute Nullable is not allowed in combination with Collection (see Spec)
1000
1070
  // Even if min cardinality is > 0, remove Nullable, because the implicit OData contract
1001
1071
  // is that a navigation property must either return an empty collection or all collection
1002
1072
  // values are !null (with other words: a collection must never return [1,2,null,3])
1003
- delete this.Nullable;
1073
+ delete this._edmAttributes.Nullable;
1004
1074
  }
1005
1075
  // we have exactly one selfReference or the default partner
1006
- let partner = (!csn.$noPartner && csn._selfReferences.length === 1) ? csn._selfReferences[0] : csn._constraints._partnerCsn;
1076
+ let partner =
1077
+ !csn.$noPartner ?
1078
+ csn._selfReferences.length === 1
1079
+ ? csn._selfReferences[0]
1080
+ : csn._constraints._partnerCsn
1081
+ : undefined;
1007
1082
  if(partner && partner['@odata.navigable'] !== false) {
1008
1083
  // $abspath[0] is main entity
1009
- this.Partner = partner.$abspath.slice(1).join(options.pathDelimiter);
1084
+ this._edmAttributes.Partner = partner.$abspath.slice(1).join(options.pathDelimiter);
1010
1085
  }
1011
1086
 
1012
1087
  /*
@@ -1019,9 +1094,9 @@ module.exports = function (options, error) {
1019
1094
  2) ContainsTarget stems from the @odata.contained annotation
1020
1095
  */
1021
1096
  if(csn['@odata.contained'] == true || csn.containsTarget) {
1022
- this.ContainsTarget = true;
1097
+ this._edmAttributes.ContainsTarget = true;
1023
1098
  }
1024
- if(this.ContainsTarget === undefined && csn.type === 'cds.Composition') {
1099
+ if(this._edmAttributes.ContainsTarget === undefined && csn.type === 'cds.Composition') {
1025
1100
  // Delete is redundant in containment
1026
1101
  // TODO: to be specified via @sap.on.delete
1027
1102
  this.append(new OnDelete(v, { Action: 'Cascade' } ) );
@@ -1032,7 +1107,7 @@ module.exports = function (options, error) {
1032
1107
  if (this.v2 && this.isNotNullable()) {
1033
1108
  // in V2 not null must be expressed with target cardinality of 1 or more,
1034
1109
  // store Nullable=false and evaluate in determineMultiplicity()
1035
- delete this.Nullable;
1110
+ delete this._edmAttributes.Nullable;
1036
1111
  }
1037
1112
 
1038
1113
  // store NavProp reference in the model for bidirectional $Partner tagging (done in getReferentialConstraints())
@@ -1076,10 +1151,10 @@ module.exports = function (options, error) {
1076
1151
  switch(c.kind) {
1077
1152
  case 'ReferentialConstraint':
1078
1153
  // collect ref constraints in dictionary
1079
- json_constraints[c.Property] = c.ReferencedProperty;
1154
+ json_constraints[c._edmAttributes.Property] = c._edmAttributes.ReferencedProperty;
1080
1155
  break;
1081
1156
  case 'OnDelete':
1082
- json['$OnDelete'] = c.Action;
1157
+ json['$OnDelete'] = c._edmAttributes.Action;
1083
1158
  break;
1084
1159
  default:
1085
1160
  error(null, 'Please debug me: Unhandled NavProp child: ' + c.kind);
@@ -1110,6 +1185,12 @@ module.exports = function (options, error) {
1110
1185
 
1111
1186
  class ReferentialConstraint extends Node
1112
1187
  {
1188
+ constructor(v, attributes, csn) {
1189
+ super(v, attributes, csn);
1190
+ this._d = null;
1191
+ this._p = null;
1192
+ }
1193
+
1113
1194
  innerXML(indent)
1114
1195
  {
1115
1196
  if(this._d && this._p)
@@ -1141,12 +1222,12 @@ module.exports = function (options, error) {
1141
1222
  {
1142
1223
  // short form: key: value
1143
1224
  const inlineConstExpr =
1144
- [ 'Edm.Binary', 'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Double', 'Edm.Duration', 'Edm.Guid',
1225
+ [ 'Edm.Binary', 'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Double', 'Edm.Duration', 'Edm.Guid',
1145
1226
  'Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.SByte','Edm.Single', /*'Edm.Stream',*/ 'Edm.String', 'Edm.TimeOfDay',
1146
1227
  /* UI.xml: defines Annotations with generic type 'Edm.PrimitiveType' */
1147
- 'Edm.PrimitiveType', 'Bool'
1148
- // Official JSON V4.01 Spec defines these paths as constant inline expression (OKRA requires them as explicit exprs):
1149
- // 'AnnotationPath', 'ModelElementPath', 'NavigationPropertyPath', 'PropertyPath',
1228
+ 'Edm.PrimitiveType', 'Bool',
1229
+ // Official JSON V4.01 Spec defines these paths as constant inline expression:
1230
+ 'AnnotationPath', 'ModelElementPath', 'NavigationPropertyPath', 'PropertyPath',
1150
1231
  ];
1151
1232
 
1152
1233
  const dict = this._jsonOnlyAttributes;
@@ -1157,20 +1238,13 @@ module.exports = function (options, error) {
1157
1238
  switch(inline[0])
1158
1239
  {
1159
1240
  /* short notation for Edm.Boolean, Edm.String and Edm.Float, see internal project:
1160
- edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
1161
- */
1241
+ edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
1242
+ */
1162
1243
  case 'Edm.Boolean':
1163
1244
  v = (v=='true'?true:(v=='false'?false:v));
1164
1245
  // eslint-no-fallthrough
1165
- case 'Edm.String':
1166
- // eslint-no-fallthrough
1167
- case 'Edm.Float':
1168
- // eslint-no-fallthrough
1169
- return v;
1170
1246
  default:
1171
- // OKRA requires this for JSON->XML mapping
1172
- // because they didn't want to lookup the type in the vocabulary
1173
- return { '$Cast': v, '$Type': inline[0] };
1247
+ return v;
1174
1248
  }
1175
1249
  }
1176
1250
  else
@@ -1200,13 +1274,12 @@ module.exports = function (options, error) {
1200
1274
  constructor(v, target)
1201
1275
  {
1202
1276
  super(v, { Target: target });
1203
- if (this.v2)
1204
- this.setXml( { xmlns : 'http://docs.oasis-open.org/odata/ns/edm' } );
1277
+ this.setXml( { xmlns : this.v2 ? 'http://docs.oasis-open.org/odata/ns/edm' : undefined } );
1205
1278
  }
1206
1279
 
1207
1280
  toJSONattributes(json)
1208
1281
  {
1209
- edmUtils.forAll(this, (v,p) => {
1282
+ forEach(this._edmAttributes, (p, v) => {
1210
1283
  if (p !== 'Target')
1211
1284
  json[p[0] === '@' ? p : '$' + p] = v;
1212
1285
  });
@@ -1255,7 +1328,7 @@ module.exports = function (options, error) {
1255
1328
  }
1256
1329
 
1257
1330
  getJsonFQTermName() {
1258
- return '@' + this.Term + (this.Qualifier ? '#' + this.Qualifier : '');
1331
+ return '@' + this._edmAttributes.Term + (this._edmAttributes.Qualifier ? '#' + this._edmAttributes.Qualifier : '');
1259
1332
  }
1260
1333
  }
1261
1334
 
@@ -1272,11 +1345,11 @@ module.exports = function (options, error) {
1272
1345
  {
1273
1346
  toJSONattributes(json)
1274
1347
  {
1275
- if(this.Type)
1276
- json['@type'] = this.Type;
1277
- let keys = Object.keys(this).filter(k => k !== 'Type');
1348
+ if(this._edmAttributes.Type)
1349
+ json['@type'] = this._edmAttributes.Type;
1350
+ let keys = Object.keys(this._edmAttributes).filter(k => k !== 'Type');
1278
1351
  for(const key of keys)
1279
- json['$'+key] = this[key];
1352
+ json['$'+key] = this._edmAttributes[key];
1280
1353
  }
1281
1354
 
1282
1355
  toJSONchildren(json)
@@ -1296,7 +1369,7 @@ module.exports = function (options, error) {
1296
1369
  json[n] = a;
1297
1370
  });
1298
1371
  // render property as const expr (or subnode)
1299
- json[c.Property] = c.toJSON();
1372
+ json[c._edmAttributes.Property] = c.toJSON();
1300
1373
  break;
1301
1374
  }
1302
1375
  default:
@@ -1311,7 +1384,7 @@ module.exports = function (options, error) {
1311
1384
  constructor(v, property)
1312
1385
  {
1313
1386
  super(v);
1314
- this.Property = property;
1387
+ this._edmAttributes.Property = property;
1315
1388
  }
1316
1389
 
1317
1390
  toJSON()
@@ -1325,7 +1398,7 @@ module.exports = function (options, error) {
1325
1398
  }
1326
1399
  }
1327
1400
  mergeJSONannotations() {
1328
- return super.mergeJSONAnnotations(this.Property);
1401
+ return super.mergeJSONAnnotations(this._edmAttributes.Property);
1329
1402
  }
1330
1403
  }
1331
1404
 
@@ -1334,14 +1407,10 @@ module.exports = function (options, error) {
1334
1407
  constructor(v, kind, details)
1335
1408
  {
1336
1409
  super(v, details);
1337
- this.setKind(kind);
1410
+ this._kind = kind;
1338
1411
  }
1339
1412
 
1340
- setKind(kind)
1341
- {
1342
- Object.defineProperty(this, 'kind',
1343
- { get: function() { return kind; }});
1344
- }
1413
+ get kind() { return this._kind; }
1345
1414
  }
1346
1415
 
1347
1416
  class ValueThing extends Thing
@@ -1349,14 +1418,14 @@ module.exports = function (options, error) {
1349
1418
  constructor(v, kind, value)
1350
1419
  {
1351
1420
  super(v, kind, undefined);
1352
- this.set( { _value : value });
1421
+ this._value = value;
1353
1422
  }
1354
1423
 
1355
1424
  toXML(indent='')
1356
1425
  {
1357
1426
  let kind = this.kind;
1358
1427
  let xml = indent + '<' + kind + this.toXMLattributes();
1359
- xml += (this._value !== undefined ? '>' + edmUtils.escapeString(this._value) + '</' + kind + '>' : '/>');
1428
+ xml += (this._value !== undefined ? '>' + edmUtils.escapeStringForText(this._value) + '</' + kind + '>' : '/>');
1360
1429
  return xml;
1361
1430
  }
1362
1431
 
@@ -1411,10 +1480,11 @@ module.exports = function (options, error) {
1411
1480
  }
1412
1481
  class Cast extends AnnotationBase {
1413
1482
  toXMLattributes() {
1483
+ // TODO: Why json?
1414
1484
  if(this._jsonOnlyAttributes['Collection'])
1415
- return ` Type="Collection(${this.Type})"`
1485
+ return ` Type="Collection(${this._edmAttributes.Type})"`
1416
1486
  else
1417
- return ` Type="${this.Type}"`
1487
+ return ` Type="${this._edmAttributes.Type}"`
1418
1488
  }
1419
1489
  toJSON() {
1420
1490
  const json = this.mergeJSONAnnotations();
@@ -1453,7 +1523,7 @@ module.exports = function (options, error) {
1453
1523
 
1454
1524
  toJSONattributes(json) // including Name
1455
1525
  {
1456
- edmUtils.forAll(this, (v,p) => {
1526
+ forEach(this._edmAttributes, (p, v) => {
1457
1527
  json[p[0] === '@' ? p : '$' + p] = v;
1458
1528
  });
1459
1529
  return json;
@@ -1482,10 +1552,10 @@ module.exports = function (options, error) {
1482
1552
  constructor(v, details, navProp, fromRole, toRole, multiplicity)
1483
1553
  {
1484
1554
  super(v, details);
1485
- this.set( { _end: [] });
1486
- this._end.push(
1555
+ this._end = [
1487
1556
  new End(v, { Role: fromRole[0], Type: fromRole[1], Multiplicity: multiplicity[0] } ),
1488
- new End(v, { Role: toRole[0], Type: toRole[1], Multiplicity: multiplicity[1] } ) );
1557
+ new End(v, { Role: toRole[0], Type: toRole[1], Multiplicity: multiplicity[1] } )
1558
+ ];
1489
1559
 
1490
1560
  // set Delete:Cascade on composition end
1491
1561
  if(navProp._csn.type === 'cds.Composition')
@@ -1516,7 +1586,7 @@ module.exports = function (options, error) {
1516
1586
  );
1517
1587
  }
1518
1588
  getDuplicateMessage() {
1519
- return `Association "${this.Association}"`
1589
+ return `Association "${this._edmAttributes.Association}"`
1520
1590
  }
1521
1591
  }
1522
1592
 
@@ -1527,8 +1597,8 @@ module.exports = function (options, error) {
1527
1597
  function(v, from, to, c)
1528
1598
  {
1529
1599
  let node = new ReferentialConstraint(v, {});
1530
- node.set({ _d: new Dependent(v, { Role: from } ) });
1531
- node.set({ _p: new Principal(v, { Role: to } ) });
1600
+ node._d = new Dependent(v, { Role: from } );
1601
+ node._p = new Principal(v, { Role: to } );
1532
1602
 
1533
1603
  edmUtils.forAll(c, cv => {
1534
1604
  node._d.append(new PropertyRef(v, cv[0].join(options.pathDelimiter)));
@@ -1592,3 +1662,5 @@ module.exports = function (options, error) {
1592
1662
  }
1593
1663
 
1594
1664
  } // instance function
1665
+
1666
+ module.exports = { EdmTypeFacetMap, EdmTypeFacetNames, EdmPrimitiveTypeMap, getEdm };