@sap/cds-compiler 2.5.2 → 2.11.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.
- package/CHANGELOG.md +235 -9
- package/bin/cdsc.js +44 -27
- package/bin/cdsse.js +1 -0
- package/doc/CHANGELOG_BETA.md +37 -3
- package/lib/api/.eslintrc.json +2 -0
- package/lib/api/main.js +37 -123
- package/lib/api/options.js +27 -15
- package/lib/api/validate.js +34 -9
- package/lib/backends.js +9 -89
- package/lib/base/dictionaries.js +2 -1
- package/lib/base/keywords.js +32 -2
- package/lib/base/message-registry.js +73 -11
- package/lib/base/messages.js +86 -30
- package/lib/base/model.js +6 -6
- package/lib/base/optionProcessorHelper.js +56 -22
- package/lib/checks/defaultValues.js +27 -2
- package/lib/checks/elements.js +1 -6
- package/lib/checks/foreignKeys.js +0 -6
- package/lib/checks/managedWithoutKeys.js +17 -0
- package/lib/checks/nonexpandableStructured.js +38 -0
- package/lib/checks/onConditions.js +9 -45
- package/lib/checks/queryNoDbArtifacts.js +25 -7
- package/lib/checks/selectItems.js +29 -2
- package/lib/checks/types.js +26 -2
- package/lib/checks/unknownMagic.js +41 -0
- package/lib/checks/utils.js +61 -0
- package/lib/checks/validator.js +60 -7
- package/lib/compiler/assert-consistency.js +23 -7
- package/lib/compiler/base.js +65 -0
- package/lib/compiler/builtins.js +30 -1
- package/lib/compiler/checks.js +8 -5
- package/lib/compiler/definer.js +157 -133
- package/lib/compiler/index.js +89 -31
- package/lib/compiler/propagator.js +5 -2
- package/lib/compiler/resolver.js +375 -185
- package/lib/compiler/shared.js +49 -202
- package/lib/compiler/utils.js +173 -0
- package/lib/edm/annotations/genericTranslation.js +183 -187
- package/lib/edm/csn2edm.js +104 -108
- package/lib/edm/edm.js +18 -21
- package/lib/edm/edmPreprocessor.js +388 -146
- package/lib/edm/edmUtils.js +104 -34
- package/lib/gen/Dictionary.json +22 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +28 -1
- package/lib/gen/language.tokens +79 -69
- package/lib/gen/languageLexer.interp +28 -1
- package/lib/gen/languageLexer.js +879 -805
- package/lib/gen/languageLexer.tokens +71 -62
- package/lib/gen/languageParser.js +5330 -4300
- package/lib/json/from-csn.js +110 -52
- package/lib/json/to-csn.js +434 -120
- package/lib/language/antlrParser.js +15 -3
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +93 -26
- package/lib/language/language.g4 +172 -31
- package/lib/main.d.ts +216 -19
- package/lib/main.js +32 -7
- package/lib/model/api.js +78 -0
- package/lib/model/csnRefs.js +413 -149
- package/lib/model/csnUtils.js +286 -75
- package/lib/model/enrichCsn.js +50 -6
- package/lib/model/revealInternalProperties.js +22 -5
- package/lib/modelCompare/compare.js +39 -21
- package/lib/optionProcessor.js +35 -18
- package/lib/render/.eslintrc.json +4 -1
- package/lib/render/DuplicateChecker.js +9 -6
- package/lib/render/toCdl.js +121 -36
- package/lib/render/toHdbcds.js +148 -98
- package/lib/render/toSql.js +114 -43
- package/lib/render/utils/common.js +8 -13
- package/lib/render/utils/sql.js +3 -3
- package/lib/sql-identifier.js +6 -1
- package/lib/transform/db/assertUnique.js +5 -6
- package/lib/transform/db/constraints.js +281 -106
- package/lib/transform/db/draft.js +11 -8
- package/lib/transform/db/expansion.js +584 -0
- package/lib/transform/db/flattening.js +341 -0
- package/lib/transform/db/groupByOrderBy.js +2 -2
- package/lib/transform/db/transformExists.js +345 -65
- package/lib/transform/db/views.js +438 -0
- package/lib/transform/forHanaNew.js +131 -793
- package/lib/transform/forOdataNew.js +30 -24
- package/lib/transform/localized.js +39 -10
- package/lib/transform/odata/attachPath.js +19 -4
- package/lib/transform/odata/generateForeignKeyElements.js +11 -10
- package/lib/transform/odata/referenceFlattener.js +60 -39
- package/lib/transform/odata/sortByAssociationDependency.js +2 -2
- package/lib/transform/odata/structuralPath.js +72 -0
- package/lib/transform/odata/structureFlattener.js +19 -18
- package/lib/transform/odata/typesExposure.js +22 -12
- package/lib/transform/transformUtilsNew.js +144 -78
- package/lib/transform/translateAssocsToJoins.js +22 -27
- package/lib/transform/universalCsnEnricher.js +67 -0
- package/lib/utils/file.js +5 -14
- package/lib/utils/moduleResolve.js +6 -8
- package/lib/utils/term.js +65 -42
- package/lib/utils/timetrace.js +48 -26
- package/package.json +1 -1
- package/lib/json/walker.js +0 -26
- package/lib/transform/sqlite +0 -0
- package/lib/utils/string.js +0 -17
|
@@ -3,7 +3,6 @@ const { isEdmPropertyRendered, isBuiltinType } = require('../../model/csnUtils')
|
|
|
3
3
|
const edmUtils = require('../edmUtils.js');
|
|
4
4
|
const preprocessAnnotations = require('./preprocessAnnotations.js');
|
|
5
5
|
const oDataDictionary = require('../../gen/Dictionary.json');
|
|
6
|
-
const { makeMessageFunction } = require('../../base/messages');
|
|
7
6
|
const { forEachDefinition } = require('../../model/csnUtils');
|
|
8
7
|
|
|
9
8
|
|
|
@@ -131,16 +130,19 @@ const knownVocabularies = Object.keys(vocabularyDefinitions);
|
|
|
131
130
|
* v - array with two boolean entries, first is for v2, second is for v4
|
|
132
131
|
* dictReplacement: for test purposes, replaces the standard oDataDictionary
|
|
133
132
|
*/
|
|
134
|
-
function csn2annotationEdm(csn,
|
|
133
|
+
function csn2annotationEdm(csn, serviceName, Edm = undefined, options=undefined, messageFunctions=undefined) {
|
|
135
134
|
|
|
135
|
+
if(!Edm)
|
|
136
|
+
throw new Error('Please debug me: csn2annotationsEdm must be invoked with Edm');
|
|
136
137
|
if(!options)
|
|
137
138
|
throw new Error('Please debug me: csn2annotationsEdm must be invoked with options');
|
|
139
|
+
if(!messageFunctions)
|
|
140
|
+
throw new Error('Please debug me: csn2annotationsEdm must be invoked with messageFunctions');
|
|
141
|
+
// global variable where we store all the generated annotations
|
|
142
|
+
const g_annosArray = [];
|
|
138
143
|
|
|
139
|
-
const messageFunctions = makeMessageFunction(csn, options);
|
|
140
144
|
const { info, warning, error } = messageFunctions;
|
|
141
145
|
|
|
142
|
-
const Edm = require('../edm.js')(options, error);
|
|
143
|
-
|
|
144
146
|
// Static dynamic expression dictionary, loaded with Edm creators
|
|
145
147
|
const [ dynamicExpressions, dynamicExpressionNames ] = initEdmJson();
|
|
146
148
|
|
|
@@ -197,10 +199,7 @@ function csn2annotationEdm(csn, edm, serviceName, options=undefined) {
|
|
|
197
199
|
}
|
|
198
200
|
}();
|
|
199
201
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
// global variable where we store all the generated annotations
|
|
203
|
-
let g_annosArray = [];
|
|
202
|
+
const v = options.v;
|
|
204
203
|
|
|
205
204
|
// Crawl over the csn and trigger the annotation translation for all kinds
|
|
206
205
|
// of annotated things.
|
|
@@ -224,40 +223,10 @@ function csn2annotationEdm(csn, edm, serviceName, options=undefined) {
|
|
|
224
223
|
});
|
|
225
224
|
|
|
226
225
|
// filter out empty <Annotations...> elements
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// distribute edm:Annotations into the schemas
|
|
232
|
-
// Create list of full qualified schema names (ServiceName[.subschema])
|
|
233
|
-
// Sort in reverse order for longest match
|
|
234
|
-
const fqSchemaNames = Object.keys(edm._service._schemas).sort((a,b) => b.length-a.length);
|
|
235
|
-
// Distribute each anno into Schema
|
|
236
|
-
g_annosArray.forEach(annos => {
|
|
237
|
-
const carrierName = annos.Target;
|
|
238
|
-
let targetSchema = fqSchemaNames.reduce((rc, sn) => !rc && carrierName && carrierName.startsWith(sn + '.') ? sn : rc, undefined);
|
|
239
|
-
// if no target schema has been found, it's a service annotation that applies to the service schema
|
|
240
|
-
if(targetSchema === undefined)
|
|
241
|
-
targetSchema = serviceName;
|
|
242
|
-
if(targetSchema) {
|
|
243
|
-
if(targetSchema !== serviceName) {
|
|
244
|
-
annos.Target = annos.Target.replace(serviceName + '.', '');
|
|
245
|
-
}
|
|
246
|
-
edm._service._schemas[targetSchema]._annotations.push(annos);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// add references for the used vocabularies
|
|
251
|
-
knownVocabularies.forEach(n => {
|
|
252
|
-
if(vocabularyDefinitions[n].used) {
|
|
253
|
-
let r = new Edm.Reference(v, vocabularyDefinitions[n].ref);
|
|
254
|
-
r.append(new Edm.Include(v, vocabularyDefinitions[n].inc))
|
|
255
|
-
edm._defaultRefs.push(r);
|
|
256
|
-
}
|
|
257
|
-
})
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return g_annosArray;
|
|
226
|
+
// add references for the used vocabularies
|
|
227
|
+
return {
|
|
228
|
+
annos: g_annosArray, usedVocabularies: Object.values(vocabularyDefinitions).filter(v => v.used)
|
|
229
|
+
};
|
|
261
230
|
|
|
262
231
|
//-------------------------------------------------------------------------------------------------
|
|
263
232
|
//-------------------------------------------------------------------------------------------------
|
|
@@ -441,10 +410,11 @@ function csn2annotationEdm(csn, edm, serviceName, options=undefined) {
|
|
|
441
410
|
|
|
442
411
|
// if the carier is an element that is not rendered or
|
|
443
412
|
// if the carrier is a derived type of a primitive type which is not rendered in V2
|
|
413
|
+
// if the carrier is a media stream element in V2
|
|
444
414
|
// do nothing
|
|
445
415
|
|
|
446
416
|
if(!isEdmPropertyRendered(carrier, options) ||
|
|
447
|
-
(edmUtils.isDerivedType(carrier)
|
|
417
|
+
(isV2() && (edmUtils.isDerivedType(carrier) || carrier['@Core.MediaType']))) {
|
|
448
418
|
return;
|
|
449
419
|
}
|
|
450
420
|
|
|
@@ -455,72 +425,95 @@ function csn2annotationEdm(csn, edm, serviceName, options=undefined) {
|
|
|
455
425
|
const knownAnnos = annoNames.filter(filterKnownVocabularies).filter(x => carrier[x] !== null || nullWhitelist.includes(x));
|
|
456
426
|
if (knownAnnos.length === 0) return;
|
|
457
427
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
//
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
//
|
|
464
|
-
//
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
//
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
// prepend annotation prefix (path) to tail steps
|
|
488
|
-
tailSteps.splice(0, 0, '@' + innerAnnoSteps.join('.'));
|
|
489
|
-
steps.push(...tailSteps);
|
|
490
|
-
}
|
|
491
|
-
mergePathStepsIntoPrefixTree(prefixTree, steps, 0, carrier);
|
|
428
|
+
const prefixTree = createPrefixTree();
|
|
429
|
+
|
|
430
|
+
// usually, for a given carrier there is one target
|
|
431
|
+
// for some carriers (service, entity), there can be an alternative target (usually the EntitySet)
|
|
432
|
+
// alternativeEdmTargetName: name of alternative target
|
|
433
|
+
// which one to choose depends on the "AppliesTo" info of the single annotations, so we have
|
|
434
|
+
// to defer this decision; this is why we here construct a function that can make the decision
|
|
435
|
+
// later when looking at single annotations
|
|
436
|
+
|
|
437
|
+
const [
|
|
438
|
+
stdEdmTargetName, // either the schema path or the EntityContainer itself
|
|
439
|
+
hasAlternativeCarrier, // is the alternative annotation target available in the EDM?
|
|
440
|
+
alternativeEdmTargetName, // EntitySet path name
|
|
441
|
+
testToStandardEdmTarget, // if true, assign to standard Edm Target
|
|
442
|
+
testToAlternativeEdmTarget, // if true, assign to alternative Edm Target
|
|
443
|
+
] = initCarrierControlVars();
|
|
444
|
+
|
|
445
|
+
// collect produced Edm.Annotation nodes for various carriers
|
|
446
|
+
const serviceAnnotations = [];
|
|
447
|
+
const stdAnnotations = [];
|
|
448
|
+
const alternativeAnnotations = [];
|
|
449
|
+
|
|
450
|
+
// now create annotation objects for all the annotations of carrier
|
|
451
|
+
handleAnno2(addAnnotation, edmTargetName /*used for messages*/, prefixTree);
|
|
452
|
+
|
|
453
|
+
// Produce Edm.Annotations and attach collected Edm.Annotation(s) to the
|
|
454
|
+
// envelope (or directly to the Schema)
|
|
455
|
+
if(serviceAnnotations.length) {
|
|
456
|
+
g_annosArray.push(...serviceAnnotations.filter(a=>a));
|
|
492
457
|
}
|
|
458
|
+
if(stdAnnotations.length) {
|
|
459
|
+
const annotations = new Edm.Annotations(v, stdEdmTargetName); // used in closure
|
|
460
|
+
annotations.append(...stdAnnotations);
|
|
461
|
+
g_annosArray.push(annotations);
|
|
462
|
+
}
|
|
463
|
+
if(alternativeAnnotations.length) {
|
|
464
|
+
const annotations = new Edm.Annotations(v, alternativeEdmTargetName);
|
|
465
|
+
annotations.append(...alternativeAnnotations);
|
|
466
|
+
g_annosArray.push(annotations);
|
|
467
|
+
}
|
|
468
|
+
|
|
493
469
|
// construct a function that is used to add an <Annotation ...> to the
|
|
494
|
-
// respective
|
|
470
|
+
// respective collector array
|
|
495
471
|
// this function is specific to the actual carrier, following the mapping rules given above
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
472
|
+
function addAnnotation(annotation, appliesTo) {
|
|
473
|
+
let rc=false;
|
|
474
|
+
if (testToAlternativeEdmTarget && appliesTo && testToAlternativeEdmTarget(appliesTo)) {
|
|
475
|
+
if (carrier.kind === 'service') {
|
|
476
|
+
if (isV2()) {
|
|
477
|
+
// there is no enclosing <Annotations ...>, so for v2 the namespace needs to be mentioned here
|
|
478
|
+
annotation.setXml( { xmlns: 'http://docs.oasis-open.org/odata/ns/edm' } );
|
|
479
|
+
}
|
|
480
|
+
serviceAnnotations.push(annotation); // for target Schema: no <Annotations> element
|
|
481
|
+
}
|
|
482
|
+
else if(hasAlternativeCarrier) {
|
|
483
|
+
alternativeAnnotations.push(annotation);
|
|
484
|
+
}
|
|
485
|
+
rc=true;
|
|
486
|
+
}
|
|
487
|
+
if(testToStandardEdmTarget(appliesTo)) {
|
|
488
|
+
stdAnnotations.push(annotation);
|
|
489
|
+
rc=true;
|
|
490
|
+
}
|
|
491
|
+
// Another crazy hack due to this crazy function:
|
|
492
|
+
// If carrier is a managed association (has keys) and rc is false (annotation was not applicable)
|
|
493
|
+
// return true to NOT trigger 'unapplicable' info message
|
|
494
|
+
if(rc === false && carrier.target && carrier.keys && appliesTo.includes('Property'))
|
|
495
|
+
rc = true;
|
|
496
|
+
return rc;
|
|
497
|
+
}
|
|
506
498
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
let testToStandardEdmTarget = (
|
|
510
|
-
let
|
|
499
|
+
function initCarrierControlVars() {
|
|
500
|
+
// eslint-disable-next-line no-unused-vars
|
|
501
|
+
let testToStandardEdmTarget = () => true; // if true, assign to standard Edm Target
|
|
502
|
+
let stdEdmTargetName = edmTargetName;
|
|
511
503
|
let alternativeEdmTargetName = null;
|
|
512
504
|
let hasAlternativeCarrier = false; // is the alternative annotation target available in the EDM?
|
|
505
|
+
let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
|
|
513
506
|
|
|
514
507
|
if (carrier.kind === 'entity' || carrier.kind === 'view') {
|
|
515
|
-
|
|
508
|
+
// If AppliesTo=[EntitySet/Singleton, EntityType], EntitySet/Singleton has precedence
|
|
516
509
|
testToAlternativeEdmTarget = (x => x.includes('EntitySet') || x.includes('Singleton'));
|
|
517
510
|
testToStandardEdmTarget = (x => x ? x.includes('EntityType') : true);
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
511
|
+
// if carrier has an alternate 'entitySetName' use this instead of EdmTargetName
|
|
512
|
+
// (see edmPreprocessor.initializeParameterizedEntityOrView(), where parameterized artifacts
|
|
513
|
+
// are split into *Parameter and *Type entities and their respective EntitySets are eventually
|
|
514
|
+
// renamed.
|
|
515
|
+
// (which is the definition key in the CSN and usually the name of the EntityType)
|
|
516
|
+
// Replace up to last dot with <serviceName>.EntityContainer/
|
|
524
517
|
alternativeEdmTargetName = carrier.$entitySetName || edmTargetName
|
|
525
518
|
const lastDotIndex = alternativeEdmTargetName.lastIndexOf('.');
|
|
526
519
|
if (lastDotIndex > -1)
|
|
@@ -528,32 +521,37 @@ function csn2annotationEdm(csn, edm, serviceName, options=undefined) {
|
|
|
528
521
|
hasAlternativeCarrier = carrier.$hasEntitySet;
|
|
529
522
|
}
|
|
530
523
|
else if (carrier.kind === 'service') {
|
|
531
|
-
|
|
532
|
-
|
|
524
|
+
// if annotated object is a service, annotation goes to EntityContainer,
|
|
525
|
+
// except if AppliesTo contains Schema but not EntityContainer, then annotation goes to Schema
|
|
533
526
|
testToAlternativeEdmTarget = (x => x.includes('Schema') && !x.includes('EntityContainer'));
|
|
534
527
|
testToStandardEdmTarget = ( x => x ? (
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
528
|
+
// either only AppliesTo=[EntityContainer]
|
|
529
|
+
(!x.includes('Schema') && x.includes('EntityContainer')) ||
|
|
530
|
+
// or AppliesTo=[Schema, EntityContainer]
|
|
531
|
+
(x.includes('Schema') && x.includes('EntityContainer')))
|
|
532
|
+
: true );
|
|
533
|
+
stdEdmTargetName = edmTargetName + '.EntityContainer';
|
|
541
534
|
alternativeEdmTargetName = edmTargetName;
|
|
542
535
|
hasAlternativeCarrier = true; // EntityContainer is always available
|
|
543
536
|
}
|
|
544
|
-
|
|
537
|
+
//element => decide if navprop or normal property
|
|
545
538
|
else if(!carrier.kind) {
|
|
546
|
-
|
|
539
|
+
// if appliesTo is undefined, return true
|
|
547
540
|
if(carrier.target) {
|
|
548
541
|
testToStandardEdmTarget = (x=> x ? x.includes('NavigationProperty') : true);
|
|
549
542
|
}
|
|
550
543
|
else {
|
|
551
|
-
|
|
544
|
+
// this might be more precise if handleAnnotation would know more about the carrier
|
|
552
545
|
testToStandardEdmTarget = (x => x ? ['Parameter', 'Property'].some(y => x.includes(y)): true);
|
|
553
546
|
}
|
|
554
547
|
}
|
|
555
|
-
|
|
556
|
-
|
|
548
|
+
return [
|
|
549
|
+
stdEdmTargetName,
|
|
550
|
+
hasAlternativeCarrier,
|
|
551
|
+
alternativeEdmTargetName,
|
|
552
|
+
testToStandardEdmTarget,
|
|
553
|
+
testToAlternativeEdmTarget
|
|
554
|
+
];
|
|
557
555
|
/* all AppliesTo entries:
|
|
558
556
|
"Action",
|
|
559
557
|
"ActionImport",
|
|
@@ -578,88 +576,86 @@ function csn2annotationEdm(csn, edm, serviceName, options=undefined) {
|
|
|
578
576
|
"Term",
|
|
579
577
|
"TypeDefinition"
|
|
580
578
|
*/
|
|
579
|
+
}
|
|
581
580
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
581
|
+
function createPrefixTree() {
|
|
582
|
+
// in csn, all annotations are flattened
|
|
583
|
+
// => values can be - primitive values (string, number)
|
|
584
|
+
// - pseudo-records with "#" or "="
|
|
585
|
+
// - arrays
|
|
586
|
+
// in OData, there are "structured" annotations -> we first need to regroup the cds annotations
|
|
587
|
+
// by building a "prefix tree" for the annotations attached to the carrier
|
|
588
|
+
// see example at definition of function mergePathStepsIntoPrefixTree
|
|
589
|
+
const prefixTree = {};
|
|
590
|
+
for (let a of knownAnnos) {
|
|
591
|
+
// remove leading @ and split at "."
|
|
592
|
+
// stop splitting at ".@" (used for nested annotations)
|
|
593
|
+
// Inline JSON EDM allows to add annotations to record members
|
|
594
|
+
// by prefixing the annotation with the record member 'foo@Common.Label'
|
|
595
|
+
// The splitter should leave such annotations alone, handleEdmJson
|
|
596
|
+
// takes care of assigning these annotations to the record members
|
|
597
|
+
const [ prefix, innerAnnotation ] = a.split('.@');
|
|
598
|
+
const steps = prefix.slice(1).split('.');
|
|
599
|
+
let i = steps.lastIndexOf('$edmJson');
|
|
600
|
+
if(i > -1) {
|
|
601
|
+
i = steps.findIndex(s => s.includes('@'), i+1);
|
|
602
|
+
if(i > -1) {
|
|
603
|
+
steps.splice(i, steps.length-i, steps.slice(i).join('.'));
|
|
596
604
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
605
|
+
}
|
|
606
|
+
if (innerAnnotation) {
|
|
607
|
+
// A voc annotation has two steps (Namespace+Name),
|
|
608
|
+
// any furter steps need to be rendered separately
|
|
609
|
+
const innerAnnoSteps = innerAnnotation.split('.');
|
|
610
|
+
const tailSteps = innerAnnoSteps.splice(2, innerAnnoSteps.length-2);
|
|
611
|
+
// prepend annotation prefix (path) to tail steps
|
|
612
|
+
tailSteps.splice(0, 0, '@' + innerAnnoSteps.join('.'));
|
|
613
|
+
steps.push(...tailSteps);
|
|
614
|
+
}
|
|
615
|
+
mergePathStepsIntoPrefixTree(prefixTree, steps, 0, carrier);
|
|
616
|
+
}
|
|
617
|
+
return prefixTree;
|
|
618
|
+
|
|
619
|
+
// tree: object where to put the next level of names
|
|
620
|
+
// path: the parts of the annotation name
|
|
621
|
+
// index: index into that array pointing to the next name to be processed
|
|
622
|
+
// 0 : vocabulary
|
|
623
|
+
// 1 : term
|
|
624
|
+
// 2+ : record properties
|
|
625
|
+
//
|
|
626
|
+
// example:
|
|
627
|
+
// @v.t1
|
|
628
|
+
// @v.t2.p1
|
|
629
|
+
// @v.t2.p2
|
|
630
|
+
// @v.t3#x.q1
|
|
631
|
+
// @v.t3#x.q2
|
|
632
|
+
// @v.t3#y.q1
|
|
633
|
+
// @v.t3#y.q2
|
|
634
|
+
//
|
|
635
|
+
// { v : { t1 : ...,
|
|
636
|
+
// t2 : { p1 : ...,
|
|
637
|
+
// p2 : ... },
|
|
638
|
+
// t3#x : { q1 : ...,
|
|
639
|
+
// q2 : ... }
|
|
640
|
+
// t3#y : { q1 : ...,
|
|
641
|
+
// q2 : ... } } }
|
|
642
|
+
function mergePathStepsIntoPrefixTree(tree, pathSteps, index, carrier) {
|
|
643
|
+
// TODO check nesting level > 3
|
|
644
|
+
let name = pathSteps[index];
|
|
645
|
+
if (index+1 < pathSteps.length ) {
|
|
646
|
+
if (!tree[name]) {
|
|
647
|
+
tree[name] = {};
|
|
604
648
|
}
|
|
605
|
-
|
|
649
|
+
mergePathStepsIntoPrefixTree(tree[name], pathSteps, index+1, carrier);
|
|
606
650
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
rc=true;
|
|
651
|
+
else {
|
|
652
|
+
tree[name] = carrier['@' + pathSteps.join('.')];
|
|
610
653
|
}
|
|
611
|
-
// Another crazy hack due to this crazy function:
|
|
612
|
-
// If carrier is a managed association (has keys) and rc is false (annotation was not applicable)
|
|
613
|
-
// return true to NOT trigger 'unapplicable' info message
|
|
614
|
-
if(rc === false && carrier.target && carrier.keys && appliesTo.includes('Property'))
|
|
615
|
-
rc = true;
|
|
616
|
-
return rc;
|
|
617
654
|
}
|
|
618
|
-
}();
|
|
619
|
-
|
|
620
|
-
// now create annotation objects for all the annotations of carrier
|
|
621
|
-
// and put them into the elements property of the result object
|
|
622
|
-
handleAnno2(addAnnotation, edmTargetName /*used for messages*/, prefixTree);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
// tree: object where to put the next level of names
|
|
627
|
-
// path: the parts of the annotation name
|
|
628
|
-
// index: index into that array pointing to the next name to be processed
|
|
629
|
-
// 0 : vocabulary
|
|
630
|
-
// 1 : term
|
|
631
|
-
// 2+ : record properties
|
|
632
|
-
//
|
|
633
|
-
// example:
|
|
634
|
-
// @v.t1
|
|
635
|
-
// @v.t2.p1
|
|
636
|
-
// @v.t2.p2
|
|
637
|
-
// @v.t3#x.q1
|
|
638
|
-
// @v.t3#x.q2
|
|
639
|
-
// @v.t3#y.q1
|
|
640
|
-
// @v.t3#y.q2
|
|
641
|
-
//
|
|
642
|
-
// { v : { t1 : ...,
|
|
643
|
-
// t2 : { p1 : ...,
|
|
644
|
-
// p2 : ... },
|
|
645
|
-
// t3#x : { q1 : ...,
|
|
646
|
-
// q2 : ... }
|
|
647
|
-
// t3#y : { q1 : ...,
|
|
648
|
-
// q2 : ... } } }
|
|
649
|
-
function mergePathStepsIntoPrefixTree(tree, pathSteps, index, carrier) {
|
|
650
|
-
// TODO check nesting level > 3
|
|
651
|
-
let name = pathSteps[index];
|
|
652
|
-
if (index+1 < pathSteps.length ) {
|
|
653
|
-
if (!tree[name]) {
|
|
654
|
-
tree[name] = {};
|
|
655
|
-
}
|
|
656
|
-
mergePathStepsIntoPrefixTree(tree[name], pathSteps, index+1, carrier);
|
|
657
|
-
}
|
|
658
|
-
else {
|
|
659
|
-
tree[name] = carrier['@' + pathSteps.join('.')];
|
|
660
655
|
}
|
|
661
656
|
}
|
|
662
657
|
|
|
658
|
+
|
|
663
659
|
// handle all the annotations for a given carrier
|
|
664
660
|
// addAnnotationFunc: a function that adds the <Annotation ...> tags created here into the
|
|
665
661
|
// correct parent tag (see handleAnnotations())
|
|
@@ -1123,7 +1119,7 @@ function csn2annotationEdm(csn, edm, serviceName, options=undefined) {
|
|
|
1123
1119
|
let dictPropertyTypeName = null;
|
|
1124
1120
|
if (dictProperties) {
|
|
1125
1121
|
dictPropertyTypeName = dictProperties[i];
|
|
1126
|
-
if (!dictPropertyTypeName){
|
|
1122
|
+
if (!dictPropertyTypeName && !getDictType(actualTypeName).OpenType){
|
|
1127
1123
|
message(warning, context, `record type '${ actualTypeName }' doesn't have a property '${ i }'`);
|
|
1128
1124
|
}
|
|
1129
1125
|
}
|