@sap/cds-compiler 2.7.0 → 2.11.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 (87) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/bin/cdsc.js +42 -25
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +10 -0
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +17 -33
  7. package/lib/api/options.js +25 -13
  8. package/lib/api/validate.js +33 -9
  9. package/lib/backends.js +9 -8
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/keywords.js +32 -2
  12. package/lib/base/message-registry.js +26 -2
  13. package/lib/base/messages.js +25 -9
  14. package/lib/base/model.js +5 -3
  15. package/lib/base/optionProcessorHelper.js +56 -22
  16. package/lib/checks/onConditions.js +5 -0
  17. package/lib/checks/selectItems.js +4 -0
  18. package/lib/checks/types.js +26 -2
  19. package/lib/checks/unknownMagic.js +41 -0
  20. package/lib/checks/validator.js +7 -2
  21. package/lib/compiler/assert-consistency.js +18 -5
  22. package/lib/compiler/base.js +65 -0
  23. package/lib/compiler/builtins.js +30 -1
  24. package/lib/compiler/checks.js +5 -2
  25. package/lib/compiler/definer.js +145 -120
  26. package/lib/compiler/index.js +16 -4
  27. package/lib/compiler/propagator.js +5 -2
  28. package/lib/compiler/resolver.js +207 -47
  29. package/lib/compiler/shared.js +47 -200
  30. package/lib/compiler/utils.js +173 -0
  31. package/lib/edm/annotations/genericTranslation.js +183 -187
  32. package/lib/edm/csn2edm.js +94 -98
  33. package/lib/edm/edm.js +16 -20
  34. package/lib/edm/edmPreprocessor.js +302 -115
  35. package/lib/edm/edmUtils.js +31 -12
  36. package/lib/gen/language.checksum +1 -1
  37. package/lib/gen/language.interp +28 -1
  38. package/lib/gen/language.tokens +79 -69
  39. package/lib/gen/languageLexer.interp +28 -1
  40. package/lib/gen/languageLexer.js +879 -805
  41. package/lib/gen/languageLexer.tokens +71 -62
  42. package/lib/gen/languageParser.js +5308 -4308
  43. package/lib/json/from-csn.js +59 -30
  44. package/lib/json/to-csn.js +354 -105
  45. package/lib/language/antlrParser.js +11 -0
  46. package/lib/language/errorStrategy.js +1 -0
  47. package/lib/language/genericAntlrParser.js +81 -14
  48. package/lib/language/language.g4 +163 -31
  49. package/lib/main.d.ts +136 -17
  50. package/lib/main.js +7 -1
  51. package/lib/model/api.js +78 -0
  52. package/lib/model/csnRefs.js +115 -32
  53. package/lib/model/csnUtils.js +71 -33
  54. package/lib/model/enrichCsn.js +36 -9
  55. package/lib/model/revealInternalProperties.js +20 -4
  56. package/lib/modelCompare/compare.js +2 -1
  57. package/lib/optionProcessor.js +33 -16
  58. package/lib/render/.eslintrc.json +3 -1
  59. package/lib/render/DuplicateChecker.js +1 -1
  60. package/lib/render/toCdl.js +60 -17
  61. package/lib/render/toHdbcds.js +122 -74
  62. package/lib/render/toSql.js +57 -32
  63. package/lib/render/utils/common.js +6 -10
  64. package/lib/sql-identifier.js +6 -1
  65. package/lib/transform/db/constraints.js +273 -119
  66. package/lib/transform/db/draft.js +9 -6
  67. package/lib/transform/db/expansion.js +19 -7
  68. package/lib/transform/db/flattening.js +31 -7
  69. package/lib/transform/db/transformExists.js +344 -66
  70. package/lib/transform/db/views.js +438 -0
  71. package/lib/transform/forHanaNew.js +65 -436
  72. package/lib/transform/forOdataNew.js +21 -10
  73. package/lib/transform/localized.js +2 -0
  74. package/lib/transform/odata/attachPath.js +19 -4
  75. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  76. package/lib/transform/odata/referenceFlattener.js +44 -38
  77. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  78. package/lib/transform/odata/structuralPath.js +72 -0
  79. package/lib/transform/odata/structureFlattener.js +13 -10
  80. package/lib/transform/odata/typesExposure.js +22 -12
  81. package/lib/transform/transformUtilsNew.js +55 -9
  82. package/lib/transform/translateAssocsToJoins.js +11 -17
  83. package/lib/transform/universalCsnEnricher.js +67 -0
  84. package/lib/utils/file.js +5 -3
  85. package/lib/utils/term.js +65 -42
  86. package/lib/utils/timetrace.js +48 -26
  87. package/package.json +1 -1
