@sap/cds-compiler 2.13.8 → 3.0.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 (127) hide show
  1. package/CHANGELOG.md +155 -1594
  2. package/bin/cdsc.js +144 -66
  3. package/doc/CHANGELOG_ARCHIVE.md +1592 -0
  4. package/doc/CHANGELOG_BETA.md +3 -4
  5. package/doc/CHANGELOG_DEPRECATED.md +35 -1
  6. package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
  7. package/doc/Versioning.md +20 -1
  8. package/lib/api/.eslintrc.json +2 -2
  9. package/lib/api/main.js +237 -122
  10. package/lib/api/options.js +17 -88
  11. package/lib/api/validate.js +12 -16
  12. package/lib/base/keywords.js +216 -109
  13. package/lib/base/message-registry.js +152 -37
  14. package/lib/base/messages.js +145 -83
  15. package/lib/base/model.js +44 -2
  16. package/lib/base/optionProcessorHelper.js +19 -0
  17. package/lib/checks/actionsFunctions.js +7 -5
  18. package/lib/checks/annotationsOData.js +11 -32
  19. package/lib/checks/arrayOfs.js +1 -34
  20. package/lib/checks/cdsPersistence.js +1 -0
  21. package/lib/checks/elements.js +6 -6
  22. package/lib/checks/invalidTarget.js +1 -1
  23. package/lib/checks/nonexpandableStructured.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +2 -1
  25. package/lib/checks/selectItems.js +5 -1
  26. package/lib/checks/types.js +4 -2
  27. package/lib/checks/utils.js +2 -2
  28. package/lib/checks/validator.js +4 -5
  29. package/lib/compiler/assert-consistency.js +16 -10
  30. package/lib/compiler/base.js +1 -0
  31. package/lib/compiler/builtins.js +98 -9
  32. package/lib/compiler/checks.js +22 -70
  33. package/lib/compiler/define.js +61 -13
  34. package/lib/compiler/extend.js +79 -14
  35. package/lib/compiler/finalize-parse-cdl.js +46 -29
  36. package/lib/compiler/index.js +100 -37
  37. package/lib/compiler/moduleLayers.js +7 -0
  38. package/lib/compiler/populate.js +19 -18
  39. package/lib/compiler/propagator.js +7 -4
  40. package/lib/compiler/resolve.js +297 -234
  41. package/lib/compiler/shared.js +107 -102
  42. package/lib/compiler/tweak-assocs.js +16 -11
  43. package/lib/compiler/utils.js +5 -0
  44. package/lib/edm/annotations/genericTranslation.js +93 -21
  45. package/lib/edm/csn2edm.js +230 -115
  46. package/lib/edm/edm.js +305 -226
  47. package/lib/edm/edmPreprocessor.js +509 -438
  48. package/lib/edm/edmUtils.js +31 -45
  49. package/lib/gen/Dictionary.json +98 -22
  50. package/lib/gen/language.checksum +1 -1
  51. package/lib/gen/language.interp +10 -30
  52. package/lib/gen/language.tokens +105 -114
  53. package/lib/gen/languageLexer.interp +1 -34
  54. package/lib/gen/languageLexer.js +889 -1007
  55. package/lib/gen/languageLexer.tokens +95 -106
  56. package/lib/gen/languageParser.js +20786 -22199
  57. package/lib/json/csnVersion.js +10 -11
  58. package/lib/json/from-csn.js +59 -51
  59. package/lib/json/to-csn.js +10 -10
  60. package/lib/language/antlrParser.js +2 -2
  61. package/lib/language/docCommentParser.js +62 -39
  62. package/lib/language/errorStrategy.js +52 -40
  63. package/lib/language/genericAntlrParser.js +348 -229
  64. package/lib/language/language.g4 +629 -653
  65. package/lib/language/multiLineStringParser.js +14 -42
  66. package/lib/language/textUtils.js +44 -0
  67. package/lib/main.d.ts +46 -43
  68. package/lib/main.js +108 -79
  69. package/lib/model/csnRefs.js +34 -7
  70. package/lib/model/csnUtils.js +337 -332
  71. package/lib/model/enrichCsn.js +1 -0
  72. package/lib/model/revealInternalProperties.js +30 -10
  73. package/lib/model/sortViews.js +32 -31
  74. package/lib/modelCompare/compare.js +6 -6
  75. package/lib/optionProcessor.js +73 -46
  76. package/lib/render/.eslintrc.json +1 -1
  77. package/lib/render/DuplicateChecker.js +4 -7
  78. package/lib/render/manageConstraints.js +70 -2
  79. package/lib/render/toCdl.js +1042 -882
  80. package/lib/render/toHdbcds.js +195 -245
  81. package/lib/render/toRename.js +44 -22
  82. package/lib/render/toSql.js +225 -241
  83. package/lib/render/utils/common.js +145 -15
  84. package/lib/render/utils/sql.js +20 -19
  85. package/lib/sql-identifier.js +6 -0
  86. package/lib/transform/db/.eslintrc.json +4 -3
  87. package/lib/transform/db/associations.js +2 -2
  88. package/lib/transform/db/cdsPersistence.js +5 -15
  89. package/lib/transform/db/constraints.js +4 -2
  90. package/lib/transform/db/expansion.js +22 -16
  91. package/lib/transform/db/flattening.js +109 -80
  92. package/lib/transform/db/transformExists.js +7 -7
  93. package/lib/transform/db/views.js +9 -6
  94. package/lib/transform/draft/.eslintrc.json +2 -2
  95. package/lib/transform/draft/db.js +6 -6
  96. package/lib/transform/draft/odata.js +6 -7
  97. package/lib/transform/forHanaNew.js +62 -48
  98. package/lib/transform/forOdataNew.js +49 -50
  99. package/lib/transform/localized.js +31 -20
  100. package/lib/transform/odata/toFinalBaseType.js +16 -14
  101. package/lib/transform/odata/typesExposure.js +146 -198
  102. package/lib/transform/odata/utils.js +1 -38
  103. package/lib/transform/transformUtilsNew.js +67 -84
  104. package/lib/transform/translateAssocsToJoins.js +7 -3
  105. package/lib/transform/universalCsn/.eslintrc.json +2 -2
  106. package/lib/transform/universalCsn/coreComputed.js +16 -9
  107. package/lib/transform/universalCsn/universalCsnEnricher.js +60 -10
  108. package/lib/utils/file.js +3 -3
  109. package/lib/utils/moduleResolve.js +13 -6
  110. package/lib/utils/timetrace.js +20 -21
  111. package/package.json +35 -4
  112. package/share/messages/message-explanations.json +2 -1
  113. package/share/messages/syntax-expected-integer.md +37 -0
  114. package/doc/ApiMigration.md +0 -237
  115. package/doc/CommandLineMigration.md +0 -58
  116. package/doc/ErrorMessages.md +0 -175
  117. package/doc/FioriAnnotations.md +0 -94
  118. package/doc/ODataTransformation.md +0 -273
  119. package/lib/backends.js +0 -529
  120. package/lib/fix_antlr4-8_warning.js +0 -56
  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 -296
  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
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,12 +212,12 @@ 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')
215
+ forEach(this._edmAttributes, (p, v) => {
216
+ if (v !== undefined && typeof v !== 'object')
140
217
  tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
141
218
  });
