@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
@@ -1,96 +0,0 @@
1
- // The module traverses over a CSN or partial one and sets $path on the non-structural nodes and references.
2
-
3
- const structuralNodeHandlers = {
4
- definitions: traverseDict,
5
- elements: traverseDict,
6
- actions: traverseDict,
7
- params: traverseDict,
8
- items: traverseTyped,
9
- enum: traverseDict,
10
- returns: traverseTyped,
11
- on: traverseArray,
12
- keys: traverseArray,
13
- ref: traverseRef,
14
- query: traverseTyped,
15
- SELECT: traverseTyped,
16
- SET: traverseTyped,
17
- args: traverseArray,
18
- columns: traverseArray,
19
- projection: traverseTyped,
20
- from: traverseTyped,
21
- mixin: traverseDict,
22
- where: traverseArray,
23
- orderBy: traverseArray,
24
- groupBy: traverseArray,
25
- having: traverseArray,
26
- xpr: traverseArray,
27
- expand: traverseArray,
28
- inline: traverseArray,
29
- }
30
-
31
- function attachPath(csn) {
32
- traverseDict(csn.definitions, ['definitions']);
33
- }
34
-
35
- function attachPathOnPartialCSN(csnPart, pathPrefix) {
36
- if(Array.isArray(csnPart))
37
- traverseArray(csnPart, pathPrefix);
38
- else
39
- traverseDict(csnPart, pathPrefix);
40
- }
41
-
42
- function traverseRef(obj, path) {
43
- if(!obj) return;
44
- setPath(obj, path);
45
- traverseArray(obj, path);
46
- }
47
-
48
- function traverseArray(obj, path) {
49
- if(!Array.isArray(obj)) return;
50
- obj.forEach( ( element, index ) => traverseTyped(element, path.concat(index)));
51
- }
52
-
53
- function traverseDict(obj, path) {
54
- if(!obj || typeof obj !== 'object') return;
55
- forAllEnumerableProperties(obj, name => {
56
- const ipath = path.concat(name);
57
- setPath(obj[name], ipath);
58
- traverseTyped(obj[name], ipath);
59
- })
60
- }
61
-
62
- function traverseDictArray(obj, path) {
63
- if(!obj || typeof obj !== 'object') return;
64
- forAllEnumerableProperties(obj, name => {
65
- const ipath = path.concat(name);
66
- setPath(obj[name], ipath);
67
- traverseArray(obj[name], ipath);
68
- })
69
- }
70
-
71
- function traverseTyped(obj, path) {
72
- if(!obj || typeof obj !== 'object') return;
73
- forAllEnumerableProperties(obj, name => {
74
- if(name[0]==='@') return; // skip annotations
75
- const func = structuralNodeHandlers[name];
76
- if(func)
77
- func(obj[name], path.concat(name));
78
- else if(path[path.length-2] === 'columns')
79
- traverseDictArray(obj[name], path.concat(name)); // for columns
80
- })
81
- }
82
-
83
- function setPath(obj, path) {
84
- if(!obj || typeof obj !== 'object') return;
85
- if(path.length>0)
86
- Object.defineProperty( obj, '$path', { value: path, configurable: true, writable: true, enumerable: false } );
87
- }
88
-
89
- function forAllEnumerableProperties(obj, callback) {
90
- Object.keys(obj).forEach(callback);
91
- }
92
-
93
- module.exports = {
94
- attachPath,
95
- attachPathOnPartialCSN,
96
- }
@@ -1,59 +0,0 @@
1
- 'use strict';
2
-
3
- const { forEachManagedAssociation } = require('./utils');
4
- const { attachPath, attachPathOnPartialCSN } = require('./attachPath');
5
-
6
- /**
7
- * This module runs through the model and for each managed association in it,
8
- * in case the foreign keys are structured, it is expanding them. Example:
9
- * entity A {
10
- * toB: association to B { stru };
11
- * } // -> CSN: keys:[ { ref:['stru'] } ]
12
- *
13
- * entity B {
14
- * stru: {
15
- * subid: Integer;
16
- * }
17
- * }
18
- * after expand -> keys:[ { ref: ['stru_subid'] } ]
19
- */
20
- module.exports = function (csn, referenceFlattener, csnUtils, isExternalServiceMember) {
21
-
22
- forEachManagedAssociation(csn, (element) => {
23
- if (element.keys) {
24
- expandStructuredKeysForAssociation(element, referenceFlattener);
25
- }
26
- }, isExternalServiceMember);
27
-
28
- // update paths and resolve references
29
- attachPath(csn);
30
- referenceFlattener.resolveAllReferences(csn, csnUtils.inspectRef, csnUtils.isStructured);
31
-
32
- function expandStructuredKeysForAssociation(assoc, referenceFlattener) {
33
- let newKeys = [];
34
- for (let key of assoc.keys) {
35
- // when are assigned $paths and when not???
36
- let paths = key.$paths;
37
- if (paths) {
38
- let lastPath = paths[paths.length - 1];
39
- let generatedElements = referenceFlattener.getGeneratedElementsForPath(lastPath);
40
- if (generatedElements) {
41
- generatedElements.forEach(elementName => {
42
- let newRef = { ref: [elementName] };
43
- if (key.as) {
44
- newRef.as = elementName.replace(key.ref[0], key.as);
45
- }
46
- newKeys.push(newRef);
47
- })
48
- continue;
49
- }
50
- }
51
- newKeys.push(key);
52
- }
53
-
54
- if (newKeys.length) {
55
- attachPathOnPartialCSN(newKeys, assoc.$path.concat('keys'));
56
- assoc.keys = newKeys;
57
- }
58
- }
59
- }
@@ -1,261 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * The module handles the processing of foreign key for managed associations.
5
- */
6
-
7
- const { copyAnnotations } = require('../../model/csnUtils');
8
- const sortByAssociationDependency = require('./sortByAssociationDependency');
9
- const { flattenStructure } = require('./structureFlattener');
10
- const { setProp } = require('../../base/model');
11
- const { implicitAs } = require('../../model/csnRefs');
12
-
13
- /**
14
- *
15
- * @param {CSN.Model} csn
16
- * @param {*} options
17
- * @param {*} referenceFlattener
18
- * @param {*} csnUtils
19
- * @param {object} error;
20
- */
21
- module.exports = function (csn, options, referenceFlattener, csnUtils, error, isExternalServiceMember) {
22
-
23
- const structuredOData = options.toOdata.odataFormat === 'structured' && options.toOdata.version === 'v4';
24
- const flatKeys = !structuredOData || (structuredOData && options.toOdata.odataForeignKeys);
25
-
26
- // sort all associations by their dependencies
27
- const sortedAssociations = sortByAssociationDependency(csn, referenceFlattener, isExternalServiceMember);
28
-
29
- // generate foreign keys
30
- processSortedAssociations(sortedAssociations, flatKeys);
31
-
32
-
33
- function processSortedAssociations(sortedAssociations, flatKeys,) {
34
- // The map will collect all generated foreign key names for the specific path
35
- let generatedForeignKeyNamesForPath = Object.create(null); // map<path,[key-name]>
36
-
37
- sortedAssociations.forEach(item => {
38
- const { definitionName, structuralNodeName, elementName, element, parent, path } = item;
39
-
40
- if (csnUtils.isManagedAssociationElement(element) && element.keys) {
41
- if (flatKeys) // tackling the ref value in assoc.keys
42
- takeoverForeignKeysOfTargetAssociations(element, path, generatedForeignKeyNamesForPath);
43
- // TODO: move in separate function
44
- fixCardinality(element);
45
- }
46
-
47
- let arrayOfGeneratedForeignKeyNames = generateForeignKeys(definitionName, structuralNodeName, elementName, element, parent, path);
48
- generatedForeignKeyNamesForPath[item.path.join('/')] = arrayOfGeneratedForeignKeyNames;
49
- })
50
-
51
- }
52
-
53
- /**
54
- * if a key is an association and it poins to another association,
55
- * the foreign keys of the target association become primary keys
56
- * in the current association
57
- */
58
- function takeoverForeignKeysOfTargetAssociations(assoc, path, generatedForeignKeyNamesForPath) {
59
- let newResult = [];
60
- assoc.keys.forEach( (key, keyIndex) => {
61
- let keyPath = path.concat('keys', keyIndex);
62
- let resolved = csnUtils.inspectRef(keyPath)
63
- let targetElement = resolved.art;
64
- if (targetElement) {
65
- if (csnUtils.isAssociation(targetElement.type)) {
66
- // association key
67
- expandAssociationKey(key);
68
- } else {
69
- newResult.push(key);
70
- }
71
- } else {
72
- // target element does not exist, warning is already reported, pass the key anyway
73
- newResult.push(key);
74
- }
75
- });
76
-
77
- function expandAssociationKey(key) {
78
- let paths = key.$paths;
79
- if (!paths) return;
80
- let lastPath = paths[paths.length - 1];
81
- let transitionPath = referenceFlattener.getElementTransition(lastPath)
82
- if (transitionPath)
83
- lastPath = transitionPath;
84
- let generatedKeys = generatedForeignKeyNamesForPath[lastPath.join('/')];
85
- if (!generatedKeys) return;
86
- generatedKeys.forEach(fkName => {
87
- let newFkRef = { ref: [fkName] };
88
- if (key.as) {
89
- let alias = fkName.replace(key.ref[0], key.as);
90
- setProp(newFkRef, 'as', alias);
91
- }
92
- newResult.push(newFkRef);
93
- })
94
- } // expandAssociationKey
95
-
96
- assoc.keys = newResult;
97
-
98
- }
99
-
100
- function fixCardinality(assoc) {
101
- if (assoc.notNull) {
102
- if (!assoc.cardinality) {
103
- assoc.cardinality = {};
104
- }
105
- if (assoc.cardinality.min === undefined) {
106
- assoc.cardinality.min = 1;
107
- }
108
- }
109
- }
110
-
111
- /**
112
- * Generates foreign keys and returns their names as an array
113
- */
114
- function generateForeignKeys(definitionName, structuralNodeName, assocName, assoc, parent, path) {
115
- let foreignKeyElements = Object.create(null);
116
-
117
- // First, loop over the keys array of the association and generate the FKs.
118
- // The result of all the FKs for the given association is accumulated
119
- // in the 'foreignKeyElements' dictionary
120
- assoc.keys.forEach( (key, keyIndex) => {
121
- let keyPath = path.concat('keys', keyIndex);
122
-
123
- let foreignKeyElementsForKey = generateForeignKeysForRef(assoc, assocName, key, keyPath);
124
- Object.assign(foreignKeyElements, foreignKeyElementsForKey);
125
- });
126
-
127
- // After that, add the new elements to the definition.
128
- // At the same time:
129
- // -> Check for coliding element's name
130
- // &
131
- // -> Propagate annotations from the association
132
- if (parent.items) // proceed to items of such
133
- parent = parent.items;
134
- if (parent.returns)
135
- parent = parent.returns.items || parent.returns;
136
-
137
- const dictionary = parent[structuralNodeName];
138
- let currElementsNames = Object.keys(parent[structuralNodeName]);
139
- for (const [foreignKeyName, foreignKey] of Object.entries(foreignKeyElements)) {
140
- copyAnnotations(assoc, foreignKey, true);
141
- // Insert artificial element into artifact, with all cross-links
142
- if (dictionary[foreignKeyName]) {
143
- if (!(dictionary[foreignKeyName]['@odata.foreignKey4'] || isDeepEqual(dictionary[foreignKeyName], foreignKey))) {
144
- const path = dictionary[foreignKeyName].$path;
145
- error(null, path, { name: foreignKeyName, art: assocName }, 'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element');
146
- }
147
- }
148
- }
149
-
150
- // make sure the generated foreign key(s) is added right after the association (that it belongs to) in the elements dictionary
151
- const assocIndex = currElementsNames.findIndex(elemName => elemName === assocName);
152
- // if (flatKeys)
153
- currElementsNames.splice(assocIndex + 1, 0, ...Object.keys(foreignKeyElements));
154
-
155
- parent[structuralNodeName] = currElementsNames.reduce((previous, name) => {
156
- previous[name] = dictionary[name] || foreignKeyElements[name];
157
- return previous;
158
- }, Object.create(null));
159
-
160
- return Object.keys(foreignKeyElements);
161
- }
162
-
163
- // FIXME: Very similar code to
164
- // transformUtilsNew::getForeignKeyArtifact & createForeignKeyElement
165
- // Can this be streamlined?
166
- function generateForeignKeysForRef(assoc, assocName, foreignKeyRef, pathInKeysArr, foreignKey4 = assocName) {
167
- // in structured OData, might be more than one generated FKs
168
- let generatedFks = Object.create(null);
169
- const fkArtifact = csnUtils.inspectRef(pathInKeysArr).art;
170
- if(fkArtifact) {
171
- if (csnUtils.isStructured(fkArtifact)) {
172
- processStucturedKey(fkArtifact, assocName, foreignKeyRef);
173
- } else {
174
- // built-in
175
- const foreignKeyElementName = `${assocName.replace(/\./g, '_')}_${foreignKeyRef.as || foreignKeyRef.ref.join('_')}`;
176
- newForeignKey(fkArtifact, foreignKeyElementName);
177
- }
178
- }
179
-
180
- return generatedFks;
181
-
182
- function processStucturedKey(fkArtifact, assocName, foreignKeyRef) {
183
- const subStruct = fkArtifact.elements ? fkArtifact : csnUtils.getFinalBaseType(fkArtifact.type);
184
- const flatElements = flattenStructure(subStruct.elements, subStruct.$path, csnUtils, options, error, undefined, fkArtifact.$path.slice(-1) || []).newFlatElements;
185
- for (const [flatElemName, flatElem] of Object.entries(flatElements)) {
186
- const foreignKeyElementName =
187
- `${assocName.replace(/\./g, '_')}_${foreignKeyRef.as ? flatElemName.replace(implicitAs(foreignKeyRef.ref), foreignKeyRef.as) : flatElemName}`;
188
- newForeignKey(flatElem, foreignKeyElementName);
189
- }
190
- }
191
-
192
- function newForeignKey(fkArtifact, foreignKeyElementName) {
193
- if (fkArtifact.type === 'cds.Association' || fkArtifact.type === 'cds.Composition') {
194
- processAssociationOrComposition(fkArtifact, foreignKeyElementName);
195
- return;
196
- }
197
-
198
- // FIXME: better use transformUtlsNew::createRealFK(...);
199
- let foreignKeyElement = Object.create(null);
200
-
201
- // Transfer selected type properties from target key element
202
- // FIXME: There is currently no other way but to treat the annotation '@odata.Type' as a type property.
203
- for (let prop of ['type', 'length', 'scale', 'precision', 'srid', 'default', '@odata.Type']) {
204
- if (fkArtifact[prop] !== undefined) {
205
- foreignKeyElement[prop] = fkArtifact[prop];
206
- }
207
- }
208
- // If the association is non-fkArtifact resp. key, so should be the foreign key field
209
- for (let prop of ['notNull', 'key']) {
210
- if (assoc[prop] !== undefined) {
211
- foreignKeyElement[prop] = assoc[prop];
212
- }
213
- }
214
-
215
- foreignKeyElement['@odata.foreignKey4'] = foreignKey4;
216
- if (flatKeys) foreignKeyRef.$generatedFieldName = foreignKeyElementName;
217
- setProp(foreignKeyElement, '$path', pathInKeysArr); // attach $path to the newly created element - used for inspectRef in processAssociationOrComposition
218
- if (assoc.$location) {
219
- setProp(foreignKeyElement, '$location', assoc.$location);
220
- }
221
- generatedFks[foreignKeyElementName] = foreignKeyElement;
222
- }
223
-
224
- function processAssociationOrComposition(fkArtifact, foreignKeyElementName) {
225
- fkArtifact.keys.forEach((keyRef,keyId) => {
226
- const path = fkArtifact.$path.concat('keys').concat(keyId);
227
- const fksForAssoc = generateForeignKeysForRef(assoc, foreignKeyElementName, keyRef, path, foreignKey4);
228
- Object.assign(generatedFks, fksForAssoc);
229
- })
230
- }
231
- }
232
- }
233
-
234
- /**
235
- *
236
- * @param {object} obj
237
- * @param {*} other
238
- * @returns {boolean} Whether 'obj' and 'other' are deeply equal. We need the
239
- * deep comparison because of annotations that have structured values and they
240
- * are propagated to the generated foreign keys.
241
- */
242
- function isDeepEqual(obj, other) {
243
- const objectKeys = Object.keys(obj);
244
- const otherKeys = Object.keys(other);
245
-
246
- if (objectKeys.length !== otherKeys.length)
247
- return false;
248
-
249
- for (let key of objectKeys) {
250
- const areValuesObjects = (obj[key] != null && typeof obj[key] === 'object')
251
- && (other[key] !== null && typeof other[key] === 'object');
252
-
253
- if (areValuesObjects) {
254
- if (!isDeepEqual(obj[key], other[key]))
255
- return false;
256
- } else if (obj[key] !== other[key]) {
257
- return false;
258
- }
259
- }
260
- return true;
261
- }