@@ -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, edm, serviceName, options=undefined) {
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
- let v = options.v;
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
- g_annosArray = g_annosArray.filter(x => x._children.length > 0 || x.kind !== 'Annotations');
228
-
229
- // edm is undefined in testODataAnnotations.js
230
- if(edm !== undefined) {
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) && options.isV2())) {
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
- // in csn, all annotations are flattened
459
- // => values can be - primitive values (string, number)
460
- // - pseudo-records with "#" or "="
461
- // - arrays
462
- // in OData, there are "structured" annotations -> we first need to regroup the cds annotations
463
- // by building a "prefix tree" for the annotations attached to the carrier
464
- // see example at definition of function mergePathStepsIntoPrefixTree
465
- const prefixTree = {};
466
- for (let a of knownAnnos) {
467
- // remove leading @ and split at "."
468
- // stop splitting at ".@" (used for nested annotations)
469
- // Inline JSON EDM allows to add annotations to record members
470
- // by prefixing the annotation with the record member 'foo@Common.Label'
471
- // The splitter should leave such annotations alone, handleEdmJson
472
- // takes care of assigning these annotations to the record members
473
- const [ prefix, innerAnnotation ] = a.split('.@');
474
- const steps = prefix.slice(1).split('.');
475
- let i = steps.lastIndexOf('$edmJson');
476
- if(i > -1) {
477
- i = steps.findIndex(s => s.includes('@'), i+1);
478
- if(i > -1) {
479
- steps.splice(i, steps.length-i, steps.slice(i).join('.'));
480
- }
481
- }
482
- if (innerAnnotation) {
483
- // A voc annotation has two steps (Namespace+Name),
484
- // any furter steps need to be rendered separately
485
- const innerAnnoSteps = innerAnnotation.split('.');
486
- const tailSteps = innerAnnoSteps.splice(2, innerAnnoSteps.length-2);
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 <Annotations ...> element
470
+ // respective collector array
495
471
  // this function is specific to the actual carrier, following the mapping rules given above
496
- let addAnnotation = function() {
497
- // usually, for a given carrier there is one target
498
- // stdName: name of this target
499
- // newAnnosStd: the corresponding <Annotations ...> tag
500
- // for some carriers (service, entity), there can be an alternative target (usually the EntitySet)
501
- // alternativeEdmTargetName: name of alternative target
502
- // newAnnosAlt: the corresponding <Annotations ...> tag
503
- // which one to choose depends on the "AppliesTo" info of the single annotations, so we have
504
- // to defer this decision; this is why we here construct a function that can make the decision
505
- // later when looking at single annotations
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
- let testToAlternativeEdmTarget = null; // if true, assign to alternative Edm Target
508
- // eslint-disable-next-line no-unused-vars
509
- let testToStandardEdmTarget = (val) => true; // if true, assign to standard Edm Target
510
- let stdName = edmTargetName;
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
- // If AppliesTo=[EntitySet/Singleton, EntityType], EntitySet/Singleton has precedence
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
- // if carrier has an alternate 'entitySetName' use this instead of EdmTargetName
519
- // (see edmPreprocessor.initializeParameterizedEntityOrView(), where parameterized artifacts
520
- // are split into *Parameter and *Type entities and their respective EntitySets are eventually
521
- // renamed.
522
- // (which is the definition key in the CSN and usually the name of the EntityType)
523
- // Replace up to last dot with <serviceName>.EntityContainer/
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
- // if annotated object is a service, annotation goes to EntityContainer,
532
- // except if AppliesTo contains Schema but not EntityContainer, then annotation goes to Schema
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
- // either only AppliesTo=[EntityContainer]
536
- (!x.includes('Schema') && x.includes('EntityContainer')) ||
537
- // or AppliesTo=[Schema, EntityContainer]
538
- (x.includes('Schema') && x.includes('EntityContainer')))
539
- : true );
540
- stdName = edmTargetName + '.EntityContainer';
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
- //element => decide if navprop or normal property
537
+ //element => decide if navprop or normal property
545
538
  else if(!carrier.kind) {
546
- // if appliesTo is undefined, return true
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
- // this might be more precise if handleAnnotation would know more about the carrier
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
- // result objects that holds all the annotation objects to be created
583
- let newAnnosStd = new Edm.Annotations(v, stdName); // used in closure
584
- g_annosArray.push(newAnnosStd);
585
- let newAnnosAlternative = null; // create only on demand
586
-
587
- return function(annotation, appliesTo) {
588
- let rc=false;
589
- if (testToAlternativeEdmTarget && appliesTo && testToAlternativeEdmTarget(appliesTo)) {
590
- if (carrier.kind === 'service') {
591
- if (isV2()) {
592
- // there is no enclosing <Annotations ...>, so for v2 the namespace needs to be mentioned here
593
- annotation.setXml( { xmlns: 'http://docs.oasis-open.org/odata/ns/edm' } );
594
- }
595
- g_annosArray.push(annotation); // for target Schema: no <Annotations> element
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
- // Add <Annotations> to existing carrier only
598
- else if(hasAlternativeCarrier) {
599
- if (!newAnnosAlternative) { // only create upon insertion of first anno
600
- newAnnosAlternative = new Edm.Annotations(v, alternativeEdmTargetName);
601
- g_annosArray.push(newAnnosAlternative);
602
- }
603
- newAnnosAlternative.append(annotation);
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
- rc=true;
649
+ mergePathStepsIntoPrefixTree(tree[name], pathSteps, index+1, carrier);
606
650
  }
607
- if(testToStandardEdmTarget(appliesTo)) {
608
- newAnnosStd.append(annotation);
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
  }