142
- edmUtils.forAll(this._xmlOnlyAttributes, (v,p) => {
143
- if (typeof v !== 'object')
219
+ forEach(this._xmlOnlyAttributes, (p, v) => {
220
+ if (v !== undefined && typeof v !== 'object')
144
221
  tmpStr += ' ' + p + '="' + edmUtils.escapeStringForAttributeValue(v) + '"'
145
222
  });
146
223
  return tmpStr;
@@ -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
666
  // check whether this is a scalar type (or array of scalar type) or a named type
603
- let scalarType = undefined;
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,29 +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
- '@sap.parameter'
888
- ];
889
-
890
950
  super(v, attributes, csn);
891
951
  // TIPHANACDS-4180
892
952
  if(this.v2)
893
953
  {
894
954
  if(csn['@odata.etag'] == true || csn['@cds.etag'] == true)
895
- this.ConcurrencyMode='Fixed'
955
+ this._edmAttributes.ConcurrencyMode='Fixed'
896
956
 
897
957
  // translate the following @sap annos as xml attributes to the Property
898
- edmUtils.forAll(csn, (v, p) => {
899
- // @ts-ignore
900
- if (Property.SAP_Annotation_Attribute_WhiteList.includes(p))
958
+ forEach(csn, (p, v) => {
959
+ if (p in Property.SAP_Annotation_Attributes)
901
960
  this.setXml( { ['sap:' + p.slice(5).replace(/\./g, '-')] : v });
902
961
  });
903
962
  }
@@ -907,7 +966,7 @@ module.exports = function (options, error) {
907
966
  // added a @Core.ComputedDefaultValue for complex defaults
908
967
  if (csn.default && !csn['@Core.ComputedDefaultValue']) {
909
968
 
910
- let def = csn.default;
969
+ const def = csn.default;
911
970
  // if def has a value, it's a simple value
912
971
  let defVal = def.val;
913
972
  // if it's a simple value with signs, produce a string representation
@@ -928,7 +987,7 @@ module.exports = function (options, error) {
928
987
  Additionally: The attribute is named 'Default' in V2 and 'DefaultValue' in V4
929
988
  */
930
989
  if(this.v4)
931
- this[`Default${this.v4 ? 'Value' : ''}`] = defVal;
990
+ this._edmAttributes[`Default${this.v4 ? 'Value' : ''}`] = defVal;
932
991
  }
933
992
  }
934
993
  }
@@ -937,6 +996,17 @@ module.exports = function (options, error) {
937
996
  // static get isProperty() { return true }
938
997
  }
939
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
+
940
1010
  class PropertyRef extends Node
941
1011
  {
942
1012
  constructor(v, Name, Alias) {
@@ -944,7 +1014,7 @@ module.exports = function (options, error) {
944
1014
  }
945
1015
 
946
1016
  toJSON() {
947
- return this.Alias ? { [this.Alias]:this.Name } : this.Name;
1017
+ return this._edmAttributes.Alias ? { [this._edmAttributes.Alias]:this._edmAttributes.Name } : this._edmAttributes.Name;
948
1018
  }
949
1019
  }
950
1020
 
@@ -955,12 +1025,12 @@ module.exports = function (options, error) {
955
1025
  super(v, attributes, csn);
956
1026
 
957
1027
  if(mode != null)
958
- this.Mode = mode;
1028
+ this._edmAttributes.Mode = mode;
959
1029
 
960
1030
  // V2 XML: Parameters that are not explicitly marked as Nullable or NotNullable in the CSN must become Nullable=true
961
1031
  // V2 XML Spec does only mention default Nullable=true for Properties not for Parameters so omitting Nullable=true let
962
1032
  // the client assume that Nullable is false.... Correct Nullable Handling is done inside Parameter constructor
963
- if(this.v2 && this.Nullable === undefined)
1033
+ if(this.v2 && this._edmAttributes.Nullable === undefined)
964
1034
  this.setXml({Nullable: true});
965
1035
  }
966
1036
 
@@ -968,7 +1038,7 @@ module.exports = function (options, error) {
968
1038
  {
969
1039
  // we need Name but NO $kind, can't use standard to JSON()
970
1040
  let json = Object.create(null);
971
- json['$Name'] = this.Name;
1041
+ json['$Name'] = this._edmAttributes.Name;
972
1042
  return this.toJSONattributes(json);
973
1043
  }
974
1044
  }
@@ -984,28 +1054,34 @@ module.exports = function (options, error) {
984
1054
  let [src, tgt] = edmUtils.determineMultiplicity(csn._constraints._partnerCsn || csn);
985
1055
  csn._constraints._multiplicity = csn._constraints._partnerCsn ? [tgt, src] : [src, tgt];
986
1056
 
987
- this.set( {
988
- _type: attributes.Type,
989
- _isCollection: this.isToMany(),
990
- _targetCsn: csn._target
991
- } );
1057
+ this._type = attributes.Type;
1058
+ this._isCollection = this.isToMany();
1059
+ this._targetCsn = csn._target;
992
1060
 
993
1061
  if (this.v4)
994
1062
  {
1063
+ if(options.isStructFormat && this._csn.key)
1064
+ this._edmAttributes.Nullable = false;
1065
+
995
1066
  // either csn has multiplicity or we have to use the multiplicity of the backlink
996
1067
  if(this._isCollection) {
997
- this.Type = `Collection(${attributes.Type})`
1068
+ this._edmAttributes.Type = `Collection(${attributes.Type})`
998
1069
  // attribute Nullable is not allowed in combination with Collection (see Spec)
999
1070
  // Even if min cardinality is > 0, remove Nullable, because the implicit OData contract
1000
1071
  // is that a navigation property must either return an empty collection or all collection
1001
1072
  // values are !null (with other words: a collection must never return [1,2,null,3])
1002
- delete this.Nullable;
1073
+ delete this._edmAttributes.Nullable;
1003
1074
  }
1004
1075
  // we have exactly one selfReference or the default partner
1005
- 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;
1006
1082
  if(partner && partner['@odata.navigable'] !== false) {
1007
1083
  // $abspath[0] is main entity
1008
- this.Partner = partner.$abspath.slice(1).join(options.pathDelimiter);
1084
+ this._edmAttributes.Partner = partner.$abspath.slice(1).join(options.pathDelimiter);
1009
1085
  }
1010
1086
 
1011
1087
  /*
@@ -1018,9 +1094,9 @@ module.exports = function (options, error) {
1018
1094
  2) ContainsTarget stems from the @odata.contained annotation
1019
1095
  */
1020
1096
  if(csn['@odata.contained'] == true || csn.containsTarget) {
1021
- this.ContainsTarget = true;
1097
+ this._edmAttributes.ContainsTarget = true;
1022
1098
  }
1023
- if(this.ContainsTarget === undefined && csn.type === 'cds.Composition') {
1099
+ if(this._edmAttributes.ContainsTarget === undefined && csn.type === 'cds.Composition') {
1024
1100
  // Delete is redundant in containment
1025
1101
  // TODO: to be specified via @sap.on.delete
1026
1102
  this.append(new OnDelete(v, { Action: 'Cascade' } ) );
@@ -1031,7 +1107,7 @@ module.exports = function (options, error) {
1031
1107
  if (this.v2 && this.isNotNullable()) {
1032
1108
  // in V2 not null must be expressed with target cardinality of 1 or more,
1033
1109
  // store Nullable=false and evaluate in determineMultiplicity()
1034
- delete this.Nullable;
1110
+ delete this._edmAttributes.Nullable;
1035
1111
  }
1036
1112
 
1037
1113
  // store NavProp reference in the model for bidirectional $Partner tagging (done in getReferentialConstraints())
@@ -1075,10 +1151,10 @@ module.exports = function (options, error) {
1075
1151
  switch(c.kind) {
1076
1152
  case 'ReferentialConstraint':
1077
1153
  // collect ref constraints in dictionary
1078
- json_constraints[c.Property] = c.ReferencedProperty;
1154
+ json_constraints[c._edmAttributes.Property] = c._edmAttributes.ReferencedProperty;
1079
1155
  break;
1080
1156
  case 'OnDelete':
1081
- json['$OnDelete'] = c.Action;
1157
+ json['$OnDelete'] = c._edmAttributes.Action;
1082
1158
  break;
1083
1159
  default:
1084
1160
  error(null, 'Please debug me: Unhandled NavProp child: ' + c.kind);
@@ -1109,6 +1185,12 @@ module.exports = function (options, error) {
1109
1185
 
1110
1186
  class ReferentialConstraint extends Node
1111
1187
  {
1188
+ constructor(v, attributes, csn) {
1189
+ super(v, attributes, csn);
1190
+ this._d = null;
1191
+ this._p = null;
1192
+ }
1193
+
1112
1194
  innerXML(indent)
1113
1195
  {
1114
1196
  if(this._d && this._p)
@@ -1140,12 +1222,18 @@ module.exports = function (options, error) {
1140
1222
  {
1141
1223
  // short form: key: value
1142
1224
  const inlineConstExpr =
1143
- [ 'Edm.Binary', 'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Double', 'Edm.Duration', 'Edm.Guid',
1144
- 'Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.SByte','Edm.Single', /*'Edm.Stream',*/ 'Edm.String', 'Edm.TimeOfDay',
1225
+ [ 'Edm.Binary', 'Edm.Boolean', 'Edm.Byte', 'Edm.Date', 'Edm.DateTimeOffset', 'Edm.Decimal', 'Edm.Double', 'Edm.Duration', 'Edm.Guid',
1226
+ 'Edm.Int16', 'Edm.Int32', 'Edm.Int64', 'Edm.SByte','Edm.Single', 'Edm.Stream', 'Edm.String', 'Edm.TimeOfDay',
1227
+ // Edm.Geo* according to https://issues.oasis-open.org/browse/ODATA-1323
1228
+ /* 'Edm.Geography', 'Edm.GeographyPoint', 'Edm.GeographyLineString', 'Edm.GeographyPolygon', 'Edm.GeographyMultiPoint',
1229
+ 'Edm.GeographyMultiLineString', 'Edm.GeographyMultiPolygon', 'Edm.GeographyCollection', 'Edm.Geometry', 'Edm.GeometryPoint',
1230
+ 'Edm.GeometryLineString', 'Edm.GeometryPolygon', 'Edm.GeometryMultiPoint', 'Edm.GeometryMultiLineString', 'Edm.GeometryMultiPolygon',
1231
+ 'Edm.GeometryCollection',
1232
+ */
1145
1233
  /* UI.xml: defines Annotations with generic type 'Edm.PrimitiveType' */
1146
- 'Edm.PrimitiveType', 'Bool'
1147
- // Official JSON V4.01 Spec defines these paths as constant inline expression (OKRA requires them as explicit exprs):
1148
- // 'AnnotationPath', 'ModelElementPath', 'NavigationPropertyPath', 'PropertyPath',
1234
+ 'Edm.PrimitiveType', 'Edm.Untyped', 'Bool',
1235
+ // Official JSON V4.01 Spec defines these paths as constant inline expression:
1236
+ 'AnnotationPath', 'ModelElementPath', 'NavigationPropertyPath', 'PropertyPath',
1149
1237
  ];
1150
1238
 
1151
1239
  const dict = this._jsonOnlyAttributes;
@@ -1156,20 +1244,13 @@ module.exports = function (options, error) {
1156
1244
  switch(inline[0])
1157
1245
  {
1158
1246
  /* short notation for Edm.Boolean, Edm.String and Edm.Float, see internal project:
1159
- edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
1160
- */
1247
+ edmx2csn-npm/edm-converters/blob/835d92a1aa6b0be25c56cef85e260c9188187429/lib/edmxV40ToJsonV40/README.md
1248
+ */
1161
1249
  case 'Edm.Boolean':
1162
1250
  v = (v=='true'?true:(v=='false'?false:v));
1163
1251
  // eslint-no-fallthrough
1164
- case 'Edm.String':
1165
- // eslint-no-fallthrough
1166
- case 'Edm.Float':
1167
- // eslint-no-fallthrough
1168
- return v;
1169
1252
  default:
1170
- // OKRA requires this for JSON->XML mapping
1171
- // because they didn't want to lookup the type in the vocabulary
1172
- return { '$Cast': v, '$Type': inline[0] };
1253
+ return v;
1173
1254
  }
1174
1255
  }
1175
1256
  else
@@ -1199,13 +1280,12 @@ module.exports = function (options, error) {
1199
1280
  constructor(v, target)
1200
1281
  {
1201
1282
  super(v, { Target: target });
1202
- if (this.v2)
1203
- this.setXml( { xmlns : 'http://docs.oasis-open.org/odata/ns/edm' } );
1283
+ this.setXml( { xmlns : this.v2 ? 'http://docs.oasis-open.org/odata/ns/edm' : undefined } );
1204
1284
  }
1205
1285
 
1206
1286
  toJSONattributes(json)
1207
1287
  {
1208
- edmUtils.forAll(this, (v,p) => {
1288
+ forEach(this._edmAttributes, (p, v) => {
1209
1289
  if (p !== 'Target')
1210
1290
  json[p[0] === '@' ? p : '$' + p] = v;
1211
1291
  });
@@ -1254,7 +1334,7 @@ module.exports = function (options, error) {
1254
1334
  }
1255
1335
 
1256
1336
  getJsonFQTermName() {
1257
- return '@' + this.Term + (this.Qualifier ? '#' + this.Qualifier : '');
1337
+ return '@' + this._edmAttributes.Term + (this._edmAttributes.Qualifier ? '#' + this._edmAttributes.Qualifier : '');
1258
1338
  }
1259
1339
  }
1260
1340
 
@@ -1271,11 +1351,11 @@ module.exports = function (options, error) {
1271
1351
  {
1272
1352
  toJSONattributes(json)
1273
1353
  {
1274
- if(this.Type)
1275
- json['@type'] = this.Type;
1276
- let keys = Object.keys(this).filter(k => k !== 'Type');
1354
+ if(this._edmAttributes.Type)
1355
+ json['@type'] = this._edmAttributes.Type;
1356
+ let keys = Object.keys(this._edmAttributes).filter(k => k !== 'Type');
1277
1357
  for(const key of keys)
1278
- json['$'+key] = this[key];
1358
+ json['$'+key] = this._edmAttributes[key];
1279
1359
  }
1280
1360
 
1281
1361
  toJSONchildren(json)
@@ -1295,7 +1375,7 @@ module.exports = function (options, error) {
1295
1375
  json[n] = a;
1296
1376
  });
1297
1377
  // render property as const expr (or subnode)
1298
- json[c.Property] = c.toJSON();
1378
+ json[c._edmAttributes.Property] = c.toJSON();
1299
1379
  break;
1300
1380
  }
1301
1381
  default:
@@ -1310,7 +1390,7 @@ module.exports = function (options, error) {
1310
1390
  constructor(v, property)
1311
1391
  {
1312
1392
  super(v);
1313
- this.Property = property;
1393
+ this._edmAttributes.Property = property;
1314
1394
  }
1315
1395
 
1316
1396
  toJSON()
@@ -1324,7 +1404,7 @@ module.exports = function (options, error) {
1324
1404
  }
1325
1405
  }
1326
1406
  mergeJSONannotations() {
1327
- return super.mergeJSONAnnotations(this.Property);
1407
+ return super.mergeJSONAnnotations(this._edmAttributes.Property);
1328
1408
  }
1329
1409
  }
1330
1410
 
@@ -1333,14 +1413,10 @@ module.exports = function (options, error) {
1333
1413
  constructor(v, kind, details)
1334
1414
  {
1335
1415
  super(v, details);
1336
- this.setKind(kind);
1416
+ this._kind = kind;
1337
1417
  }
1338
1418
 
1339
- setKind(kind)
1340
- {
1341
- Object.defineProperty(this, 'kind',
1342
- { get: function() { return kind; }});
1343
- }
1419
+ get kind() { return this._kind; }
1344
1420
  }
1345
1421
 
1346
1422
  class ValueThing extends Thing
@@ -1348,7 +1424,7 @@ module.exports = function (options, error) {
1348
1424
  constructor(v, kind, value)
1349
1425
  {
1350
1426
  super(v, kind, undefined);
1351
- this.set( { _value : value });
1427
+ this._value = value;
1352
1428
  }
1353
1429
 
1354
1430
  toXML(indent='')
@@ -1410,10 +1486,11 @@ module.exports = function (options, error) {
1410
1486
  }
1411
1487
  class Cast extends AnnotationBase {
1412
1488
  toXMLattributes() {
1489
+ // TODO: Why json?
1413
1490
  if(this._jsonOnlyAttributes['Collection'])
1414
- return ` Type="Collection(${this.Type})"`
1491
+ return ` Type="Collection(${this._edmAttributes.Type})"`
1415
1492
  else
1416
- return ` Type="${this.Type}"`
1493
+ return ` Type="${this._edmAttributes.Type}"`
1417
1494
  }
1418
1495
  toJSON() {
1419
1496
  const json = this.mergeJSONAnnotations();
@@ -1452,7 +1529,7 @@ module.exports = function (options, error) {
1452
1529
 
1453
1530
  toJSONattributes(json) // including Name
1454
1531
  {
1455
- edmUtils.forAll(this, (v,p) => {
1532
+ forEach(this._edmAttributes, (p, v) => {
1456
1533
  json[p[0] === '@' ? p : '$' + p] = v;
1457
1534
  });
1458
1535
  return json;
@@ -1481,10 +1558,10 @@ module.exports = function (options, error) {
1481
1558
  constructor(v, details, navProp, fromRole, toRole, multiplicity)
1482
1559
  {
1483
1560
  super(v, details);
1484
- this.set( { _end: [] });
1485
- this._end.push(
1561
+ this._end = [
1486
1562
  new End(v, { Role: fromRole[0], Type: fromRole[1], Multiplicity: multiplicity[0] } ),
1487
- new End(v, { Role: toRole[0], Type: toRole[1], Multiplicity: multiplicity[1] } ) );
1563
+ new End(v, { Role: toRole[0], Type: toRole[1], Multiplicity: multiplicity[1] } )
1564
+ ];
1488
1565
 
1489
1566
  // set Delete:Cascade on composition end
1490
1567
  if(navProp._csn.type === 'cds.Composition')
@@ -1515,7 +1592,7 @@ module.exports = function (options, error) {
1515
1592
  );
1516
1593
  }
1517
1594
  getDuplicateMessage() {
1518
- return `Association "${this.Association}"`
1595
+ return `Association "${this._edmAttributes.Association}"`
1519
1596
  }
1520
1597
  }
1521
1598
 
@@ -1526,8 +1603,8 @@ module.exports = function (options, error) {
1526
1603
  function(v, from, to, c)
1527
1604
  {
1528
1605
  let node = new ReferentialConstraint(v, {});
1529
- node.set({ _d: new Dependent(v, { Role: from } ) });
1530
- node.set({ _p: new Principal(v, { Role: to } ) });
1606
+ node._d = new Dependent(v, { Role: from } );
1607
+ node._p = new Principal(v, { Role: to } );
1531
1608
 
1532
1609
  edmUtils.forAll(c, cv => {
1533
1610
  node._d.append(new PropertyRef(v, cv[0].join(options.pathDelimiter)));
@@ -1591,3 +1668,5 @@ module.exports = function (options, error) {
1591
1668
  }
1592
1669
 
1593
1670
  } // instance function
1671
+
1672
+ module.exports = { EdmTypeFacetMap, EdmTypeFacetNames, EdmPrimitiveTypeMap, getEdm };