From 695b71d8439ff4d1b98568a6ce874b8805997bc7 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Wed, 6 Mar 2024 17:58:45 +0100 Subject: [PATCH 01/37] Implemented basic syntax and scoping --- .../RosettaSemanticTokensService.java | 3 ++ rosetta-lang/model/Rosetta.xcore | 22 ++++++++++ .../java/com/regnosys/rosetta/Rosetta.xtext | 21 ++++++++- .../IdentifierRepresentationService.java | 2 +- .../java/expression/ExpressionGenerator.xtend | 4 +- .../scoping/RosettaScopeProvider.xtend | 13 ++++++ .../rosetta/types/CardinalityProvider.xtend | 10 ++++- .../regnosys/rosetta/types/Rosetta.xsemantics | 7 ++- .../rosetta/types/RosettaTypeProvider.xtend | 10 ++++- .../rosetta/utils/ImplicitVariableUtil.java | 30 ++++++++++++- .../RosettaTranslateValidationTest.xtend | 43 +++++++++++++++++++ 11 files changed, 153 insertions(+), 12 deletions(-) create mode 100644 rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java index 70396bd2a..0812c3bdc 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java @@ -20,6 +20,7 @@ import com.regnosys.rosetta.rosetta.RosettaSymbol; import com.regnosys.rosetta.rosetta.RosettaType; import com.regnosys.rosetta.rosetta.RosettaTypeAlias; +import com.regnosys.rosetta.rosetta.TranslationParameter; import com.regnosys.rosetta.rosetta.TypeCall; import com.regnosys.rosetta.rosetta.TypeParameter; import com.regnosys.rosetta.rosetta.expression.ClosureParameter; @@ -243,6 +244,8 @@ private SemanticToken markSymbol(EObject objectToMark, EStructuralFeature featur return markAlias(objectToMark, featureToMark, (ShortcutDeclaration)symbol); } else if (symbol instanceof TypeParameter) { return createSemanticToken(objectToMark, featureToMark, PARAMETER); + } else if (symbol instanceof TranslationParameter) { + return createSemanticToken(objectToMark, featureToMark, PARAMETER); } return null; } diff --git a/rosetta-lang/model/Rosetta.xcore b/rosetta-lang/model/Rosetta.xcore index fca67db2f..4e110d5bd 100644 --- a/rosetta-lang/model/Rosetta.xcore +++ b/rosetta-lang/model/Rosetta.xcore @@ -416,6 +416,28 @@ enum RosettaQualifiableType { * Translate external synonym support *****************************************/ +class RosettaTranslateSource extends RosettaRootElement, RosettaNamed { + refers RosettaTranslateSource[] superSources + contains Translation[] translations +} + +class Translation { + contains TranslationParameter[] leftParameters + contains TranslationParameter[] rightParameters + contains TranslationRule[] rules opposite translation +} + +class TranslationParameter extends RosettaSymbol, RosettaTyped { + +} + +class TranslationRule { + refers Translation translation opposite rules + + contains RosettaExpression left + contains RosettaExpression right +} + abstract class ExternalAnnotationSource extends RosettaRootElement, RosettaNamed { contains RosettaExternalClass[] externalClasses contains RosettaExternalEnum[] externalEnums diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index cc3d1a3a9..664327bf8 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -154,7 +154,7 @@ RosettaRootElement: RosettaBasicType | RosettaRecordType | RosettaLibraryFunction | RosettaSynonymSource | RosettaRule | RosettaMetaType | RosettaExternalSynonymSource | RosettaExternalRuleSource | RosettaReport | RosettaTypeAlias | - RosettaQualifiedType | RosettaCalculationType + RosettaQualifiedType | RosettaCalculationType | RosettaTranslateSource ; @@ -759,6 +759,25 @@ RosettaOnlyExistsElementRoot returns RosettaReference: /***************************************** * Translate external synonym and rule support *****************************************/ +RosettaTranslateSource: + 'translate' 'source' RosettaNamed ('extends' superSources+=[RosettaTranslateSource|QualifiedName] (',' superSources+=[RosettaTranslateSource|QualifiedName])* )? + '{' + (translations+=Translation)* + '}' +; + +Translation: + 'translate' leftParameters+=TranslationParameter (',' leftParameters+=TranslationParameter)* '<=>' rightParameters+=TranslationParameter (',' rightParameters+=TranslationParameter)* ':' + (rules+=TranslationRule)* +; + +TranslationParameter: + RosettaNamed? RosettaTyped +; + +TranslationRule: + '+' left=RosettaCalcExpression '<=>' right=RosettaCalcExpression +; fragment ExternalAnnotationSource: '{' diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/IdentifierRepresentationService.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/IdentifierRepresentationService.java index b11e3bd86..dc8348c53 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/IdentifierRepresentationService.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/IdentifierRepresentationService.java @@ -11,7 +11,7 @@ public class IdentifierRepresentationService { private ImplicitVariableUtil implicitVarUtil; public ImplicitVariableRepresentation getImplicitVarInContext(EObject context) { - EObject definingContainer = implicitVarUtil.findContainerDefiningImplicitVariable(context).orElseThrow(); + EObject definingContainer = implicitVarUtil.findObjectDefiningImplicitVariable(context).orElseThrow(); return new ImplicitVariableRepresentation(definingContainer); } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend index 80e976c5b..26ca35a76 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend @@ -231,8 +231,8 @@ class ExpressionGenerator extends RosettaExpressionSwitch { static Logger LOGGER = LoggerFactory.getLogger(CardinalityProvider) @@ -123,6 +124,9 @@ class CardinalityProvider extends RosettaExpressionSwitch { TypeParameter: { false } + TranslationParameter: { + false + } default: { LOGGER.error("Cardinality not defined for symbol: " + symbol?.eClass?.name) false @@ -148,7 +152,7 @@ class CardinalityProvider extends RosettaExpressionSwitch { } def isImplicitVariableMulti(EObject context, boolean breakOnClosureParameter) { - val definingContainer = context.findContainerDefiningImplicitVariable + val definingContainer = context.findObjectDefiningImplicitVariable definingContainer.map [ if (it instanceof Data) { false @@ -156,6 +160,8 @@ class CardinalityProvider extends RosettaExpressionSwitch { isClosureParameterMulti(it.function) } else if (it instanceof RosettaRule) { false + } else if (it instanceof TranslationParameter) { + false } else { false } @@ -243,7 +249,7 @@ class CardinalityProvider extends RosettaExpressionSwitch { } } else if (op instanceof RosettaImplicitVariable) { - val definingContainer = op.findContainerDefiningImplicitVariable + val definingContainer = op.findObjectDefiningImplicitVariable definingContainer.map [ if (it instanceof ThenOperation) (it as RosettaFunctionalOperation).argument.isOutputListOfLists diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics index 1858515be..0965a8090 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics @@ -67,6 +67,7 @@ import com.regnosys.rosetta.interpreter.RosettaValue import com.rosetta.util.DottedPath import com.regnosys.rosetta.rosetta.RosettaRule import java.math.BigInteger +import com.regnosys.rosetta.rosetta.TranslationParameter inject extension TypeFactory typeFactory inject extension TypeValidationUtil util @@ -160,7 +161,7 @@ auxiliary functionalOperationItemType(RosettaFunctionalOperation op) { } auxiliary typeOfImplicitVariable(EObject c) { - val definingContainer = c.findContainerDefiningImplicitVariable + val definingContainer = c.findObjectDefiningImplicitVariable definingContainer.map [ if (it instanceof Data) { createListType(new RDataType(it), single) @@ -168,7 +169,9 @@ auxiliary typeOfImplicitVariable(EObject c) { functionalOperationItemType } else if (it instanceof RosettaRule) { input?.typeCallToRType(new RosettaInterpreterContext)?.createListType(single) - } + } else if (it instanceof TranslationParameter) { + typeCall.typeCallToRType(new RosettaInterpreterContext)?.createListType(single) + } ] } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend index 4ee762e21..bc9998411 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend @@ -73,6 +73,7 @@ import com.regnosys.rosetta.rosetta.RosettaRule import java.math.BigInteger import org.eclipse.xtext.resource.XtextResource import javax.inject.Provider +import com.regnosys.rosetta.rosetta.TranslationParameter class RosettaTypeProvider extends RosettaExpressionSwitch> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" @@ -145,6 +146,9 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { @@ -202,14 +206,16 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { - val definingContainer = context.findContainerDefiningImplicitVariable + val definingContainer = context.findObjectDefiningImplicitVariable definingContainer.map [ if (it instanceof Data) { new RDataType(it) } else if (it instanceof RosettaFunctionalOperation) { safeRType(argument, cycleTracker) } else if (it instanceof RosettaRule) { - input?.typeCallToRType ?: MISSING + input?.typeCallToRType + } else if (it instanceof TranslationParameter) { + typeCall.typeCallToRType } ].orElse(MISSING) } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java index 8bca5965a..ca36e39ab 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java @@ -1,13 +1,18 @@ package com.regnosys.rosetta.utils; +import java.util.List; import java.util.Optional; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.EcoreUtil2; import com.regnosys.rosetta.rosetta.RosettaRule; +import com.regnosys.rosetta.rosetta.Translation; +import com.regnosys.rosetta.rosetta.TranslationParameter; +import com.regnosys.rosetta.rosetta.TranslationRule; import com.regnosys.rosetta.rosetta.expression.ExpressionFactory; import com.regnosys.rosetta.rosetta.expression.InlineFunction; +import com.regnosys.rosetta.rosetta.expression.RosettaExpression; import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation; import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable; import com.regnosys.rosetta.rosetta.simple.Data; @@ -28,7 +33,7 @@ public RosettaImplicitVariable getDefaultImplicitVariable() { /** * Find the enclosing object that defines the implicit variable in the given expression. */ - public Optional findContainerDefiningImplicitVariable(EObject context) { + public Optional findObjectDefiningImplicitVariable(EObject context) { Iterable containers = EcoreUtil2.getAllContainers(context); EObject prev = context; for (EObject container: containers) { @@ -42,16 +47,37 @@ public Optional findContainerDefiningImplicitVariable(EObject context) } } else if (container instanceof RosettaRule) { return Optional.of(container); + } else if (container instanceof TranslationRule) { + TranslationRule rule = (TranslationRule)container; + Translation trans = rule.getTranslation(); + TranslationParameter implicitParam = null; + + RosettaExpression left = rule.getLeft(); + if (left != null && left.equals(prev)) { + implicitParam = findFirstUnnamedParameter(trans.getLeftParameters()); + } else { + RosettaExpression right = rule.getRight(); + if (right != null && right.equals(prev)) { + implicitParam = findFirstUnnamedParameter(trans.getRightParameters()); + } + } + + if (implicitParam != null) { + return Optional.of(implicitParam); + } } prev = container; } return Optional.empty(); } + private TranslationParameter findFirstUnnamedParameter(List params) { + return params.stream().filter(p -> p.getName() == null).findFirst().orElse(null); + } /** * Indicates whether an implicit variable exists in the given context. */ public boolean implicitVariableExistsInContext(EObject context) { - return findContainerDefiningImplicitVariable(context).isPresent(); + return findObjectDefiningImplicitVariable(context).isPresent(); } } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend new file mode 100644 index 000000000..2ab9bfb83 --- /dev/null +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend @@ -0,0 +1,43 @@ +/* + * generated by Xtext 2.10.0 + */ +package com.regnosys.rosetta.validation + +import com.regnosys.rosetta.tests.util.ModelHelper +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.eclipse.xtext.testing.validation.ValidationTestHelper +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* +import javax.inject.Inject + +@ExtendWith(InjectionExtension) +@InjectWith(MyRosettaInjectorProvider) +class RosettaTranslateValidationTest implements RosettaIssueCodes { + + @Inject extension ValidationTestHelper + @Inject extension ModelHelper + + @Test + def void testTranslate() { + ''' + type Foo: + a string (1..1) + + type Bar: + b string (1..1) + + translate source FooBar1 { + translate Foo <=> Bar: + + a <=> b + } + + translate source FooBar2 { + translate foo Foo <=> bar Bar: + + foo -> a <=> bar -> b + } + '''.parseRosettaWithNoIssues + } +} From 274673c998475b5ac471091c9a3af5c9cad34c7f Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Mon, 11 Mar 2024 11:01:50 +0100 Subject: [PATCH 02/37] fixes --- rosetta-ide/rosetta.tmLanguage.yaml | 47 ++++++++++++++++++- .../ide/textmate/GenerateTmGrammar.java | 2 +- rosetta-lang/model/Rosetta.xcore | 4 +- .../java/com/regnosys/rosetta/Rosetta.xtext | 6 +-- .../rosetta/utils/ImplicitVariableUtil.java | 11 ++--- .../RosettaTranslateValidationTest.xtend | 9 ++-- 6 files changed, 61 insertions(+), 18 deletions(-) diff --git a/rosetta-ide/rosetta.tmLanguage.yaml b/rosetta-ide/rosetta.tmLanguage.yaml index d1e13c19b..e05554566 100644 --- a/rosetta-ide/rosetta.tmLanguage.yaml +++ b/rosetta-ide/rosetta.tmLanguage.yaml @@ -28,7 +28,7 @@ variables: # - keywords that may also be used as another keyword in a certain context (e.g., `synonym`), # - keywords that are unambiguous, i.e., all other keywords indicating the start of a root element. identifiersConflictingWithRootStart: '{{wordStart}}(version){{wordEnd}}' - ambiguousRootStart: '{{wordStart}}(synonym|rule){{wordEnd}}' + ambiguousRootStart: '{{wordStart}}(synonym|rule|translate){{wordEnd}}' unambiguousRootStart: '{{wordStart}}(namespace|import|isEvent|isProduct|body|corpus|segment|basicType|recordType|typeAlias|library|reporting|eligibility|metaType|report|annotation|enum|type|func){{wordEnd}}' rootStart: '{{identifiersConflictingWithRootStart}}|{{ambiguousRootStart}}|{{unambiguousRootStart}}' strictRootEnd: (?={{rootStart}}) @@ -135,6 +135,7 @@ repository: - include: '#rosettaTypeAlias' - include: '#rosettaLibraryFunction' - include: '#rosettaAnnotationSource' + - include: '#rosettaTranslateSource' - include: '#rosettaRule' - include: '#rosettaMetaType' - include: '#rosettaReport' @@ -441,6 +442,50 @@ repository: - include: '#comment' - include: '#annotation' + rosettaTranslateSource: + name: meta.translate-source.rosetta + begin: '{{wordStart}}translate{{wordEnd}}' + beginCaptures: + 0: { name: keyword.other.rosetta } + end: '{{unambiguousRootEnd}}' + patterns: + - include: '#comment' + - begin: '(?<={{wordStart}}translate{{wordEnd}})' + end: (?=\{)|{{rootEnd}} + patterns: + - include: '#comment' + - name: keyword.other.source.rosetta + match: '{{wordStart}}source{{wordEnd}}' + - name: keyword.other.extends.rosetta + match: '{{wordStart}}extends{{wordEnd}}' + - name: entity.name.translate-source.rosetta + match: '{{identifier}}' + - name: meta.translate-source-body.rosetta + begin: (\{) + beginCaptures: + 1: { name: punctuation.section.braces.begin.rosetta } + end: (\})|{{unambiguousRootEnd}} + endCaptures: + 1: { name: punctuation.section.braces.end.rosetta } + patterns: + - include: '#comment' + - name: meta.translation.rosetta + begin: '{{wordStart}}translate{{wordEnd}}' + beginCaptures: + 0: { name: keyword.other.rosetta } + end: (?={{wordStart}}translate{{wordEnd}}|\})|{{rootEnd}} + patterns: + - include: '#comment' + - begin: (?<={{wordStart}}translate{{wordEnd}}) + end: ':' + patterns: + - include: '#comment' + - begin: '#' + end: (?=#|{{wordStart}}translate{{wordEnd}}|\})|{{rootEnd}} + patterns: + - include: '#comment' + - include: '#expression' + rosettaRule: patterns: - name: meta.rule.rosetta diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/textmate/GenerateTmGrammar.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/textmate/GenerateTmGrammar.java index 8b7481b91..b600be02c 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/textmate/GenerateTmGrammar.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/textmate/GenerateTmGrammar.java @@ -33,7 +33,7 @@ import com.regnosys.rosetta.services.RosettaGrammarAccess; public class GenerateTmGrammar { - private static List ignoredRosettaKeywords = List.of("..", "condition", /* @Compat */"qualifiedType", "calculationType"); + private static List ignoredRosettaKeywords = List.of("..", "condition", /* @Compat */"qualifiedType", "calculationType", "<=>"); /** * param 0: path to input yaml file diff --git a/rosetta-lang/model/Rosetta.xcore b/rosetta-lang/model/Rosetta.xcore index 4e110d5bd..edd65e0d6 100644 --- a/rosetta-lang/model/Rosetta.xcore +++ b/rosetta-lang/model/Rosetta.xcore @@ -434,8 +434,8 @@ class TranslationParameter extends RosettaSymbol, RosettaTyped { class TranslationRule { refers Translation translation opposite rules - contains RosettaExpression left - contains RosettaExpression right + contains RosettaExpression[] left + contains RosettaExpression[] right } abstract class ExternalAnnotationSource extends RosettaRootElement, RosettaNamed { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index 664327bf8..718e97280 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -196,8 +196,8 @@ TypeParameterValidID: ; ValidID: - ID | 'condition' | 'source' | 'value' | 'version' // used in CDM model - | 'pattern' + ID | 'condition' | 'source' | 'value' | 'version' + | 'pattern' | 'translate' ; @@ -776,7 +776,7 @@ TranslationParameter: ; TranslationRule: - '+' left=RosettaCalcExpression '<=>' right=RosettaCalcExpression + '#' left+=RosettaCalcExpression (',' left+=RosettaCalcExpression)* '<=>' right+=RosettaCalcExpression (',' right+=RosettaCalcExpression)* ; fragment ExternalAnnotationSource: diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java index ca36e39ab..52c132d1c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java @@ -6,6 +6,7 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.EcoreUtil2; +import com.regnosys.rosetta.rosetta.RosettaPackage.Literals; import com.regnosys.rosetta.rosetta.RosettaRule; import com.regnosys.rosetta.rosetta.Translation; import com.regnosys.rosetta.rosetta.TranslationParameter; @@ -52,14 +53,10 @@ public Optional findObjectDefiningImplicitVariable(EObject context) { Translation trans = rule.getTranslation(); TranslationParameter implicitParam = null; - RosettaExpression left = rule.getLeft(); - if (left != null && left.equals(prev)) { + if (prev.eContainmentFeature() == Literals.TRANSLATION_RULE__LEFT) { implicitParam = findFirstUnnamedParameter(trans.getLeftParameters()); - } else { - RosettaExpression right = rule.getRight(); - if (right != null && right.equals(prev)) { - implicitParam = findFirstUnnamedParameter(trans.getRightParameters()); - } + } else if (prev.eContainmentFeature() == Literals.TRANSLATION_RULE__RIGHT) { + implicitParam = findFirstUnnamedParameter(trans.getRightParameters()); } if (implicitParam != null) { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend index 2ab9bfb83..60dc138c2 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend @@ -31,12 +31,13 @@ class RosettaTranslateValidationTest implements RosettaIssueCodes { translate source FooBar1 { translate Foo <=> Bar: - + a <=> b + # a <=> b + # 42 <=> 42 } - translate source FooBar2 { - translate foo Foo <=> bar Bar: - + foo -> a <=> bar -> b + translate source FooBar2 extends FooBar1 { + translate string, foo Foo <=> bar Bar: + # foo -> a, foo <=> bar -> b } '''.parseRosettaWithNoIssues } From deab2c5dbc65d897ec6a68386cfa507fd054fe6a Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Thu, 18 Apr 2024 09:57:50 +0200 Subject: [PATCH 03/37] fixed highlighting --- rosetta-ide/rosetta.tmLanguage.yaml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/rosetta-ide/rosetta.tmLanguage.yaml b/rosetta-ide/rosetta.tmLanguage.yaml index e05554566..a601dc3f1 100644 --- a/rosetta-ide/rosetta.tmLanguage.yaml +++ b/rosetta-ide/rosetta.tmLanguage.yaml @@ -27,12 +27,13 @@ variables: # - keywords that may also be used as an identifier (e.g., `version`), # - keywords that may also be used as another keyword in a certain context (e.g., `synonym`), # - keywords that are unambiguous, i.e., all other keywords indicating the start of a root element. - identifiersConflictingWithRootStart: '{{wordStart}}(version){{wordEnd}}' - ambiguousRootStart: '{{wordStart}}(synonym|rule|translate){{wordEnd}}' + identifiersConflictingWithRootStart: '{{wordStart}}(translate){{wordEnd}}' + identifiersConflictingWithNamespace: '{{wordStart}}(version){{wordEnd}}' + ambiguousRootStart: '{{wordStart}}(synonym|rule){{wordEnd}}' unambiguousRootStart: '{{wordStart}}(namespace|import|isEvent|isProduct|body|corpus|segment|basicType|recordType|typeAlias|library|reporting|eligibility|metaType|report|annotation|enum|type|func){{wordEnd}}' - rootStart: '{{identifiersConflictingWithRootStart}}|{{ambiguousRootStart}}|{{unambiguousRootStart}}' - strictRootEnd: (?={{rootStart}}) - rootEnd: (?={{ambiguousRootStart}}|{{unambiguousRootStart}}) + rootStart: '{{identifiersConflictingWithRootStart}}|{{identifiersConflictingWithNamespace}}|{{ambiguousRootStart}}|{{unambiguousRootStart}}' + namespaceEnd: (?={{identifiersConflictingWithNamespace}}|{{ambiguousRootStart}}|{{unambiguousRootStart}}) + rootEnd: (?={{identifiersConflictingWithRootStart}}|{{ambiguousRootStart}}|{{unambiguousRootStart}}) unambiguousRootEnd: (?={{unambiguousRootStart}}) sectionStart: '{{wordStart}}((\Qpost-\E)?condition|set|add|inputs|output|alias){{wordEnd}}' @@ -45,7 +46,7 @@ variables: synonymAnnotationSectionEnd: (?={{synonymAnnotationSection}})|(?=\])|{{rootEnd}} docReferenceAnnotationSection: '{{wordStart}}(rationale|rationale_author|structured_provision|provision|reportedField){{wordEnd}}' docReferenceAnnotationSectionEnd: (?={{docReferenceAnnotationSection}}|\])|{{sectionEnd}} - expressionEnd: '(?=,|\))|{{sectionEnd}}' + expressionEnd: '(?=,|\)|\})|{{sectionEnd}}' functionalOperationEnd: (?=\])|(?={{listOperation}}|{{wordStart}}(else|then){{wordEnd}})|{{expressionEnd}} patterns: @@ -67,7 +68,7 @@ repository: begin: '{{wordStart}}namespace{{wordEnd}}' beginCaptures: 0: { name: keyword.other.namespace.rosetta } - end: '{{strictRootEnd}}' + end: '{{namespaceEnd}}' patterns: - include: '#comment' - include: '#documentation' @@ -447,7 +448,9 @@ repository: begin: '{{wordStart}}translate{{wordEnd}}' beginCaptures: 0: { name: keyword.other.rosetta } - end: '{{unambiguousRootEnd}}' + end: (\})|{{rootEnd}} + endCaptures: + 1: { name: punctuation.section.braces.end.rosetta } patterns: - include: '#comment' - begin: '(?<={{wordStart}}translate{{wordEnd}})' @@ -464,9 +467,7 @@ repository: begin: (\{) beginCaptures: 1: { name: punctuation.section.braces.begin.rosetta } - end: (\})|{{unambiguousRootEnd}} - endCaptures: - 1: { name: punctuation.section.braces.end.rosetta } + end: (?=\})|{{unambiguousRootEnd}} patterns: - include: '#comment' - name: meta.translation.rosetta From dc5eeecf004ef8d7aa6800ab57214393fc1767da Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 21 May 2024 11:31:05 +0200 Subject: [PATCH 04/37] Implemented syntax and scoping --- .../RosettaSemanticTokensService.java | 2 +- rosetta-lang/model/Rosetta.xcore | 32 +---------- rosetta-lang/model/RosettaTranslate.xcore | 34 +++++++++++- .../java/com/regnosys/rosetta/Rosetta.xtext | 55 ++++++++++++------- .../rosetta/RosettaStandaloneSetup.xtend | 1 + .../scoping/RosettaScopeProvider.xtend | 28 +++++----- .../rosetta/types/CardinalityProvider.xtend | 2 +- .../regnosys/rosetta/types/Rosetta.xsemantics | 2 +- .../rosetta/types/RosettaTypeProvider.xtend | 2 +- .../rosetta/utils/ImplicitVariableUtil.java | 26 +++------ .../RosettaTranslateValidationTest.xtend | 11 ++-- 11 files changed, 103 insertions(+), 92 deletions(-) diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java index d3216d9a1..6d16ae1fc 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/semantictokens/RosettaSemanticTokensService.java @@ -36,7 +36,7 @@ import com.regnosys.rosetta.rosetta.RosettaSymbol; import com.regnosys.rosetta.rosetta.RosettaType; import com.regnosys.rosetta.rosetta.RosettaTypeAlias; -import com.regnosys.rosetta.rosetta.TranslationParameter; +import com.regnosys.rosetta.rosetta.translate.TranslationParameter; import com.regnosys.rosetta.rosetta.TypeCall; import com.regnosys.rosetta.rosetta.TypeParameter; import com.regnosys.rosetta.rosetta.expression.ClosureParameter; diff --git a/rosetta-lang/model/Rosetta.xcore b/rosetta-lang/model/Rosetta.xcore index edd65e0d6..9b22558ac 100644 --- a/rosetta-lang/model/Rosetta.xcore +++ b/rosetta-lang/model/Rosetta.xcore @@ -44,16 +44,10 @@ interface RosettaTyped { } } -class RosettaFeature extends RosettaNamed { - /** - * @return The name or 'value' if it's null - */ - derived String getNameOrDefault get { - return name ?: 'value' - } +interface RosettaFeature extends RosettaNamed { } -class RosettaTypedFeature extends RosettaFeature, RosettaTyped {} +interface RosettaTypedFeature extends RosettaFeature, RosettaTyped {} /** * A named symbol that may be used in an expression to reference an object @@ -416,28 +410,6 @@ enum RosettaQualifiableType { * Translate external synonym support *****************************************/ -class RosettaTranslateSource extends RosettaRootElement, RosettaNamed { - refers RosettaTranslateSource[] superSources - contains Translation[] translations -} - -class Translation { - contains TranslationParameter[] leftParameters - contains TranslationParameter[] rightParameters - contains TranslationRule[] rules opposite translation -} - -class TranslationParameter extends RosettaSymbol, RosettaTyped { - -} - -class TranslationRule { - refers Translation translation opposite rules - - contains RosettaExpression[] left - contains RosettaExpression[] right -} - abstract class ExternalAnnotationSource extends RosettaRootElement, RosettaNamed { contains RosettaExternalClass[] externalClasses contains RosettaExternalEnum[] externalEnums diff --git a/rosetta-lang/model/RosettaTranslate.xcore b/rosetta-lang/model/RosettaTranslate.xcore index c920219ff..8a7ced8a8 100644 --- a/rosetta-lang/model/RosettaTranslate.xcore +++ b/rosetta-lang/model/RosettaTranslate.xcore @@ -4,8 +4,14 @@ complianceLevel="8.0", bundleManifest="false", modelPluginID="") package com.regnosys.rosetta.rosetta.translate +import com.regnosys.rosetta.rosetta.RosettaFeature +import com.regnosys.rosetta.rosetta.RosettaMetaType import com.regnosys.rosetta.rosetta.RosettaNamed import com.regnosys.rosetta.rosetta.RosettaRootElement +import com.regnosys.rosetta.rosetta.RosettaSymbol +import com.regnosys.rosetta.rosetta.RosettaTyped +import com.regnosys.rosetta.rosetta.TypeCall +import com.regnosys.rosetta.rosetta.expression.RosettaExpression class TranslateSource extends RosettaRootElement, RosettaNamed { refers TranslateSource[] superSources @@ -13,5 +19,31 @@ class TranslateSource extends RosettaRootElement, RosettaNamed { } class Translation { + contains TypeCall resultType + contains TranslationParameter[] parameters + contains TranslateInstruction[] typeInstructions + contains TranslateMetaInstruction[] typeMetaInstructions -} \ No newline at end of file + contains TranslationRule[] rules opposite translation +} + +class TranslationParameter extends RosettaSymbol, RosettaTyped { + +} + +class TranslationRule { + refers Translation translation opposite rules + + refers RosettaFeature feature + contains TranslateInstruction[] instructions + contains TranslateMetaInstruction[] metaInstructions +} + +class TranslateInstruction { + contains RosettaExpression expression +} + +class TranslateMetaInstruction { + refers RosettaMetaType metaFeature + contains RosettaExpression expression +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index a9d9689f1..240cfc6bb 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -155,7 +155,7 @@ RosettaRootElement: RosettaBasicType | RosettaRecordType | RosettaLibraryFunction | RosettaSynonymSource | RosettaRule | RosettaMetaType | RosettaExternalSynonymSource | RosettaExternalRuleSource | RosettaReport | RosettaTypeAlias | - RosettaQualifiedType | RosettaCalculationType | RosettaTranslateSource + RosettaQualifiedType | RosettaCalculationType | TranslateSource ; @@ -769,25 +769,6 @@ RosettaOnlyExistsElementRoot returns RosettaReference: /***************************************** * Translate external synonym and rule support *****************************************/ -RosettaTranslateSource: - 'translate' 'source' RosettaNamed ('extends' superSources+=[RosettaTranslateSource|QualifiedName] (',' superSources+=[RosettaTranslateSource|QualifiedName])* )? - '{' - (translations+=Translation)* - '}' -; - -Translation: - 'translate' leftParameters+=TranslationParameter (',' leftParameters+=TranslationParameter)* '<=>' rightParameters+=TranslationParameter (',' rightParameters+=TranslationParameter)* ':' - (rules+=TranslationRule)* -; - -TranslationParameter: - RosettaNamed? RosettaTyped -; - -TranslationRule: - '#' left+=RosettaCalcExpression (',' left+=RosettaCalcExpression)* '<=>' right+=RosettaCalcExpression (',' right+=RosettaCalcExpression)* -; fragment ExternalAnnotationSource: '{' @@ -893,3 +874,37 @@ RosettaRule: ('as' identifier=STRING)?) ) ; + +/*********** + * Translate + ************/ +TranslateSource: + 'translate' 'source' RosettaNamed ('extends' superSources+=[TranslateSource|QualifiedName] (',' superSources+=[TranslateSource|QualifiedName])* )? + '{' + (translations+=Translation)* + '}' +; + +Translation: + resultType=TypeCall ('from' parameters+=TranslationParameter (',' parameters+=TranslationParameter)*)? ':' + (typeInstructions+=TranslateInstruction | typeMetaInstructions+=TranslateMetaInstruction)* + rules+=TranslationRule* +; + +TranslationParameter: + RosettaNamed? RosettaTyped +; + +TranslationRule: + '+' feature=[RosettaFeature] + (instructions+=TranslateInstruction | metaInstructions+=TranslateMetaInstruction)* +; + +TranslateInstruction: + '[' 'from' expression=RosettaCalcExpression ']' +; + +TranslateMetaInstruction: + '[' 'meta' metaFeature=[RosettaMetaType] 'from' expression=RosettaCalcExpression ']' +; + diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaStandaloneSetup.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaStandaloneSetup.xtend index e103d051e..551d04937 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaStandaloneSetup.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaStandaloneSetup.xtend @@ -8,6 +8,7 @@ import com.regnosys.rosetta.rosetta.simple.SimplePackage import org.eclipse.emf.ecore.EPackage import com.regnosys.rosetta.rosetta.expression.ExpressionPackage import com.google.inject.Injector +import com.regnosys.rosetta.rosetta.translate.TranslatePackage /** * Initialization support for running Xtext languages without Equinox extension registry. diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index a6907c99e..e8c159d99 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -37,6 +37,7 @@ import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* +import static com.regnosys.rosetta.rosetta.translate.TranslatePackage.Literals.* import com.regnosys.rosetta.rosetta.expression.InlineFunction import com.regnosys.rosetta.rosetta.RosettaAttributeReference import java.util.List @@ -56,8 +57,9 @@ import com.regnosys.rosetta.rosetta.ParametrizedRosettaType import javax.inject.Inject import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair -import com.regnosys.rosetta.rosetta.TranslationRule -import com.regnosys.rosetta.rosetta.expression.RosettaExpression +import com.regnosys.rosetta.rosetta.translate.Translation +import com.regnosys.rosetta.rosetta.translate.TranslationRule +import com.regnosys.rosetta.types.TypeSystem /** * This class contains custom scoping description. @@ -72,6 +74,7 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { static Logger LOGGER = LoggerFactory.getLogger(RosettaScopeProvider) @Inject RosettaTypeProvider typeProvider + @Inject TypeSystem typeSystem @Inject extension RosettaExtensions @Inject extension RosettaConfigExtension configs @Inject extension RosettaFunctionExtensions @@ -199,7 +202,7 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { return Scopes.scopeFor(classRef.allAttributes) } return IScope.NULLSCOPE - } + } case ROSETTA_EXTERNAL_ENUM_VALUE__ENUM_REF: { if (context instanceof RosettaExternalEnumValue) { val enumRef = (context.eContainer as RosettaExternalEnum).typeRef @@ -224,6 +227,13 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { case ROSETTA_EXTERNAL_RULE_SOURCE__SUPER_SOURCES: { return defaultScope(context, reference).filteredScope[it.EClass == ROSETTA_EXTERNAL_RULE_SOURCE] } + case TRANSLATION_RULE__FEATURE: { + if (context instanceof TranslationRule) { + val translation = context.translation + return createExtendedFeatureScope(translation, typeSystem.typeCallToRType(translation.resultType)) + } + return IScope.NULLSCOPE + } } // LOGGER.warn('''No scope defined for «context.class.simpleName» referencing «reference.name».''') return defaultScope(context, reference) @@ -288,16 +298,8 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { object.isPostCondition || descr.EObjectOrProxy.eContainingFeature !== FUNCTION__OUTPUT ]) } - RosettaExpression: { - if (object.eContainmentFeature === TRANSLATION_RULE__LEFT) { - val rule = object.eContainer as TranslationRule - Scopes.scopeFor(rule.translation.leftParameters.filter[name !== null], parentScope) - } else if (object.eContainmentFeature === TRANSLATION_RULE__RIGHT) { - val rule = object.eContainer as TranslationRule - Scopes.scopeFor(rule.translation.rightParameters.filter[name !== null], parentScope) - } else { - parentScope - } + Translation: { + Scopes.scopeFor(object.parameters.filter[name !== null], parentScope) } RosettaModel: filteredScope(defaultScope(object, reference))[ descr | diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend index fd539edb4..b7c045d65 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend @@ -69,7 +69,7 @@ import com.regnosys.rosetta.rosetta.RosettaRule import com.regnosys.rosetta.rosetta.expression.ToDateOperation import com.regnosys.rosetta.rosetta.expression.ToDateTimeOperation import com.regnosys.rosetta.rosetta.expression.ToZonedDateTimeOperation -import com.regnosys.rosetta.rosetta.TranslationParameter +import com.regnosys.rosetta.rosetta.translate.TranslationParameter class CardinalityProvider extends RosettaExpressionSwitch { static Logger LOGGER = LoggerFactory.getLogger(CardinalityProvider) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics index 0965a8090..e3391e38a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/Rosetta.xsemantics @@ -67,7 +67,7 @@ import com.regnosys.rosetta.interpreter.RosettaValue import com.rosetta.util.DottedPath import com.regnosys.rosetta.rosetta.RosettaRule import java.math.BigInteger -import com.regnosys.rosetta.rosetta.TranslationParameter +import com.regnosys.rosetta.rosetta.translate.TranslationParameter inject extension TypeFactory typeFactory inject extension TypeValidationUtil util diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend index b2f169481..3694b9a6c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend @@ -76,7 +76,7 @@ import javax.inject.Provider import com.regnosys.rosetta.rosetta.expression.ToDateOperation import com.regnosys.rosetta.rosetta.expression.ToDateTimeOperation import com.regnosys.rosetta.rosetta.expression.ToZonedDateTimeOperation -import com.regnosys.rosetta.rosetta.TranslationParameter +import com.regnosys.rosetta.rosetta.translate.TranslationParameter class RosettaTypeProvider extends RosettaExpressionSwitch> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java index 9b58873ba..2e743a1f4 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java @@ -22,14 +22,12 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.EcoreUtil2; -import com.regnosys.rosetta.rosetta.RosettaPackage.Literals; import com.regnosys.rosetta.rosetta.RosettaRule; -import com.regnosys.rosetta.rosetta.Translation; -import com.regnosys.rosetta.rosetta.TranslationParameter; -import com.regnosys.rosetta.rosetta.TranslationRule; +import com.regnosys.rosetta.rosetta.translate.Translation; +import com.regnosys.rosetta.rosetta.translate.TranslationParameter; +import com.regnosys.rosetta.rosetta.translate.TranslationRule; import com.regnosys.rosetta.rosetta.expression.ExpressionFactory; import com.regnosys.rosetta.rosetta.expression.InlineFunction; -import com.regnosys.rosetta.rosetta.expression.RosettaExpression; import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation; import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable; import com.regnosys.rosetta.rosetta.simple.Data; @@ -50,7 +48,7 @@ public RosettaImplicitVariable getDefaultImplicitVariable() { /** * Find the enclosing object that defines the implicit variable in the given expression. */ - public Optional findObjectDefiningImplicitVariable(EObject context) { + public Optional findObjectDefiningImplicitVariable(EObject context) { Iterable containers = EcoreUtil2.getAllContainers(context); EObject prev = context; for (EObject container: containers) { @@ -67,24 +65,14 @@ public Optional findObjectDefiningImplicitVariable(EObject context) { } else if (container instanceof TranslationRule) { TranslationRule rule = (TranslationRule)container; Translation trans = rule.getTranslation(); - TranslationParameter implicitParam = null; - - if (prev.eContainmentFeature() == Literals.TRANSLATION_RULE__LEFT) { - implicitParam = findFirstUnnamedParameter(trans.getLeftParameters()); - } else if (prev.eContainmentFeature() == Literals.TRANSLATION_RULE__RIGHT) { - implicitParam = findFirstUnnamedParameter(trans.getRightParameters()); - } - - if (implicitParam != null) { - return Optional.of(implicitParam); - } + return findFirstUnnamedParameter(trans.getParameters()); } prev = container; } return Optional.empty(); } - private TranslationParameter findFirstUnnamedParameter(List params) { - return params.stream().filter(p -> p.getName() == null).findFirst().orElse(null); + private Optional findFirstUnnamedParameter(List params) { + return params.stream().filter(p -> p.getName() == null).findFirst(); } /** diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend index 60dc138c2..3bedd2d8d 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend @@ -30,14 +30,15 @@ class RosettaTranslateValidationTest implements RosettaIssueCodes { b string (1..1) translate source FooBar1 { - translate Foo <=> Bar: - # a <=> b - # 42 <=> 42 + Foo from Bar: + + a + [from b] } translate source FooBar2 extends FooBar1 { - translate string, foo Foo <=> bar Bar: - # foo -> a, foo <=> bar -> b + Foo from bar Bar, string: + + a + [from bar -> b + item] } '''.parseRosettaWithNoIssues } From 9907d843b266572e38a8e4d131c6cc062c6bb140 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 28 May 2024 17:30:33 +0200 Subject: [PATCH 05/37] WIP --- rosetta-lang/model/RosettaTranslate.xcore | 4 +- .../java/com/regnosys/rosetta/Rosetta.xtext | 4 +- .../scoping/RosettaScopeProvider.xtend | 34 ++- .../rosetta/utils/ImplicitVariableUtil.java | 6 +- .../regnosys/rosetta/utils/TranslateUtil.java | 62 +++++ .../AbstractDeclarativeRosettaValidator.java | 108 +++++++++ .../validation/RosettaSimpleValidator.xtend | 94 +------- .../RosettaTranslateValidator.xtend | 56 +++++ .../rosetta/validation/RosettaValidator.xtend | 2 +- .../StandaloneRosettaTypingValidator.java | 1 + .../tests/RosettaTranslateParsingTest.xtend | 47 ++++ .../RosettaTranslateValidationTest.xtend | 223 +++++++++++++++++- 12 files changed, 527 insertions(+), 114 deletions(-) create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/validation/AbstractDeclarativeRosettaValidator.java create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend create mode 100644 rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaTranslateParsingTest.xtend diff --git a/rosetta-lang/model/RosettaTranslate.xcore b/rosetta-lang/model/RosettaTranslate.xcore index 8a7ced8a8..2e564dfc6 100644 --- a/rosetta-lang/model/RosettaTranslate.xcore +++ b/rosetta-lang/model/RosettaTranslate.xcore @@ -40,10 +40,10 @@ class TranslationRule { } class TranslateInstruction { - contains RosettaExpression expression + contains RosettaExpression[] expressions } class TranslateMetaInstruction { + contains RosettaExpression[] expressions refers RosettaMetaType metaFeature - contains RosettaExpression expression } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index 240cfc6bb..4084e7299 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -901,10 +901,10 @@ TranslationRule: ; TranslateInstruction: - '[' 'from' expression=RosettaCalcExpression ']' + '[' 'from' expressions+=RosettaCalcExpression (',' expressions+=RosettaCalcExpression)* ']' ; TranslateMetaInstruction: - '[' 'meta' metaFeature=[RosettaMetaType] 'from' expression=RosettaCalcExpression ']' + '[' 'meta' metaFeature=[RosettaMetaType] 'from' expressions+=RosettaCalcExpression (',' expressions+=RosettaCalcExpression)* ']' ; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index e8c159d99..a58185dad 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -60,6 +60,8 @@ import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair import com.regnosys.rosetta.rosetta.translate.Translation import com.regnosys.rosetta.rosetta.translate.TranslationRule import com.regnosys.rosetta.types.TypeSystem +import com.regnosys.rosetta.rosetta.translate.TranslateMetaInstruction +import com.regnosys.rosetta.rosetta.simple.Annotated /** * This class contains custom scoping description. @@ -234,6 +236,20 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { } return IScope.NULLSCOPE } + case TRANSLATE_META_INSTRUCTION__META_FEATURE: { + if (context instanceof TranslateMetaInstruction) { + val container = context.eContainer + val annotated = if (container instanceof TranslationRule) { + container.feature + } else if (container instanceof Translation) { + container.resultType.type + } + if (annotated instanceof Annotated) { + return new SimpleScope(getMetaDescriptions(annotated)) + } + } + return IScope.NULLSCOPE + } } // LOGGER.warn('''No scope defined for «context.class.simpleName» referencing «reference.name».''') return defaultScope(context, reference) @@ -333,14 +349,20 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { receiver.symbol } if (feature instanceof Attribute) { - val metas = feature.metaAnnotations.map[it.attribute?.name].filterNull.toList - if (metas !== null && !metas.isEmpty) { - allPosibilities.addAll(configs.findMetaTypes(feature).filter[ - metas.contains(it.name.lastSegment.toString) - ].map[new AliasedEObjectDescription(QualifiedName.create(it.name.lastSegment), it)]) - } + allPosibilities.addAll(getMetaDescriptions(feature)) } return new SimpleScope(allPosibilities) } + + private def Iterable getMetaDescriptions(Annotated obj) { + val metas = obj.metaAnnotations.map[it.attribute?.name].filterNull.toList + if (!metas.isEmpty) { + configs.findMetaTypes(obj).filter[ + metas.contains(it.name.lastSegment.toString) + ].map[new AliasedEObjectDescription(QualifiedName.create(it.name.lastSegment), it)] + } else { + emptyList + } + } } \ No newline at end of file diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java index 2e743a1f4..2af1a7cb5 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/ImplicitVariableUtil.java @@ -25,7 +25,6 @@ import com.regnosys.rosetta.rosetta.RosettaRule; import com.regnosys.rosetta.rosetta.translate.Translation; import com.regnosys.rosetta.rosetta.translate.TranslationParameter; -import com.regnosys.rosetta.rosetta.translate.TranslationRule; import com.regnosys.rosetta.rosetta.expression.ExpressionFactory; import com.regnosys.rosetta.rosetta.expression.InlineFunction; import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation; @@ -62,9 +61,8 @@ public Optional findObjectDefiningImplicitVariable(EObject co } } else if (container instanceof RosettaRule) { return Optional.of(container); - } else if (container instanceof TranslationRule) { - TranslationRule rule = (TranslationRule)container; - Translation trans = rule.getTranslation(); + } else if (container instanceof Translation) { + Translation trans = (Translation)container; return findFirstUnnamedParameter(trans.getParameters()); } prev = container; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java new file mode 100644 index 000000000..0fe10832b --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java @@ -0,0 +1,62 @@ +package com.regnosys.rosetta.utils; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import com.google.common.collect.Streams; +import com.regnosys.rosetta.rosetta.translate.TranslateSource; +import com.regnosys.rosetta.rosetta.translate.Translation; +import com.regnosys.rosetta.types.RType; +import com.regnosys.rosetta.types.TypeSystem; + +public class TranslateUtil { + private final TypeSystem typeSystem; + + @Inject + public TranslateUtil(TypeSystem typeSystem) { + this.typeSystem = typeSystem; + } + + private Stream getAllTranslations(TranslateSource source) { + return Streams.concat( + source.getSuperSources().stream().flatMap(s -> getAllTranslations(s)), + source.getTranslations().stream() + ); + } + + public List findMatches(TranslateSource source, RType resultType, List inputTypes) { + return getAllTranslations(source) + .filter(t -> matches(t, resultType, inputTypes)) + .collect(Collectors.toList()); + } + + public boolean hasAnyMatch(TranslateSource source, RType resultType, List inputTypes) { + return getAllTranslations(source) + .anyMatch(t -> matches(t, resultType, inputTypes)); + } + + public boolean matches(Translation translation, RType resultType, List inputTypes) { + // Check parameters match + if (translation.getParameters().size() != inputTypes.size()) { + return false; + } + for (int i=0;i getEPackages() { + List result = new ArrayList(); + result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/Rosetta")); + result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaSimple")); + result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaExpression")); + result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaTranslate")); + return result; + } + + @Override + public void register(EValidatorRegistrar registrar) { + } + + @Override + protected void handleExceptionDuringValidation(Throwable targetException) throws RuntimeException { + super.handleExceptionDuringValidation(targetException); + LOGGER.error(targetException.getMessage(), targetException); + } + + @Override + protected MethodWrapper createMethodWrapper(AbstractDeclarativeValidator instanceToUse, Method method) { + return new RosettaMethodWrapper(instanceToUse, method); + } + + private static class RosettaMethodWrapper extends MethodWrapper { + protected RosettaMethodWrapper(AbstractDeclarativeValidator instance, Method m) { + super(instance, m); + } + + @Override + public void invoke(State state) { + try { + super.invoke(state); + } catch (Exception e) { + String message = "Unexpected validation failure running " + getMethod().getName(); + LOGGER.error(message, e); + state.hasErrors = true; + state.chain.add(createDiagnostic(message, state)); + } + } + + private Diagnostic createDiagnostic(String message, State state) { + return new FeatureBasedDiagnostic(Diagnostic.ERROR, message, state.currentObject, null, -1, state.currentCheckType, null); + } + } + + protected void errorKeyword(String message, EObject o, Keyword keyword) { + INode k = findDirectKeyword(o, keyword); + if (k != null) { + getMessageAcceptor().acceptError( + message, + o, + k.getOffset(), + k.getLength(), + null + ); + } + } + protected INode findDirectKeyword(EObject o, Keyword keyword) { + return findDirectKeyword(o, keyword.getValue()); + } + protected INode findDirectKeyword(EObject o, String keyword) { + ICompositeNode node = NodeModelUtils.findActualNodeFor(o); + return findDirectKeyword(node, keyword); + } + protected INode findDirectKeyword(ICompositeNode node, String keyword) { + for (INode n : node.getChildren()) { + EObject ge = n.getGrammarElement(); + if (ge instanceof Keyword && ((Keyword)ge).getValue().equals(keyword)) { // I compare the keywords by value instead of directly by reference because of an issue that sometimes arises when running multiple tests consecutively. I'm not sure what the issue is. + return n; + } + if ((ge instanceof RuleCall || ge instanceof Action) && n instanceof ICompositeNode && !(ge.eContainer() instanceof Assignment)) { + INode keywordInFragment = findDirectKeyword((ICompositeNode)n, keyword); + if (keywordInFragment != null) { + return keywordInFragment; + } + } + } + return null; + } +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend index d2e7cd310..97902191c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend @@ -71,7 +71,6 @@ import com.regnosys.rosetta.utils.ExternalAnnotationUtil import com.regnosys.rosetta.utils.ExternalAnnotationUtil.CollectRuleVisitor import com.regnosys.rosetta.utils.ImplicitVariableUtil import com.regnosys.rosetta.utils.RosettaConfigExtension -import java.lang.reflect.Method import java.time.format.DateTimeFormatter import java.util.List import java.util.Map @@ -79,23 +78,15 @@ import java.util.Set import java.util.Stack import java.util.regex.Pattern import java.util.regex.PatternSyntaxException -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.eclipse.emf.common.util.Diagnostic import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.EPackage import org.eclipse.emf.ecore.EReference import org.eclipse.xtext.EcoreUtil2 -import org.eclipse.xtext.Keyword import org.eclipse.xtext.naming.IQualifiedNameProvider import org.eclipse.xtext.naming.QualifiedName import org.eclipse.xtext.nodemodel.util.NodeModelUtils import org.eclipse.xtext.resource.XtextSyntaxDiagnostic import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider -import org.eclipse.xtext.validation.AbstractDeclarativeValidator import org.eclipse.xtext.validation.Check -import org.eclipse.xtext.validation.EValidatorRegistrar -import org.eclipse.xtext.validation.FeatureBasedDiagnostic import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* @@ -113,13 +104,9 @@ import com.regnosys.rosetta.types.RDataType import com.regnosys.rosetta.rosetta.ParametrizedRosettaType import com.regnosys.rosetta.rosetta.RosettaRootElement import com.regnosys.rosetta.rosetta.expression.ThenOperation -import org.eclipse.xtext.RuleCall import org.eclipse.xtext.nodemodel.INode -import org.eclipse.xtext.nodemodel.ICompositeNode -import org.eclipse.xtext.Assignment import com.regnosys.rosetta.rosetta.expression.RosettaOperation import com.regnosys.rosetta.types.CardinalityProvider -import org.eclipse.xtext.Action import com.regnosys.rosetta.rosetta.expression.ParseOperation import com.regnosys.rosetta.rosetta.expression.ToStringOperation import com.regnosys.rosetta.types.builtin.RBasicType @@ -134,7 +121,7 @@ import com.regnosys.rosetta.rosetta.RosettaReport // TODO: split expression validator // TODO: type check type call arguments -class RosettaSimpleValidator extends AbstractDeclarativeValidator { +class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Inject extension RosettaExtensions @Inject extension RosettaExpectedTypeProvider @@ -152,85 +139,6 @@ class RosettaSimpleValidator extends AbstractDeclarativeValidator { @Inject extension TypeSystem @Inject extension RosettaGrammarAccess - static final Logger log = LoggerFactory.getLogger(RosettaSimpleValidator); - - protected override List getEPackages() { - val result = newArrayList - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/Rosetta")); - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaSimple")); - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaExpression")); - result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaTranslate")); - return result; - } - - override void register(EValidatorRegistrar registrar) { - } - - protected override void handleExceptionDuringValidation(Throwable targetException) throws RuntimeException { - super.handleExceptionDuringValidation(targetException); - log.error(targetException.getMessage(), targetException); - } - - protected override MethodWrapper createMethodWrapper(AbstractDeclarativeValidator instanceToUse, Method method) { - return new RosettaMethodWrapper(instanceToUse, method); - } - - protected static class RosettaMethodWrapper extends MethodWrapper { - protected new(AbstractDeclarativeValidator instance, Method m) { - super(instance, m) - } - - override void invoke(State state) { - try { - super.invoke(state); - } catch (Exception e) { - val String message = "Unexpected validation failure running " + method.name - log.error(message, e); - state.hasErrors = true; - state.chain.add(createDiagnostic(message, state)) - } - } - - def Diagnostic createDiagnostic(String message, State state) { - new FeatureBasedDiagnostic(Diagnostic.ERROR, message, state.currentObject, null, -1, state.currentCheckType, - null, null) - } - } - - private def errorKeyword(String message, EObject o, Keyword keyword) { - val k = findDirectKeyword(o, keyword) - if (k !== null) { - messageAcceptor.acceptError( - message, - o, - k.offset, - k.length, - null - ) - } - } - private def INode findDirectKeyword(EObject o, Keyword keyword) { - findDirectKeyword(o, keyword.value) - } - private def INode findDirectKeyword(EObject o, String keyword) { - val node = NodeModelUtils.findActualNodeFor(o) - findDirectKeyword(node, keyword) - } - private def INode findDirectKeyword(ICompositeNode node, String keyword) { - for (n : node.children) { - val ge = n.grammarElement - if (ge instanceof Keyword && (ge as Keyword).value == keyword) { // I compare the keywords by value instead of directly by reference because of an issue that sometimes arises when running multiple tests consecutively. I'm not sure what the issue is. - return n - } - if ((ge instanceof RuleCall || ge instanceof Action) && n instanceof ICompositeNode && !(ge.eContainer instanceof Assignment)) { - val keywordInFragment = findDirectKeyword(n as ICompositeNode, keyword) - if (keywordInFragment !== null) { - return keywordInFragment - } - } - } - } - @Check def void ruleMustHaveInputTypeDeclared(RosettaRule rule) { if (rule.input === null) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend new file mode 100644 index 000000000..d375bfb14 --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend @@ -0,0 +1,56 @@ +package com.regnosys.rosetta.validation + +import org.eclipse.xtext.validation.Check +import com.regnosys.rosetta.rosetta.translate.Translation +import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* +import com.regnosys.rosetta.rosetta.translate.TranslateInstruction +import com.regnosys.rosetta.rosetta.translate.TranslateMetaInstruction +import com.regnosys.rosetta.rosetta.translate.TranslationRule +import javax.inject.Inject +import com.regnosys.rosetta.types.CardinalityProvider +import com.regnosys.rosetta.utils.TranslateUtil + +class RosettaTranslateValidator extends AbstractDeclarativeRosettaValidator { + + @Inject extension CardinalityProvider + @Inject extension TranslateUtil + + @Check + def void checkUniqueTranslateParameterNames(Translation translation) { + val visited = newHashSet + var unnamedSeen = false + for (param: translation.parameters) { + if (param.name === null) { + if (unnamedSeen) { + error('''Cannot have multiple unnamed parameters.''', param, null); + } + unnamedSeen = true + } else { + if (!visited.add(param.name)) { + error('''Duplicate parameter name `«param.name»`.''', param, ROSETTA_NAMED__NAME); + } + } + } + } + + @Check + def void checkTranslateInstruction(TranslateInstruction instruction) { + val container = instruction.eContainer + if (container instanceof TranslationRule) { + if (!container.feature.isFeatureMulti && instruction.expression.isMulti) { + error('''Expression must be of single cardinality when mapping to attribute `«container.feature.name»` of single cardinality .''', instruction.expression, null); + } + } else if (container instanceof Translation) { + if (instruction.expression.isMulti) { + error('''Expression must be of single cardinality when mapping to a type.''', instruction.expression, null); + } + } + } + + @Check + def void checkTranslateMetaInstruction(TranslateMetaInstruction metaInstruction) { + if (metaInstruction.expression.isMulti) { + error('''Expression must be of single cardinality.''', metaInstruction.expression, null); + } + } +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend index 39de06559..7b4a513c1 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaValidator.xtend @@ -10,7 +10,7 @@ import org.eclipse.xtext.validation.ComposedChecks * * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation */ -@ComposedChecks(validators = #[RosettaSimpleValidator, StandaloneRosettaTypingValidator]) +@ComposedChecks(validators = #[RosettaSimpleValidator, StandaloneRosettaTypingValidator, RosettaTranslateValidator]) class RosettaValidator extends AbstractRosettaValidator { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java index 0abaee0e2..5a8442683 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/StandaloneRosettaTypingValidator.java @@ -76,6 +76,7 @@ protected List getEPackages() { result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/Rosetta")); result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaSimple")); result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaExpression")); + result.add(EPackage.Registry.INSTANCE.getEPackage("http://www.rosetta-model.com/RosettaTranslate")); return result; } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaTranslateParsingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaTranslateParsingTest.xtend new file mode 100644 index 000000000..e7cac2c90 --- /dev/null +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaTranslateParsingTest.xtend @@ -0,0 +1,47 @@ +/* + * generated by Xtext 2.10.0 + */ +package com.regnosys.rosetta.tests + +import com.regnosys.rosetta.tests.util.ModelHelper +import org.eclipse.xtext.testing.InjectWith +import org.eclipse.xtext.testing.extensions.InjectionExtension +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.^extension.ExtendWith + +import javax.inject.Inject + +@ExtendWith(InjectionExtension) +@InjectWith(RosettaInjectorProvider) +class RosettaTranslateParsingTest { + + @Inject extension ModelHelper + + @Test + def void testTranslate() { + ''' + metaType location string + + type Foo: + a string (1..1) + [metadata location] + + type Bar: + b string (1..1) + c string (1..1) + + translate source FooBar1 { + Foo from Bar: + + a + [from b] + [meta location from c] + } + + translate source FooBar2 extends FooBar1 { + Foo from bar Bar, string: + + a + [from bar -> b + item] + } + '''.parseRosettaWithNoIssues + } +} diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend index 3bedd2d8d..b0103f69e 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.^extension.ExtendWith import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* +import static com.regnosys.rosetta.rosetta.translate.TranslatePackage.Literals.* import javax.inject.Inject @ExtendWith(InjectionExtension) @@ -21,24 +22,234 @@ class RosettaTranslateValidationTest implements RosettaIssueCodes { @Inject extension ModelHelper @Test - def void testTranslate() { - ''' + def void testDuplicateParameterNamesAreDisallowed() { + val model = ''' + type Foo: + a string (1..1) + + type Bar: + b string (1..1) + + translate source FooBar { + Foo from var Bar, var string: + + a + [from var] + } + '''.parseRosetta + + model.assertError(TRANSLATION_PARAMETER, null, + "Duplicate parameter name `var`." + ) + } + + @Test + def void testMaxOneUnnamedParameter() { + val model = ''' type Foo: a string (1..1) type Bar: b string (1..1) - translate source FooBar1 { + translate source FooBar { + Foo from Bar, string: + + a + [from b] + } + '''.parseRosetta + + model.assertError(TRANSLATION_PARAMETER, null, + "Cannot have multiple unnamed parameters." + ) + } + + @Test + def void testMultiToSingleCardinalityIsDisallowed() { + val model = ''' + type Foo: + a string (1..1) + + type Bar: + b string (0..2) + + translate source FooBar { + Foo from Bar: + + a + [from b] + } + '''.parseRosetta + + model.assertError(ROSETTA_EXPRESSION, null, + "Expression must be of single cardinality when mapping to attribute `a` of single cardinality." + ) + } + + @Test + def void testTypeTranslationMustBeSingle() { + val model = ''' + type Foo: + a string (1..1) + + type Bar: + b string (0..2) + + translate source FooBar { + Foo from Bar: + [from b] + + Foo from string: + + a + [from item] + } + '''.parseRosetta + + model.assertError(ROSETTA_EXPRESSION, null, + "Expression must be of single cardinality when mapping to a type." + ) + } + + @Test + def void testMetaTranslationMustBeSingle() { + val model = ''' + type Foo: + a string (1..1) + [metadata scheme] + + type Bar: + b string (0..2) + + translate source FooBar { + Foo from Bar: + + a + [meta scheme from b] + } + '''.parseRosetta + + model.assertError(ROSETTA_EXPRESSION, null, + "Expression must be of single cardinality." + ) + } + + @Test + def void testMatchingTranslationMustExist() { + val model = ''' + type Foo: + a string (1..1) + + type Bar: + b Qux (1..1) + + type Qux: + c string (1..1) + + translate source FooBar { + Foo from Bar: + + a + [from b] + } + '''.parseRosetta + + model.assertError(ROSETTA_EXPRESSION, null, + "aaaaaaaaaaaat" + ) + } + + @Test + def void testMatchingTypeTranslationMustExist() { + val model = ''' + type Foo: + a string (1..1) + + type Bar: + b Qux (1..1) + + type Qux: + c string (1..1) + + translate source FooBar { + Foo from Bar: + [from b] + } + '''.parseRosetta + + model.assertError(ROSETTA_EXPRESSION, null, + "aaaaaaaaaaaat" + ) + } + + @Test + def void testMatchingTranslationExists() { + ''' + type Foo: + a string (1..1) + + type Bar: + b Qux (1..1) + + type Qux: + c string (1..1) + + translate source FooBar { + Foo from Bar: + + a + [from b, 42] + + string from Qux, context number: + [from c] + } + '''.parseRosettaWithNoIssues + } + + @Test + def void testMatchingTranslationExistsInParent() { + ''' + type Foo: + a string (1..1) + + type Bar: + b Qux (1..1) + + type Qux: + c string (1..1) + + translate source Parent { + string from Qux: + [from c] + } + + translate source FooBar extends Parent { Foo from Bar: + a [from b] } + '''.parseRosettaWithNoIssues + } + + @Test + def void testMatchingTranslationWithExtendsExists() { + ''' + type Foo: + a AttrType (1..1) + + type SuperAttrType: + + type AttrType extends SuperAttrType: + sub string (1..1) - translate source FooBar2 extends FooBar1 { - Foo from bar Bar, string: + type Bar: + b Qux (1..1) + + type SuperQux: + + type Qux extends Qux: + c string (1..1) + + translate source FooBar { + Foo from Bar: + a - [from bar -> b + item] + [from b] + + SuperAttrType from SuperQux: } '''.parseRosettaWithNoIssues } From 33fd8486f770adecd5cb3558ed547b4a859a0988 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Fri, 7 Jun 2024 13:23:25 +0200 Subject: [PATCH 06/37] WIP --- rosetta-lang/model/RosettaTranslate.xcore | 4 +- .../regnosys/rosetta/utils/TranslateUtil.java | 7 ++- .../RosettaTranslateValidator.xtend | 48 +++++++++++++++---- .../RosettaTranslateValidationTest.xtend | 14 ++++++ 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/rosetta-lang/model/RosettaTranslate.xcore b/rosetta-lang/model/RosettaTranslate.xcore index 2e564dfc6..bb3e1c474 100644 --- a/rosetta-lang/model/RosettaTranslate.xcore +++ b/rosetta-lang/model/RosettaTranslate.xcore @@ -15,10 +15,12 @@ import com.regnosys.rosetta.rosetta.expression.RosettaExpression class TranslateSource extends RosettaRootElement, RosettaNamed { refers TranslateSource[] superSources - contains Translation[] translations + contains Translation[] translations opposite source } class Translation { + refers TranslateSource source opposite translations + contains TypeCall resultType contains TranslationParameter[] parameters contains TranslateInstruction[] typeInstructions diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java index 0fe10832b..58eca08e8 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java @@ -10,14 +10,17 @@ import com.regnosys.rosetta.rosetta.translate.TranslateSource; import com.regnosys.rosetta.rosetta.translate.Translation; import com.regnosys.rosetta.types.RType; +import com.regnosys.rosetta.types.RosettaTypeProvider; import com.regnosys.rosetta.types.TypeSystem; public class TranslateUtil { private final TypeSystem typeSystem; + private final RosettaTypeProvider typeProvider; @Inject - public TranslateUtil(TypeSystem typeSystem) { + public TranslateUtil(TypeSystem typeSystem, RosettaTypeProvider typeProvider) { this.typeSystem = typeSystem; + this.typeProvider = typeProvider; } private Stream getAllTranslations(TranslateSource source) { @@ -45,7 +48,7 @@ public boolean matches(Translation translation, RType resultType, List in } for (int i=0;i Date: Fri, 7 Jun 2024 14:39:46 +0200 Subject: [PATCH 07/37] Fixed highlighting --- rosetta-ide/rosetta.tmLanguage.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rosetta-ide/rosetta.tmLanguage.yaml b/rosetta-ide/rosetta.tmLanguage.yaml index edb7a7c2e..b2bc02fb4 100644 --- a/rosetta-ide/rosetta.tmLanguage.yaml +++ b/rosetta-ide/rosetta.tmLanguage.yaml @@ -45,8 +45,8 @@ variables: synonymAnnotationSectionEnd: (?={{synonymAnnotationSection}})|(?=\])|{{rootEnd}} docReferenceAnnotationSection: '{{wordStart}}(rationale|rationale_author|structured_provision|provision|reportedField){{wordEnd}}' docReferenceAnnotationSectionEnd: (?={{docReferenceAnnotationSection}}|\])|{{sectionEnd}} - expressionEnd: '(?=,|\))|{{sectionEnd}}' - functionalOperationEnd: (?=\])|(?={{listOperation}}|{{wordStart}}(else|then){{wordEnd}})|{{expressionEnd}} + expressionEnd: '(?=,|\)|\])|{{sectionEnd}}' + functionalOperationEnd: (?={{listOperation}}|{{wordStart}}(else|then){{wordEnd}})|{{expressionEnd}} patterns: - include: '#model' From 6448a71f156ac11fb9987722f9c7e96468d4fce0 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Mon, 10 Jun 2024 17:53:59 +0200 Subject: [PATCH 08/37] Fixed --- rosetta-ide/rosetta.tmLanguage.yaml | 60 ++++++++----------- .../RosettaTranslateValidator.xtend | 3 +- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/rosetta-ide/rosetta.tmLanguage.yaml b/rosetta-ide/rosetta.tmLanguage.yaml index 1f79e525f..a9412c8b5 100644 --- a/rosetta-ide/rosetta.tmLanguage.yaml +++ b/rosetta-ide/rosetta.tmLanguage.yaml @@ -503,17 +503,16 @@ repository: - include: '#comment' - include: '#typeCall' - include: '#typeCall' - - name: meta.translation.result-type.rosetta - begin: '(?={{identifier}})' - end: (?={{wordStart}}from{{wordEnd}})|(?=\:|\})|{{rootEnd}} - patterns: - - include: '#comment' - - name: keyword.other.from.rosetta - match: '{{wordStart}}from{{wordEnd}}' - - include: '#typeCall' - - include: '#comment' + - name: meta.translation.result-type.rosetta + begin: '(?={{identifier}})' + end: (?={{wordStart}}from{{wordEnd}})|(?=\:|\})|{{rootEnd}} + patterns: + - include: '#comment' + - name: keyword.other.from.rosetta + match: '{{wordStart}}from{{wordEnd}}' + - include: '#typeCall' - include: '#colon' - - include: '#annotation' + - include: '#translateAnnotation' - name: meta.translation-attribute-mapping.rosetta begin: '(\+|-)\s*({{identifier}})' beginCaptures: @@ -522,25 +521,7 @@ repository: end: (?={{identifier}}|\}|\+|-)|{{rootEnd}} patterns: - include: '#comment' - - include: '#annotation' - - - - name: meta.translation.rosetta - begin: '{{wordStart}}translate{{wordEnd}}' - beginCaptures: - 0: { name: keyword.other.rosetta } - end: (?={{wordStart}}translate{{wordEnd}}|\})|{{rootEnd}} - patterns: - - include: '#comment' - - begin: (?<={{wordStart}}translate{{wordEnd}}) - end: ':' - patterns: - - include: '#comment' - - begin: '#' - end: (?=#|{{wordStart}}translate{{wordEnd}}|\})|{{rootEnd}} - patterns: - - include: '#comment' - - include: '#expression' + - include: '#translateAnnotation' rosettaRule: patterns: @@ -691,8 +672,6 @@ repository: - include: '#synonymAnnotationBody' - include: '#referenceAnnotationBody' - include: '#ruleReferenceAnnotationBody' - - include: '#translateInstructionAnnotationBody' - - include: '#translateMetaInstructionAnnotationBody' - include: '#customAnnotationBody' prefixAnnotationBody: @@ -814,21 +793,32 @@ repository: - name: entity.name.rule.rosetta match: '{{identifier}}' - translateInstructionAnnotationBody: + translateAnnotation: name: meta.annotated.translate.rosetta + begin: (\[) + beginCaptures: + 1: { name: punctuation.annotation.begin.rosetta } + end: (\])|{{unambiguousRootEnd}} + endCaptures: + 1: { name: punctuation.annotation.end.rosetta } + patterns: + - include: '#comment' + - include: '#translateInstructionAnnotationBody' + - include: '#translateMetaInstructionAnnotationBody' + + translateInstructionAnnotationBody: begin: '{{wordStart}}from{{wordEnd}}' beginCaptures: - 0: { name: variable.annotation.translate-instruction.rosetta } + 0: { name: keyword.other.from.rosetta } end: (?=\]|\})|{{rootEnd}} patterns: - include: '#comment' - include: '#expression' translateMetaInstructionAnnotationBody: - name: meta.annotated.translate.rosetta begin: '{{wordStart}}meta{{wordEnd}}' beginCaptures: - 0: { name: variable.annotation.translate-meta-instruction.rosetta } + 0: { name: keyword.other.meta.rosetta } end: (?=\]|\})|{{rootEnd}} patterns: - include: '#comment' diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend index 445cbac00..4e8df0623 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend @@ -52,7 +52,8 @@ class RosettaTranslateValidator extends AbstractDeclarativeRosettaValidator { // No direct assignment - check if an appropriate translation exists val source = EcoreUtil2.getContainerOfType(instruction, TranslateSource) if (!source.hasAnyMatch(resultType, inputTypes)) { - error('''No translation exists to translate «FOR input : inputTypes SEPARATOR ', '»«input.name»«ENDFOR» into «resultType.name».''', instruction, null); + val multipleInputs = inputTypes.size >= 2 + error('''No translation exists to translate «IF multipleInputs»(«ENDIF»«FOR input : inputTypes SEPARATOR ', '»«input.name»«ENDFOR»«IF multipleInputs»)«ENDIF» into «resultType.name».''', instruction, null); } } } From dbc38a202200a693bea8e931b3e661a55053f1eb Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Fri, 14 Jun 2024 09:35:35 +0200 Subject: [PATCH 09/37] Added param to branch deploy --- .github/workflows/branch-deploy.yml | 30 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/.github/workflows/branch-deploy.yml b/.github/workflows/branch-deploy.yml index b01a03ec0..0e5b4e0ab 100644 --- a/.github/workflows/branch-deploy.yml +++ b/.github/workflows/branch-deploy.yml @@ -2,6 +2,11 @@ name: Deploy a branch as a snapshot version. on: workflow_dispatch: + inputs: + run-tests: + type: boolean + description: Run tests + default: true # Cancel previous jobs concurrency: @@ -12,15 +17,16 @@ jobs: deploy-branch: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Normalize branch name - id: normalize-branch-name - uses: ./.github/actions/normalize-branch-name - - name: Set version - run: mvn -B versions:set -DnewVersion=0.0.0.${{ steps.normalize-branch-name.outputs.normalized }}-SNAPSHOT - - uses: ./.github/actions/maven-build - with: - build-command: deploy # Deploy a snapshot build of main - env: - CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} - CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} + - uses: actions/checkout@v4 + - name: Normalize branch name + id: normalize-branch-name + uses: ./.github/actions/normalize-branch-name + - name: Set version + run: mvn -B versions:set -DgenerateBackupPoms=false -DnewVersion=0.0.0.${{ steps.normalize-branch-name.outputs.normalized }}-SNAPSHOT + - uses: ./.github/actions/maven-build + with: + build-command: deploy # Deploy a snapshot build of a chosen branch + run-tests: ${{ github.event.inputs.run-tests }} + env: + CI_DEPLOY_USERNAME: ${{ vars.CI_DEPLOY_USERNAME }} + CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} From 8c543c5b0d4b7686af9d03167a8eb68d931f53f8 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Fri, 14 Jun 2024 10:29:28 +0200 Subject: [PATCH 10/37] Branch deploy fix --- .github/workflows/branch-deploy.yml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/branch-deploy.yml b/.github/workflows/branch-deploy.yml index 0e5b4e0ab..12a154f70 100644 --- a/.github/workflows/branch-deploy.yml +++ b/.github/workflows/branch-deploy.yml @@ -18,15 +18,27 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Set up Maven Central Repository + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + architecture: x64 + cache: maven + server-id: ossrh + server-username: CI_DEPLOY_USERNAME + server-password: CI_DEPLOY_PASSWORD + gpg-private-key: ${{ secrets.RUNE_GPG_PRIVATE_KEY }} + gpg-passphrase: GPG_PASSPHRASE - name: Normalize branch name id: normalize-branch-name uses: ./.github/actions/normalize-branch-name - name: Set version run: mvn -B versions:set -DgenerateBackupPoms=false -DnewVersion=0.0.0.${{ steps.normalize-branch-name.outputs.normalized }}-SNAPSHOT - - uses: ./.github/actions/maven-build - with: - build-command: deploy # Deploy a snapshot build of a chosen branch - run-tests: ${{ github.event.inputs.run-tests }} + - name: Publish package + run: mvn -B -U clean deploy env: - CI_DEPLOY_USERNAME: ${{ vars.CI_DEPLOY_USERNAME }} + GPG_KEYNAME: ${{ secrets.RUNE_GPG_KEYNAME }} + GPG_PASSPHRASE: ${{ secrets.RUNE_GPG_PASSPHRASE }} + CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} From 352ef40c52413d252ad1655839c19a75a6554e05 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Tue, 2 Jul 2024 15:03:02 +0100 Subject: [PATCH 11/37] add import as syntax with validation --- rosetta-lang/model/Rosetta.xcore | 3 +++ .../main/java/com/regnosys/rosetta/Rosetta.xtext | 5 +++-- .../rosetta/validation/RosettaSimpleValidator.xtend | 8 +++++++- .../validation/RosettaTranslateValidationTest.xtend | 2 +- .../rosetta/validation/RosettaValidatorTest.xtend | 13 +++++++++++++ 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/rosetta-lang/model/Rosetta.xcore b/rosetta-lang/model/Rosetta.xcore index 1c44b07c6..6c8353fa5 100644 --- a/rosetta-lang/model/Rosetta.xcore +++ b/rosetta-lang/model/Rosetta.xcore @@ -23,8 +23,11 @@ class RosettaModel extends RosettaDefinable { class Import { String importedNamespace + String namespaceAlias + } + /********************************************************************** * Common types */ diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index da7484375..97a3ec2ba 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -27,10 +27,11 @@ QualifiedName: ; Import: - 'import' importedNamespace=QualifiedNameWithWildcard; - + 'import' importedNamespace=(QualifiedNameWithWildcard) ('as' namespaceAlias=STRING)?; + QualifiedNameWithWildcard: QualifiedName ('.' '*')?; + RootElement: Annotation diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend index 9964e6a16..c5cca7c2e 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend @@ -1421,7 +1421,13 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { for (ns : model.imports) { if (ns.importedNamespace !== null) { val qn = QualifiedName.create(ns.importedNamespace.split('\\.')) - val isUsed = if (qn.lastSegment.equals('*')) { + val isWildcard = qn.lastSegment.equals('*'); + if (!isWildcard && ns.namespaceAlias !== null) { + error('''"as" statement can only be used with wildcard imports''', ns, IMPORT__NAMESPACE_ALIAS); + } + + + val isUsed = if (isWildcard) { usedNames.stream.anyMatch[startsWith(qn.skipLast(1)) && segmentCount === qn.segmentCount] } else { usedNames.contains(qn) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend index 272f61fba..07930760d 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend @@ -38,7 +38,7 @@ class RosettaTranslateValidationTest implements RosettaIssueCodes { '''.parseRosetta model.assertError(TRANSLATION_PARAMETER, null, - "Duplicate parameter name `var`." + '"as" statement can only be used with wildcard imports' ) } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index 01b81579f..80d135418 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -20,6 +20,8 @@ import org.junit.jupiter.api.^extension.ExtendWith import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* +import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* + import javax.inject.Inject import com.regnosys.rosetta.tests.util.ExpressionParser @@ -31,6 +33,17 @@ class RosettaValidatorTest implements RosettaIssueCodes { @Inject extension ModelHelper @Inject extension ExpressionParser + @Test + def void testCannotUseImportAliasesWithoutWildcard() { + val model = ''' + import foo.bar.Test as someAlias + '''.parseRosetta + + model.assertError(IMPORT, null, + '"as" statement can only be used with wildcard import' + ) + } + @Test def void testCannotAccessMetaFeatureAfterDeepFeatureCall() { val context = ''' From 4c35550da51fee45ae514d09ea0781d84d460752 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Tue, 2 Jul 2024 16:02:43 +0100 Subject: [PATCH 12/37] use correct xtext type for alias --- .../java/com/regnosys/rosetta/Rosetta.xtext | 2 +- .../validation/RosettaValidatorTest.xtend | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index 97a3ec2ba..824530464 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -27,7 +27,7 @@ QualifiedName: ; Import: - 'import' importedNamespace=(QualifiedNameWithWildcard) ('as' namespaceAlias=STRING)?; + 'import' importedNamespace=(QualifiedNameWithWildcard) ('as' namespaceAlias=ValidID)?; QualifiedNameWithWildcard: QualifiedName ('.' '*')?; diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index 80d135418..ed1d6a1c0 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -44,6 +44,29 @@ class RosettaValidatorTest implements RosettaIssueCodes { ) } + @Test + def void testCanUserImportAlisesWhenWildcardPresent() { + val model1 = ''' + namespace foo.bar + + type A: + id string (1..1) + ''' + + val model2 = ''' + namespace test + + import foo.bar.* as someAlias + + + + type B: + a A (1..1) + ''' + + #[model1, model2].parseRosettaWithNoIssues + } + @Test def void testCannotAccessMetaFeatureAfterDeepFeatureCall() { val context = ''' From e977086683964d518daedbc44de7052bcf585678 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Wed, 3 Jul 2024 17:38:49 +0100 Subject: [PATCH 13/37] add failing import alias test --- .../rosetta/tests/RosettaParsingTest.xtend | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend index 4bed0876a..6c6a565a6 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend @@ -34,6 +34,28 @@ class RosettaParsingTest { @Inject extension ValidationTestHelper @Inject extension ExpressionParser + @Test + def void testCanUseAlisesWhenImpoting() { + val model1 = ''' + namespace foo.bar + + type A: + id string (1..1) + ''' + + val model2 = ''' + namespace test + + import foo.bar.* as someAlias + + + type B: + a someAlias.A (1..1) + ''' + + #[model1, model2].parseRosettaWithNoIssues + } + @Test def void testValidDefaultSyntax() { "a default 2" From 9e866d3c0fb84e51a896117254ab64af7b6d1e6d Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Thu, 4 Jul 2024 11:35:36 +0100 Subject: [PATCH 14/37] save --- .../scoping/AliasAwareImportNormalizer.java | 67 +++++++++++++++++++ .../scoping/RosettaScopeProvider.xtend | 13 ++-- .../rosetta/tests/RosettaParsingTest.xtend | 2 +- 3 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java new file mode 100644 index 000000000..6f5269156 --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java @@ -0,0 +1,67 @@ +package com.regnosys.rosetta.scoping; + +import java.util.Objects; + +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.scoping.impl.ImportNormalizer; + +public class AliasAwareImportNormalizer extends ImportNormalizer { + private final String namespaceAlias; + + public AliasAwareImportNormalizer(QualifiedName importedNamespace, boolean wildCard, boolean ignoreCase, String namespaceAlias) { + super(importedNamespace, wildCard, ignoreCase); + this.namespaceAlias = namespaceAlias; + } + + @Override + public QualifiedName deresolve(QualifiedName fullyQualifiedName) { + if (namespaceAlias != null) { + return null; //TODO: implement this + } else { + return super.deresolve(fullyQualifiedName); + } + } + + @Override + public QualifiedName resolve(QualifiedName relativeName) { + if (namespaceAlias != null) { + return null; //TODO: implement this + } else { + return super.resolve(relativeName); + } + } + + @Override + public String toString() { + return getImportedNamespacePrefix().toString() + (hasWildCard() ? ".*" : "") + (namespaceAlias != null ? "as " + namespaceAlias : ""); + } + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = super.hashCode(); + result = prime * result + namespaceAlias.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this==obj) + return true; + if (obj==null) + return false; + if (super.equals(obj) == false) { + return false; + } + if (obj instanceof AliasAwareImportNormalizer) { + AliasAwareImportNormalizer other = (AliasAwareImportNormalizer)obj; + return other.namespaceAlias.equals(namespaceAlias); + } + return false; + } + + + +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index 0fee3ab94..e7eab2015 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -279,15 +279,18 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { override protected internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase) { return if (context instanceof RosettaModel) { - val imports = super.internalGetImportedNamespaceResolvers(context, ignoreCase) - imports.add( - doCreateImportNormalizer(getQualifiedNameConverter.toQualifiedName(context.name), true, ignoreCase) - ) - return imports +// val imports = super.internalGetImportedNamespaceResolvers(context, ignoreCase) + return context.imports.map [createImportedNamespaceResolver(importedNamespace, ignoreCase)] } else emptyList } + private def createImportedNamespaceResolver(String namespace, String namespaceAlias, boolean ignoreCase) { + //TODO: create AliasAwareImportNormalizer + } + + + private def IScope defaultScope(EObject object, EReference reference) { filteredScope(super.getScope(object, reference), [it.EClass !== FUNCTION_DISPATCH]) } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend index 6c6a565a6..68d5be194 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend @@ -46,7 +46,7 @@ class RosettaParsingTest { val model2 = ''' namespace test - import foo.bar.* as someAlias + import foo.bar.* as someAlias type B: From 473345cecff9336e86cd2f555ef102c558b3be78 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Thu, 4 Jul 2024 17:19:30 +0200 Subject: [PATCH 15/37] WIP --- .../java/translate/TranslationGenerator.xtend | 31 +++ .../rosetta/model/lib/ModelTranslationId.java | 112 ++++++++++ .../java/translate/TranslateTest.xtend | 194 ++++++++++++++++++ .../java/translate/TranslateTestUtil.java | 36 ++++ 4 files changed, 373 insertions(+) create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/translate/TranslationGenerator.xtend create mode 100644 rosetta-runtime/src/main/java/com/rosetta/model/lib/ModelTranslationId.java create mode 100644 rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend create mode 100644 rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/translate/TranslationGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/translate/TranslationGenerator.xtend new file mode 100644 index 000000000..529974652 --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/translate/TranslationGenerator.xtend @@ -0,0 +1,31 @@ +package com.regnosys.rosetta.generator.java.translate + +import javax.inject.Inject +import org.eclipse.xtext.generator.IFileSystemAccess2 +import com.regnosys.rosetta.generator.java.util.ImportManagerExtension +import com.regnosys.rosetta.generator.java.types.JavaTypeTranslator +import com.regnosys.rosetta.types.RObjectFactory +import com.regnosys.rosetta.generator.java.JavaScope +import com.regnosys.rosetta.generator.java.function.FunctionGenerator +import com.regnosys.rosetta.rosetta.translate.Translation +import com.rosetta.util.types.JavaClass +import com.rosetta.model.lib.functions.RosettaFunction + +class TranslationGenerator { + @Inject extension JavaTypeTranslator + @Inject extension RObjectFactory + @Inject extension ImportManagerExtension + @Inject FunctionGenerator functionGenerator + + + def generate(IFileSystemAccess2 fsa, Translation translation) { + val rFunction = buildRFunction(translation) + val clazz = rFunction.toFunctionJavaClass + val baseInterface = JavaClass.from(RosettaFunction) + val topScope = new JavaScope(clazz.packageName) + val classBody = functionGenerator.rBuildClass(rFunction, false, #[baseInterface], emptyMap, false, topScope) + + val content = buildClass(clazz.packageName, classBody, topScope) + fsa.generateFile(clazz.canonicalName.withForwardSlashes + ".java", content) + } +} \ No newline at end of file diff --git a/rosetta-runtime/src/main/java/com/rosetta/model/lib/ModelTranslationId.java b/rosetta-runtime/src/main/java/com/rosetta/model/lib/ModelTranslationId.java new file mode 100644 index 000000000..3ce91f5ea --- /dev/null +++ b/rosetta-runtime/src/main/java/com/rosetta/model/lib/ModelTranslationId.java @@ -0,0 +1,112 @@ +package com.rosetta.model.lib; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.Validate; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.rosetta.util.DottedPath; + +public class ModelTranslationId extends ModelId implements Comparable { + private static Pattern TRAANSLATION_REPR_PATTERN = Pattern.compile("<(?[a-zA-Z0-9_. ]+) -> (?[a-zA-Z0-9_.]+)>"); + + private final ModelSymbolId translateSource; + private final List inputTypes; + private final ModelSymbolId outputType; + + public ModelTranslationId(ModelSymbolId translateSource, List inputTypes, ModelSymbolId outputType) { + super(translateSource.getNamespace()); + Objects.requireNonNull(translateSource); + Validate.noNullElements(inputTypes); + Objects.requireNonNull(outputType); + + this.translateSource = translateSource; + this.inputTypes = inputTypes; + this.outputType = outputType; + } + + @JsonCreator + public static ModelTranslationId fromNamespaceAndTranslationString(String str) { + DottedPath parts = DottedPath.splitOnDots(str); + DottedPath namespaceAndSource = parts.parent(); + ModelSymbolId source = new ModelSymbolId(namespaceAndSource.parent(), namespaceAndSource.last()); + Matcher matcher = TRAANSLATION_REPR_PATTERN.matcher(parts.last()); + if (matcher.matches()) { + String rawInputList = matcher.group("inputList"); + if (rawInputList != null) { + List inputList = Arrays.stream(rawInputList.split(" ")).map(qn -> ModelSymbolId.fromQualifiedName(qn)).collect(Collectors.toList()); + ModelSymbolId output = ModelSymbolId.fromQualifiedName(matcher.group("output")); + return new ModelTranslationId(source, inputList, output); + } + } + throw new IllegalArgumentException("Invalid format for translation representation: " + parts.last()); + } + + public ModelSymbolId getTranslateSource() { + return translateSource; + } + public List getInputTypes() { + return inputTypes; + } + public ModelSymbolId getOutputType() { + return outputType; + } + + @Override + public String getAlphanumericName() { + return inputTypes.stream().map(t -> t.getAlphanumericName()).collect(Collectors.joining("And")) + "To" + outputType.getAlphanumericName(); + } + + @JsonValue + @Override + public String toString() { + return translateSource.getQualifiedName().child("<" + inputTypes.stream().map(t -> t.toString()).collect(Collectors.joining(" ")) + " -> " + outputType + ">").withDots(); + } + + @Override + public int hashCode() { + return Objects.hash(inputTypes, outputType, translateSource); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ModelTranslationId other = (ModelTranslationId) obj; + return Objects.equals(inputTypes, other.inputTypes) && Objects.equals(outputType, other.outputType) + && Objects.equals(translateSource, other.translateSource); + } + + @Override + public int compareTo(ModelTranslationId o) { + int namespaceComp = getNamespace().compareTo(o.getNamespace()); + if (namespaceComp != 0) { + return namespaceComp; + } + int sourceComp = translateSource.compareTo(o.translateSource); + if (sourceComp != 0) { + return sourceComp; + } + for (int i=0; i classes.createInstanceUsingBuilder("Qux", #{ + "c" -> "My favorite number is " + }) + }) + val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ + "a" -> "My favorite number is 42" + }) + + val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); + assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) + } + + @Test + def void testTranslationWithMultiCardinality() { + val code = ''' + type Foo: + a string (0..*) + + type Bar: + bs Qux (0..*) + + type Qux: + cs string (0..*) + + translate source FooBar { + Foo from Bar: + + a + [from bs, [1, 2]] + + string from Qux, context number: + [from cs join ", " + ": " + context to-string] + } + '''.generateCode + + val classes = code.compileToClasses + + val bar = classes.createInstanceUsingBuilder("Bar", #{ + "bs" -> #[ + classes.createInstanceUsingBuilder("Qux", #{ + "cs" -> #["a", "b"] + }), + classes.createInstanceUsingBuilder("Qux", #{ + "cs" -> #[] + }), + classes.createInstanceUsingBuilder("Qux", #{ + "cs" -> #["This", "is", "ignored"] + }) + ] + }) + val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ + "a" -> #["a, b: 1", ": 2"] + }) + + val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); + assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) + } + + @Test + def void testTranslationWithMetadata() { + val code = ''' + metaType key string + metaType id string + metaType reference string + + type Foo: + [metadata key] + a int (1..1) + [metadata id] + self Foo (1..1) + [metadata reference] + value string (0..*) + [metadata scheme] + + type Bar: + v string (1..1) + t string (1..1) + + translate source FooBar { + Foo from Bar: + [meta key from "self"] + + a + [from 42] + [meta id from "favoriteNumber"] + + self + [meta reference from "self"] + + value + [from ["a", "b", "c"]] + [meta scheme from ["schemeA", "schemeB"]] + } + '''.generateCode + + val classes = code.compileToClasses + + val bar = classes.createInstanceUsingBuilder("Bar", #{ + "bs" -> #[ + classes.createInstanceUsingBuilder("Qux", #{ + "cs" -> #["a", "b"] + }), + classes.createInstanceUsingBuilder("Qux", #{ + "cs" -> #[] + }), + classes.createInstanceUsingBuilder("Qux", #{ + "cs" -> #["This", "is", "ignored"] + }) + ] + }) + val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "key" -> #[Key.builder.setKeyValue("self")] + }), + "a" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaInteger", #{ + "value" -> 42, + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "id" -> "favoriteNumber" + }) + }), + "self" -> classes.createInstanceUsingBuilder("Foo", #{ + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "reference" -> Reference.builder.setReference("self") + }) + }), + "value" -> #[ + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "a", + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "schemeA" + }) + }), + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "b", + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "schemeB" + }) + }), + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "c" + }) + ] + }) + + val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); + assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) + } + +} \ No newline at end of file diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java new file mode 100644 index 000000000..d4cce3c0c --- /dev/null +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java @@ -0,0 +1,36 @@ +package com.regnosys.rosetta.generator.java.translate; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.inject.Inject; + +import com.google.inject.Injector; +import com.regnosys.rosetta.tests.util.ModelHelper; +import com.rosetta.model.lib.ModelSymbolId; +import com.rosetta.model.lib.functions.RosettaFunction; +import com.rosetta.util.types.JavaClass; +import com.rosetta.util.types.generated.GeneratedJavaClassService; + +public class TranslateTestUtil { + private final Injector injector; + private final GeneratedJavaClassService generatedJavaClassService; + private final ModelHelper modelHelper; + + @Inject + public TranslateTestUtil(Injector injector, GeneratedJavaClassService generatedJavaClassService, ModelHelper modelHelper) { + this.injector = injector; + this.generatedJavaClassService = generatedJavaClassService; + this.modelHelper = modelHelper; + } + + public RosettaFunction createTranslation(Map> classes, String sourceName, List inputNames, String outputName) { + JavaClass classRepr = generatedJavaClassService.toJavaTranslationFunction( + new ModelSymbolId(modelHelper.rootPackage(), sourceName), + inputNames.stream().map(n -> new ModelSymbolId(modelHelper.rootPackage(), n)).collect(Collectors.toList()), + new ModelSymbolId(modelHelper.rootPackage(), outputName) + ); + return (RosettaFunction)injector.getInstance(classes.get(classRepr.getCanonicalName().toString())); + } +} From 96432ab9af3a46e867643f4dfe8e376830586d40 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Thu, 4 Jul 2024 17:19:41 +0200 Subject: [PATCH 16/37] WIP --- .../rosetta/types/RObjectFactory.java | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java index 497cbfdf8..1b6bedd00 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java @@ -17,6 +17,7 @@ package com.regnosys.rosetta.types; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -38,19 +39,26 @@ import com.regnosys.rosetta.rosetta.simple.Operation; import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration; import com.regnosys.rosetta.rosetta.simple.SimpleFactory; +import com.regnosys.rosetta.rosetta.translate.TranslateSource; +import com.regnosys.rosetta.rosetta.translate.Translation; +import com.regnosys.rosetta.rosetta.translate.TranslationParameter; +import com.regnosys.rosetta.utils.TranslateUtil; import com.rosetta.model.lib.ModelReportId; import com.rosetta.model.lib.ModelSymbolId; +import com.rosetta.model.lib.ModelTranslationId; import com.rosetta.util.DottedPath; public class RObjectFactory { @Inject - private RosettaTypeProvider rosettaTypeProvider; + private RosettaTypeProvider typeProvider; @Inject private CardinalityProvider cardinalityProvider; @Inject private TypeSystem typeSystem; @Inject private RosettaExtensions rosettaExtensions; + @Inject + private TranslateUtil translateUtil; public RFunction buildRFunction(Function function) { return new RFunction( @@ -67,7 +75,7 @@ public RFunction buildRFunction(Function function) { public RFunction buildRFunction(RosettaRule rule) { RType inputRType = typeSystem.typeCallToRType(rule.getInput()); - RType outputRType = rosettaTypeProvider.getRType(rule.getExpression()); + RType outputRType = typeProvider.getRType(rule.getExpression()); boolean outputIsMulti = cardinalityProvider.isMulti(rule.getExpression()); RAttribute outputAttribute = new RAttribute("output", null, outputRType, List.of(), outputIsMulti); @@ -161,9 +169,34 @@ private ROperation generateOperationForRuleReference(Attribute inputAttribute, R return new ROperation(ROperationType.SET, pathHead, pathTail, symbolRef); } + + public RFunction buildRFunction(Translation translation) { + List inputs = translation.getParameters().stream().map(this::buildRAttribute).collect(Collectors.toList()); + RType outputRType = typeSystem.typeCallToRType(translation.getResultType()); + RAttribute outputAttribute = new RAttribute("output", null, outputRType, List.of(), false); + List operations = translation.getTypeInstructions() + .stream() + .map(instr -> new ROperation(ROperationType.SET, outputAttribute, List.of(), instr.getExpressions().get(0))) // TODO + .collect(Collectors.toList()); + + return new RFunction( + translateUtil.toTranslationId(translation), + null, + inputs, + outputAttribute, + RFunctionOrigin.TRANSLATION, + List.of(), + List.of(), + List.of(), + operations, + List.of() + ); + } + + public List public RAttribute buildRAttribute(Attribute attribute) { - RType rType = this.rosettaTypeProvider.getRTypeOfSymbol(attribute); + RType rType = typeProvider.getRTypeOfSymbol(attribute); List metaAnnotations = attribute.getAnnotations().stream() .filter(a -> a.getAnnotation().getName().equals("metadata")).map(a -> buildRAttribute(a.getAttribute())) .collect(Collectors.toList()); @@ -172,6 +205,16 @@ public RAttribute buildRAttribute(Attribute attribute) { cardinalityProvider.isSymbolMulti(attribute)); } + + public RAttribute buildRAttribute(TranslationParameter parameter) { + RType rType = typeProvider.getRTypeOfSymbol(parameter); + String name = parameter.getName(); + if (name == null) { + name = "item"; + } + return new RAttribute(name, null, rType, Collections.emptyList(), false); + + } public RShortcut buildRShortcut(ShortcutDeclaration shortcut) { return new RShortcut(shortcut.getName(), shortcut.getDefinition(), shortcut.getExpression()); From 34b6cd26a85dcc3f2d3f43f77fdb08bb8afeb13f Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 5 Jul 2024 10:39:07 +0100 Subject: [PATCH 17/37] implement alias aware normalizer --- .../scoping/AliasAwareImportNormalizer.java | 45 +++++---- .../scoping/RosettaScopeProvider.xtend | 93 ++++++++++++------- 2 files changed, 84 insertions(+), 54 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java index 6f5269156..0fd9e03eb 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java @@ -1,31 +1,38 @@ package com.regnosys.rosetta.scoping; -import java.util.Objects; - import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.scoping.impl.ImportNormalizer; public class AliasAwareImportNormalizer extends ImportNormalizer { - private final String namespaceAlias; + private final QualifiedName namespaceAlias; - public AliasAwareImportNormalizer(QualifiedName importedNamespace, boolean wildCard, boolean ignoreCase, String namespaceAlias) { + public AliasAwareImportNormalizer(QualifiedName importedNamespace, String namespaceAlias, boolean wildCard, + boolean ignoreCase) { super(importedNamespace, wildCard, ignoreCase); - this.namespaceAlias = namespaceAlias; + this.namespaceAlias = namespaceAlias != null ? QualifiedName.create(namespaceAlias) : null; } - + @Override - public QualifiedName deresolve(QualifiedName fullyQualifiedName) { + public QualifiedName deresolve(QualifiedName fullyQualifiedName) { if (namespaceAlias != null) { - return null; //TODO: implement this + QualifiedName deresolved = super.deresolve(fullyQualifiedName); + if (deresolved != null) { + return namespaceAlias.append(fullyQualifiedName); + } + return null; } else { return super.deresolve(fullyQualifiedName); - } + } } - + @Override public QualifiedName resolve(QualifiedName relativeName) { - if (namespaceAlias != null) { - return null; //TODO: implement this + if (relativeName.isEmpty()) + return null; + + if (namespaceAlias != null && relativeName.startsWith(namespaceAlias) + && relativeName.getSegmentCount() != namespaceAlias.getSegmentCount()) { + return super.resolve(relativeName.skipFirst(namespaceAlias.getSegmentCount())); } else { return super.resolve(relativeName); } @@ -33,10 +40,10 @@ public QualifiedName resolve(QualifiedName relativeName) { @Override public String toString() { - return getImportedNamespacePrefix().toString() + (hasWildCard() ? ".*" : "") + (namespaceAlias != null ? "as " + namespaceAlias : ""); + return getImportedNamespacePrefix().toString() + (hasWildCard() ? ".*" : "") + + (namespaceAlias != null ? "as " + namespaceAlias : ""); } - @Override public int hashCode() { final int prime = 31; @@ -48,20 +55,18 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (this==obj) + if (this == obj) return true; - if (obj==null) + if (obj == null) return false; if (super.equals(obj) == false) { return false; } if (obj instanceof AliasAwareImportNormalizer) { - AliasAwareImportNormalizer other = (AliasAwareImportNormalizer)obj; + AliasAwareImportNormalizer other = (AliasAwareImportNormalizer) obj; return other.namespaceAlias.equals(namespaceAlias); } return false; } - - - + } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index e7eab2015..44c33a62b 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -6,15 +6,27 @@ package com.regnosys.rosetta.scoping import com.google.common.base.Predicate import com.regnosys.rosetta.RosettaExtensions import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions +import com.regnosys.rosetta.rosetta.ParametrizedRosettaType +import com.regnosys.rosetta.rosetta.RosettaAttributeReference import com.regnosys.rosetta.rosetta.RosettaEnumValueReference import com.regnosys.rosetta.rosetta.RosettaEnumeration import com.regnosys.rosetta.rosetta.RosettaExternalClass import com.regnosys.rosetta.rosetta.RosettaExternalEnum import com.regnosys.rosetta.rosetta.RosettaExternalEnumValue import com.regnosys.rosetta.rosetta.RosettaExternalRegularAttribute -import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall import com.regnosys.rosetta.rosetta.RosettaModel +import com.regnosys.rosetta.rosetta.RosettaTypeAlias +import com.regnosys.rosetta.rosetta.TypeCall +import com.regnosys.rosetta.rosetta.expression.ChoiceOperation +import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair +import com.regnosys.rosetta.rosetta.expression.InlineFunction +import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression +import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall +import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall +import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference +import com.regnosys.rosetta.rosetta.simple.Annotated import com.regnosys.rosetta.rosetta.simple.AnnotationRef +import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.Condition import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.Function @@ -22,48 +34,38 @@ import com.regnosys.rosetta.rosetta.simple.FunctionDispatch import com.regnosys.rosetta.rosetta.simple.Operation import com.regnosys.rosetta.rosetta.simple.Segment import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration +import com.regnosys.rosetta.rosetta.translate.TranslateMetaInstruction +import com.regnosys.rosetta.rosetta.translate.Translation +import com.regnosys.rosetta.rosetta.translate.TranslationRule +import com.regnosys.rosetta.types.RDataType +import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.RosettaTypeProvider -import org.slf4j.Logger -import org.slf4j.LoggerFactory +import com.regnosys.rosetta.types.TypeSystem +import com.regnosys.rosetta.utils.DeepFeatureCallUtil +import com.regnosys.rosetta.utils.RosettaConfigExtension +import java.util.List +import javax.inject.Inject import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.EReference import org.eclipse.xtext.EcoreUtil2 +import org.eclipse.xtext.naming.QualifiedName +import org.eclipse.xtext.resource.EObjectDescription import org.eclipse.xtext.resource.IEObjectDescription +import org.eclipse.xtext.resource.impl.AliasedEObjectDescription import org.eclipse.xtext.scoping.IScope import org.eclipse.xtext.scoping.Scopes import org.eclipse.xtext.scoping.impl.FilteringScope +import org.eclipse.xtext.scoping.impl.ImportNormalizer import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider +import org.eclipse.xtext.scoping.impl.SimpleScope +import org.eclipse.xtext.util.Strings +import org.slf4j.Logger +import org.slf4j.LoggerFactory import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* -import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* +import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import static com.regnosys.rosetta.rosetta.translate.TranslatePackage.Literals.* -import com.regnosys.rosetta.rosetta.expression.InlineFunction -import com.regnosys.rosetta.rosetta.RosettaAttributeReference -import java.util.List -import org.eclipse.xtext.scoping.impl.SimpleScope -import org.eclipse.xtext.resource.EObjectDescription -import org.eclipse.xtext.naming.QualifiedName -import com.regnosys.rosetta.utils.RosettaConfigExtension -import org.eclipse.xtext.resource.impl.AliasedEObjectDescription -import com.regnosys.rosetta.rosetta.simple.Attribute -import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference -import com.regnosys.rosetta.rosetta.expression.ChoiceOperation -import com.regnosys.rosetta.types.RType -import com.regnosys.rosetta.rosetta.RosettaTypeAlias -import com.regnosys.rosetta.rosetta.TypeCall -import com.regnosys.rosetta.rosetta.ParametrizedRosettaType -import javax.inject.Inject -import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression -import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair -import com.regnosys.rosetta.rosetta.translate.Translation -import com.regnosys.rosetta.rosetta.translate.TranslationRule -import com.regnosys.rosetta.types.TypeSystem -import com.regnosys.rosetta.rosetta.translate.TranslateMetaInstruction -import com.regnosys.rosetta.rosetta.simple.Annotated -import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall -import com.regnosys.rosetta.types.RDataType -import com.regnosys.rosetta.utils.DeepFeatureCallUtil /** * This class contains custom scoping description. @@ -279,14 +281,37 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { override protected internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase) { return if (context instanceof RosettaModel) { -// val imports = super.internalGetImportedNamespaceResolvers(context, ignoreCase) - return context.imports.map [createImportedNamespaceResolver(importedNamespace, ignoreCase)] + return context.imports.map [createImportedNamespaceResolver(importedNamespace, namespaceAlias, ignoreCase)] } else emptyList } - private def createImportedNamespaceResolver(String namespace, String namespaceAlias, boolean ignoreCase) { - //TODO: create AliasAwareImportNormalizer + private def ImportNormalizer createImportedNamespaceResolver(String namespace, String namespaceAlias, + boolean ignoreCase) { + if (Strings.isEmpty(namespace)) { + return null; + } + + val importedNamespace = qualifiedNameConverter.toQualifiedName(namespace) + if (importedNamespace === null || importedNamespace.isEmpty()) { + return null; + } + + val hasWildCard = ignoreCase ? + importedNamespace.getLastSegment().equalsIgnoreCase(getWildCard()) : + importedNamespace.getLastSegment().equals(getWildCard()); + + if (hasWildCard) { + if (importedNamespace.getSegmentCount() <= 1) + return null; + return doCreateImportNormalizer(importedNamespace.skipLast(1), namespaceAlias, true, ignoreCase); + } else { + return doCreateImportNormalizer(importedNamespace, namespaceAlias, false, ignoreCase); + } + } + + private def ImportNormalizer doCreateImportNormalizer(QualifiedName importedNamespace, String namespaceAlias, boolean wildcard, boolean ignoreCase) { + return new AliasAwareImportNormalizer(importedNamespace, namespaceAlias, wildcard, ignoreCase); } From 49c8d1705e0d091e7ace2ca8a384d3eedfedbb51 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 5 Jul 2024 11:19:04 +0100 Subject: [PATCH 18/37] fix deresolve bug for import as --- .../regnosys/rosetta/scoping/AliasAwareImportNormalizer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java index 0fd9e03eb..3d298748d 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java @@ -17,7 +17,7 @@ public QualifiedName deresolve(QualifiedName fullyQualifiedName) { if (namespaceAlias != null) { QualifiedName deresolved = super.deresolve(fullyQualifiedName); if (deresolved != null) { - return namespaceAlias.append(fullyQualifiedName); + return namespaceAlias.append(deresolved); } return null; } else { From 7e64ef6d84c5258dae7473dec756db45a7fc99d2 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 5 Jul 2024 13:35:22 +0100 Subject: [PATCH 19/37] remove reduced dep pom --- rosetta-profiling/dependency-reduced-pom.xml | 59 -------------------- 1 file changed, 59 deletions(-) delete mode 100644 rosetta-profiling/dependency-reduced-pom.xml diff --git a/rosetta-profiling/dependency-reduced-pom.xml b/rosetta-profiling/dependency-reduced-pom.xml deleted file mode 100644 index 8db3218f5..000000000 --- a/rosetta-profiling/dependency-reduced-pom.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - com.regnosys.rosetta.parent - com.regnosys.rosetta - 0.0.0.main-SNAPSHOT - - 4.0.0 - com.regnosys.rosetta.profiling - Profiling tools for the Rosetta DSL. - - - - maven-shade-plugin - 3.6.0 - - - package - - shade - - - ${uberjar.name} - - - org.openjdk.jmh.Main - - - - - - *:* - - META-INF/*.SF - META-INF/*.DSA - META-INF/*.RSA - - - - - - - - - - - - org.openjdk.jmh - jmh-generator-annprocess - 1.37 - provided - - - - 1.37 - benchmarks - UTF-8 - - From c8b47f19bb8a6b1f2759c92eeab2795e5447a996 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 5 Jul 2024 13:38:41 +0100 Subject: [PATCH 20/37] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 31ef6ee72..0220ef1b3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ plugin.xml_gen **/*.iml .checkstyle .classpath +rosetta-profiling/dependency-reduced-pom.xml **/.settings **/src-gen/ From 5f8ffd93fbd2a1e27334c1b536d215cb55236686 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Fri, 5 Jul 2024 14:55:47 +0200 Subject: [PATCH 21/37] Implemented code generation excluding meta features --- rosetta-lang/model/RosettaExpression.xcore | 8 + rosetta-lang/model/RosettaTranslate.xcore | 7 +- .../java/com/regnosys/rosetta/Rosetta.xtext | 4 +- .../rosetta/generator/RosettaGenerator.xtend | 10 +- .../java/expression/ExpressionGenerator.xtend | 156 ++++++++++++++---- .../expression/JavaDependencyProvider.xtend | 18 +- .../generator/java/statement/JavaBlock.java | 3 + .../generator/java/statement/JavaForLoop.java | 45 +++++ .../java/statement/JavaStatement.java | 11 ++ .../statement/builder/JavaBlockBuilder.java | 4 +- .../builder/JavaStatementBuilder.java | 31 +++- .../java/types/JavaTypeTranslator.java | 4 + .../interpreter/RosettaInterpreter.java | 9 +- .../scoping/RosettaScopeProvider.xtend | 6 +- .../rosetta/types/CardinalityProvider.xtend | 8 +- .../regnosys/rosetta/types/RAttribute.java | 5 +- .../com/regnosys/rosetta/types/RFunction.java | 18 +- .../rosetta/types/RFunctionOrigin.java | 2 +- .../rosetta/types/RObjectFactory.java | 38 ++++- .../rosetta/types/RosettaTypeProvider.xtend | 5 + .../utils/RosettaExpressionSwitch.java | 5 + .../regnosys/rosetta/utils/TranslateUtil.java | 16 ++ .../RosettaTranslateValidator.xtend | 2 +- .../generated/GeneratedJavaClassService.java | 14 ++ .../java/translate/TranslateTest.xtend | 7 +- .../java/translate/TranslateTestUtil.java | 12 +- 26 files changed, 382 insertions(+), 66 deletions(-) create mode 100644 rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaForLoop.java diff --git a/rosetta-lang/model/RosettaExpression.xcore b/rosetta-lang/model/RosettaExpression.xcore index c84d5fb3c..39c2e2fdd 100644 --- a/rosetta-lang/model/RosettaExpression.xcore +++ b/rosetta-lang/model/RosettaExpression.xcore @@ -14,7 +14,9 @@ import com.regnosys.rosetta.rosetta.RosettaFeature import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs import com.regnosys.rosetta.rosetta.RosettaMapTestExpression import com.regnosys.rosetta.rosetta.RosettaTyped +import com.regnosys.rosetta.rosetta.TypeCall import com.regnosys.rosetta.rosetta.simple.Attribute +import com.regnosys.rosetta.rosetta.translate.TranslateSource import org.eclipse.emf.common.util.BasicEList @@ -170,6 +172,12 @@ class ConstructorKeyValuePair { contains RosettaExpression value } +class TranslateDispatchOperation extends RosettaExpression { + refers TranslateSource source + refers RosettaExpression[] inputs + refers TypeCall outputType +} + interface RosettaOperation extends RosettaExpression { String operator } diff --git a/rosetta-lang/model/RosettaTranslate.xcore b/rosetta-lang/model/RosettaTranslate.xcore index 819a8b135..756a36402 100644 --- a/rosetta-lang/model/RosettaTranslate.xcore +++ b/rosetta-lang/model/RosettaTranslate.xcore @@ -4,7 +4,7 @@ complianceLevel="8.0", bundleManifest="false", modelPluginID="") package com.regnosys.rosetta.rosetta.translate -import com.regnosys.rosetta.rosetta.RosettaFeature +import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.RosettaMetaType import com.regnosys.rosetta.rosetta.RosettaNamed import com.regnosys.rosetta.rosetta.RosettaRootElement @@ -12,6 +12,7 @@ import com.regnosys.rosetta.rosetta.RosettaSymbol import com.regnosys.rosetta.rosetta.RosettaTyped import com.regnosys.rosetta.rosetta.TypeCall import com.regnosys.rosetta.rosetta.expression.RosettaExpression +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation class TranslateSource extends RosettaRootElement, RosettaNamed { refers TranslateSource[] superSources @@ -36,12 +37,14 @@ class TranslationParameter extends RosettaSymbol, RosettaTyped { class TranslationRule { refers Translation translation opposite rules - refers RosettaFeature feature + refers Attribute attribute contains TranslateInstruction[] instructions contains TranslateMetaInstruction[] metaInstructions } abstract class BaseTranslateInstruction { + contains TranslateDispatchOperation _internalDispatchExpression + contains RosettaExpression[] expressions } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext index da7484375..979bd517c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -914,7 +914,7 @@ TranslationParameter: ; TranslationRule: - '+' feature=[RosettaFeature] + '+' attribute=[Attribute|ValidID] (instructions+=TranslateInstruction | metaInstructions+=TranslateMetaInstruction)* ; @@ -927,6 +927,6 @@ TranslateInstruction: ; TranslateMetaInstruction: - '[' 'meta' metaFeature=[RosettaMetaType] BaseTranslateInstruction + '[' 'meta' metaFeature=[RosettaMetaType|ValidID] BaseTranslateInstruction ; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/RosettaGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/RosettaGenerator.xtend index af52eafdc..a293436c9 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/RosettaGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/RosettaGenerator.xtend @@ -31,7 +31,6 @@ import org.eclipse.xtext.generator.IGenerator2 import org.eclipse.xtext.generator.IGeneratorContext import org.slf4j.Logger import org.slf4j.LoggerFactory -import com.regnosys.rosetta.generator.java.reports.RuleGenerator import com.regnosys.rosetta.generator.java.condition.ConditionGenerator import com.regnosys.rosetta.generator.java.reports.ReportGenerator import javax.inject.Inject @@ -42,6 +41,9 @@ import com.regnosys.rosetta.config.RosettaGeneratorsConfiguration import com.regnosys.rosetta.generator.java.expression.DeepPathUtilGenerator import com.regnosys.rosetta.utils.DeepFeatureCallUtil import com.regnosys.rosetta.types.RDataType +import com.regnosys.rosetta.generator.java.reports.RuleGenerator +import com.regnosys.rosetta.generator.java.translate.TranslationGenerator +import com.regnosys.rosetta.rosetta.translate.TranslateSource /** * Generates code from your model files on save. @@ -59,6 +61,7 @@ class RosettaGenerator implements IGenerator2 { @Inject ExternalGenerators externalGenerators @Inject JavaPackageInfoGenerator javaPackageInfoGenerator @Inject RuleGenerator ruleGenerator + @Inject TranslationGenerator translationGenerator @Inject ModelObjectGenerator dataGenerator @Inject ValidatorsGenerator validatorsGenerator @@ -194,6 +197,11 @@ class RosettaGenerator implements IGenerator2 { tabulatorGenerator.generate(fsa, externalClass.data, Optional.of(it)) ] } + TranslateSource: { + it.translations.forEach [ + translationGenerator.generate(fsa, it) + ] + } } ] enumGenerator.generate(packages, fsa, model.elements, version) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend index fce3b981b..ea0287890 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/ExpressionGenerator.xtend @@ -128,7 +128,15 @@ import java.time.ZonedDateTime import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall import com.regnosys.rosetta.rosetta.expression.DefaultOperation import com.regnosys.rosetta.generator.java.statement.builder.JavaConditionalExpression -import com.regnosys.rosetta.rosetta.RosettaSymbol +import com.regnosys.rosetta.rosetta.translate.TranslationParameter +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation +import com.regnosys.rosetta.utils.TranslateUtil +import java.util.ArrayList +import com.fasterxml.jackson.core.type.TypeReference +import com.regnosys.rosetta.generator.java.statement.JavaLocalVariableDeclarationStatement +import com.regnosys.rosetta.generator.java.statement.JavaForLoop +import com.regnosys.rosetta.generator.java.statement.JavaBlock +import com.regnosys.rosetta.generator.java.statement.JavaStatementList class ExpressionGenerator extends RosettaExpressionSwitch { @@ -152,6 +160,7 @@ class ExpressionGenerator extends RosettaExpressionSwitchof(«it»)''', - MAPPER_C.wrap(itemType) - ) - ] + JavaStatementBuilder.invokeMethod( + elements, + [JavaExpression.from('''«MapperC».<«itemType»>of(«it»)''', MAPPER_C.wrap(itemType))], + context.scope + ) } override protected caseMapOperation(MapOperation expr, Context context) { @@ -1008,6 +1002,10 @@ class ExpressionGenerator extends RosettaExpressionSwitch input0List = ...; + // final List input1List = ...; + // ... + // final List outputList = new ArrayList<>(); + // for (int i = 0; i < input0List.size() && i < input1List.size() && ...; i++) + // outputList.add(dispatchFunc.evaluate(input0List.get(i), input1List.get(i), ...)); + // } + // outputList + + var argsListDeclarations = new JavaBlock(new JavaStatementList()); + val forScope = context.scope.childScope("for-loop") + val indexId = forScope.createUniqueIdentifier("i") + var JavaExpression forConditions = null + val itemArgs = newArrayList + for (var i = 0; i < expr.inputs.size; i++) { + // Declare inputs as variables of the form: + // List inputList0 = ; + // List inputList1 = ; + // ... + val itemType = rCallable.inputs.get(i).attributeToJavaType + val argsExpr = expr.inputs.get(i).javaCode(LIST.wrapExtendsIfNotFinal(itemType), context.scope) + val argListVar = context.scope.createUniqueIdentifier(itemType.simpleName.toFirstLower + "List") + argsListDeclarations = argsListDeclarations.append( + argsExpr.complete[ + new JavaLocalVariableDeclarationStatement(true, LIST.wrapExtendsIfNotFinal(itemType), argListVar, it) + ] + ) + // Create a condition of the form: + // i < inputList0.size() && i < inputList1.size() && ... + val cond = JavaExpression.from('''«indexId» < «argListVar».size()''', JavaPrimitiveType.BOOLEAN) + if (forConditions === null) { + forConditions = cond + } else { + val prev = forConditions + forConditions = JavaExpression.from('''«prev» && «cond»''', JavaPrimitiveType.BOOLEAN) + } + // Items of the inputs can be accessed using `inputList0.get(i)`, `inputList1.get(i)`, etc. + itemArgs.add(JavaExpression.from('''«argListVar».get(«indexId»)''', itemType)) + } + + val accId = context.scope.createUniqueIdentifier(javaOutputType.simpleName.toFirstLower + "List") + val accType = LIST.wrap(javaOutputType) + argsListDeclarations + .append( + new JavaLocalVariableDeclarationStatement( + true, + accType, + accId, + JavaExpression.from('''new «ArrayList»<>()''',JavaGenericTypeDeclaration.from(new TypeReference>() {}).wrap(javaOutputType)) + ) + ).append( + new JavaForLoop( + new JavaLocalVariableDeclarationStatement(false, JavaPrimitiveType.INT, indexId, JavaExpression.from('''0''', JavaPrimitiveType.INT)), + forConditions, + JavaExpression.from('''«indexId»++''', JavaPrimitiveType.INT), + JavaStatementBuilder.invokeMethod( + itemArgs, + [JavaExpression.from('''«accId».add(«context.scope.getIdentifierOrThrow(rCallable.toFunctionJavaClass.toDependencyInstance)».evaluate(«it»))''', JavaPrimitiveType.BOOLEAN)], + context.scope + ).completeAsExpressionStatement + ) + ).append( + new JavaVariable(accId, accType) + ) + } else { + val args = newArrayList + for (var i = 0; i < expr.inputs.size; i++) { + args.add(expr.inputs.get(i).javaCode(rCallable.inputs.get(i).attributeToJavaType, context.scope)) + } + JavaStatementBuilder.invokeMethod( + args, + [JavaExpression.from('''«context.scope.getIdentifierOrThrow(rCallable.toFunctionJavaClass.toDependencyInstance)».evaluate(«it»)''', javaOutputType)], + context.scope + ) + } + } + } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/JavaDependencyProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/JavaDependencyProvider.xtend index 3c752ad2c..3c6cd087e 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/JavaDependencyProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/JavaDependencyProvider.xtend @@ -14,6 +14,9 @@ import com.regnosys.rosetta.types.RosettaTypeProvider import com.regnosys.rosetta.generator.java.types.JavaTypeTranslator import com.regnosys.rosetta.types.RDataType import java.util.List +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation +import com.regnosys.rosetta.utils.TranslateUtil +import com.regnosys.rosetta.types.TypeSystem /** * A class that helps determine which RosettaFunctions a Rosetta object refers to @@ -21,15 +24,28 @@ import java.util.List class JavaDependencyProvider { @Inject RObjectFactory rTypeBuilderFactory @Inject RosettaTypeProvider typeProvider + @Inject TypeSystem typeSystem @Inject extension JavaTypeTranslator + @Inject TranslateUtil translateUtil def List> javaDependencies(RosettaExpression expression) { val rosettaSymbols = EcoreUtil2.eAllOfType(expression, RosettaSymbolReference).map[it.symbol] val deepFeatureCalls = EcoreUtil2.eAllOfType(expression, RosettaDeepFeatureCall) + val translateDispatchOperations = EcoreUtil2.eAllOfType(expression, TranslateDispatchOperation) + val actualDispatches = newArrayList + for (op : translateDispatchOperations) { + val inputTypes = op.inputs.map[typeProvider.getRType(it)] + val outputType = typeSystem.typeCallToRType(op.outputType) + if (inputTypes.size !== 1 || !typeSystem.isSubtypeOf(inputTypes.head, outputType)) { + val match = translateUtil.findMatches(op.source, outputType, inputTypes).last + actualDispatches.add(rTypeBuilderFactory.buildRFunction(match)) + } + } ( rosettaSymbols.filter(Function).map[rTypeBuilderFactory.buildRFunction(it).toFunctionJavaClass] + rosettaSymbols.filter(RosettaRule).map[rTypeBuilderFactory.buildRFunction(it).toFunctionJavaClass] + - deepFeatureCalls.map[typeProvider.getRType(receiver)].filter(RDataType).map[data.toDeepPathUtilJavaClass] + deepFeatureCalls.map[typeProvider.getRType(receiver)].filter(RDataType).map[data.toDeepPathUtilJavaClass] + + actualDispatches.map[toFunctionJavaClass] ).toSet.sortBy[it.simpleName] } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaBlock.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaBlock.java index 909de11bf..e0076d387 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaBlock.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaBlock.java @@ -18,6 +18,9 @@ import org.eclipse.xtend2.lib.StringConcatenationClient.TargetStringConcatenation; +import com.regnosys.rosetta.generator.java.statement.builder.JavaBlockBuilder; +import com.regnosys.rosetta.generator.java.statement.builder.JavaStatementBuilder; + /** * Based on the Java specification: https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-Block * diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaForLoop.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaForLoop.java new file mode 100644 index 000000000..117d6fc0a --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaForLoop.java @@ -0,0 +1,45 @@ +package com.regnosys.rosetta.generator.java.statement; + +import org.eclipse.xtend2.lib.StringConcatenationClient.TargetStringConcatenation; + +import com.regnosys.rosetta.generator.java.statement.builder.JavaExpression; + +/** + * Based on the Java specification: https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-BasicForStatement + * + * Example: + * ``` + * for (int i=0; i<42; i++) { + * list.add(i); + * } + * ``` + * + * See `JavaStatementBuilder` for more documentation. + */ +public class JavaForLoop extends JavaStatement { + private final JavaLocalVariableDeclarationStatement forInit; + private final JavaExpression forCondition; + private final JavaExpression forUpdate; + private final JavaStatement forBody; + + public JavaForLoop(JavaLocalVariableDeclarationStatement forInit, JavaExpression forCondition, JavaExpression forUpdate, JavaStatement forBody) { + this.forInit = forInit; + this.forCondition = forCondition; + this.forUpdate = forUpdate; + this.forBody = forBody; + } + + @Override + public void appendTo(TargetStringConcatenation target) { + target.append("for ("); + target.append(forInit); + target.append(" "); + target.append(forCondition); + target.append("; "); + target.append(forUpdate); + target.append(") "); + // Calling `toBlock()` will make sure that the body is always enclosed in curly braces. + // This is a style preference, and is technically not necessary. + target.append(forBody.toBlock()); + } +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaStatement.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaStatement.java index bd914e2e9..3422fd47b 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaStatement.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/JavaStatement.java @@ -17,6 +17,8 @@ package com.regnosys.rosetta.generator.java.statement; import com.regnosys.rosetta.generator.TargetLanguageRepresentation; +import com.regnosys.rosetta.generator.java.statement.builder.JavaBlockBuilder; +import com.regnosys.rosetta.generator.java.statement.builder.JavaStatementBuilder; /** * A representation of a statement in Java. Examples: @@ -108,4 +110,13 @@ public JavaBlock append(JavaStatement other) { } return new JavaBlock(JavaStatementList.of(this, other)); } + /** + * Append the given statement builder to this statement. Behaves the same as `builder.prepend(this)`. + * + * This operation flattens block statements. See #append(JavaStatement) for examples. + * ``` + */ + public JavaBlockBuilder append(JavaStatementBuilder builder) { + return new JavaBlockBuilder(this.asStatementList(), builder); + } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaBlockBuilder.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaBlockBuilder.java index 0fb121787..890ff662c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaBlockBuilder.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaBlockBuilder.java @@ -59,12 +59,12 @@ public JavaType getExpressionType() { } @Override - public JavaStatementBuilder mapExpression(Function mapper) { + public JavaBlockBuilder mapExpression(Function mapper) { return new JavaBlockBuilder(statements, lastStatement.mapExpression(mapper)); } @Override - public JavaStatementBuilder then(JavaStatementBuilder after, BiFunction combineExpressions, JavaScope scope) { + public JavaBlockBuilder then(JavaStatementBuilder after, BiFunction combineExpressions, JavaScope scope) { if (after instanceof JavaBlockBuilder) { return this.then((JavaBlockBuilder)after, combineExpressions, scope); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaStatementBuilder.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaStatementBuilder.java index 3a4e62c20..8f2499a93 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaStatementBuilder.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaStatementBuilder.java @@ -16,13 +16,18 @@ package com.regnosys.rosetta.generator.java.statement.builder; +import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; +import org.eclipse.xtend2.lib.StringConcatenationClient.TargetStringConcatenation; + import com.regnosys.rosetta.generator.GeneratedIdentifier; import com.regnosys.rosetta.generator.java.JavaScope; +import com.regnosys.rosetta.generator.java.statement.JavaBlock; import com.regnosys.rosetta.generator.java.statement.JavaLambdaBody; import com.regnosys.rosetta.generator.java.statement.JavaStatement; +import com.regnosys.rosetta.generator.java.statement.JavaStatementList; import com.rosetta.util.types.JavaType; /** @@ -52,7 +57,7 @@ * ``` * * *Example 2* - * Say we want to build the following block: + * Say we want to build the following block (we don't really care about the name of the variable): * ``` * { * int ifThenElseResult; @@ -85,6 +90,30 @@ * ``` */ public abstract class JavaStatementBuilder { + + public static JavaStatementBuilder invokeMethod(List arguments, Function methodInvoker, JavaScope scope) { + if (arguments.isEmpty()) { + return methodInvoker.apply(null); + } + JavaStatementBuilder argCode = arguments.get(0); + for (var i = 1; i < arguments.size(); i++) { + argCode = argCode.then( + arguments.get(i), + (argList, newArg) -> new JavaExpression(null) { + @Override + public void appendTo(TargetStringConcatenation target) { + target.append(argList); + target.append(", "); + target.append(newArg); + } + }, + scope + ); + } + return argCode.collapseToSingleExpression(scope).mapExpression(methodInvoker); + } + + /** * Get the type of the last expression of this builder, * or the least common supertype of all expressions in different branches of this builder. diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java index 53107b4f0..3cc0e6cfa 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java @@ -66,6 +66,7 @@ import com.regnosys.rosetta.utils.RosettaTypeSwitch; import com.rosetta.model.lib.ModelReportId; import com.rosetta.model.lib.ModelSymbolId; +import com.rosetta.model.lib.ModelTranslationId; import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.model.lib.reports.ReportFunction; import com.rosetta.model.lib.reports.Tabulator; @@ -118,6 +119,9 @@ public JavaClass toFunctionJavaClass(RFunction func) return generatedJavaClassService.toJavaReportFunction(func.getReportId()); case RULE: return generatedJavaClassService.toJavaRule(func.getSymbolId()); + case TRANSLATION: + ModelTranslationId id = func.getTranslationId(); + return generatedJavaClassService.toJavaTranslationFunction(id.getTranslateSource(), id.getInputTypes(), id.getOutputType()); default: throw new IllegalStateException("Unknown origin of RFunction: " + func.getOrigin()); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaInterpreter.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaInterpreter.java index 2f42d61dc..b3d918a37 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaInterpreter.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/interpreter/RosettaInterpreter.java @@ -81,6 +81,7 @@ import com.regnosys.rosetta.rosetta.expression.ToStringOperation; import com.regnosys.rosetta.rosetta.expression.ToTimeOperation; import com.regnosys.rosetta.rosetta.expression.ToZonedDateTimeOperation; +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation; import com.regnosys.rosetta.types.RosettaTypeProvider; import com.regnosys.rosetta.types.TypeSystem; import com.regnosys.rosetta.types.RType; @@ -580,7 +581,13 @@ protected RosettaValue caseToZonedDateTimeOperation(ToZonedDateTimeOperation exp @Override protected RosettaValue caseConstructorExpression(RosettaConstructorExpression expr, RosettaInterpreterContext context) { - // TODO Auto-generated method stub + // TODO throw new RosettaInterpreterException("Constructor expressions are not supported yet."); } + @Override + protected RosettaValue caseTranslateDispatchOperation(TranslateDispatchOperation expr, + RosettaInterpreterContext context) { + // TODO + throw new RosettaInterpreterException("Translate dispatch operations are not supported yet."); + } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index 0fee3ab94..1bc9ed013 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -238,10 +238,10 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { case ROSETTA_EXTERNAL_RULE_SOURCE__SUPER_SOURCES: { return defaultScope(context, reference).filteredScope[it.EClass == ROSETTA_EXTERNAL_RULE_SOURCE] } - case TRANSLATION_RULE__FEATURE: { + case TRANSLATION_RULE__ATTRIBUTE: { if (context instanceof TranslationRule) { val translation = context.translation - return createExtendedFeatureScope(translation, typeSystem.typeCallToRType(translation.resultType)) + return Scopes.scopeFor(typeSystem.typeCallToRType(translation.resultType).allFeatures(context)) } return IScope.NULLSCOPE } @@ -249,7 +249,7 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { if (context instanceof TranslateMetaInstruction) { val container = context.eContainer val annotated = if (container instanceof TranslationRule) { - container.feature + container.attribute } else if (container instanceof Translation) { container.resultType.type } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend index bd9fa0cf3..2447fb64f 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/CardinalityProvider.xtend @@ -71,6 +71,7 @@ import com.regnosys.rosetta.rosetta.expression.ToZonedDateTimeOperation import com.regnosys.rosetta.rosetta.translate.TranslationParameter import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall import com.regnosys.rosetta.rosetta.expression.DefaultOperation +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation class CardinalityProvider extends RosettaExpressionSwitch { static Logger LOGGER = LoggerFactory.getLogger(CardinalityProvider) @@ -537,5 +538,10 @@ class CardinalityProvider extends RosettaExpressionSwitch { override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Boolean breakOnClosureParameter) { false - } + } + + override protected caseTranslateDispatchOperation(TranslateDispatchOperation expr, Boolean context) { + expr.inputs.head.isMulti(context) + } + } \ No newline at end of file diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java index 6668b8a34..9975cb9f4 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java @@ -75,6 +75,9 @@ public boolean equals(Object obj) { && Objects.equals(rType, other.rType); } - + @Override + public String toString() { + return String.format("RAttribute[name=%s, type=%s, isMulti=%s]", name, rType, isMulti); + } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunction.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunction.java index 9ef4e373d..2bce1bcfa 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunction.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunction.java @@ -23,11 +23,13 @@ import com.rosetta.model.lib.ModelId; import com.rosetta.model.lib.ModelReportId; import com.rosetta.model.lib.ModelSymbolId; +import com.rosetta.model.lib.ModelTranslationId; import com.rosetta.util.DottedPath; public class RFunction { private ModelSymbolId symbolId; private ModelReportId reportId; + private ModelTranslationId translationId; private String definition; private List inputs; private RAttribute output; @@ -66,12 +68,22 @@ public RFunction(ModelReportId reportId, String definition, List inp shortcuts, operations, annotations); this.reportId = reportId; } + public RFunction(ModelTranslationId translationId, String definition, List inputs, + RAttribute output, RFunctionOrigin origin, List preConditions, List postConditions, + List shortcuts, List operations, List annotations) { + this(definition, inputs, output, origin, preConditions, postConditions, + shortcuts, operations, annotations); + this.translationId = translationId; + } public ModelId getId() { if (symbolId != null) { return symbolId; } - return reportId; + if (reportId != null) { + return reportId; + } + return translationId; } public ModelSymbolId getSymbolId() { @@ -82,6 +94,10 @@ public ModelReportId getReportId() { return reportId; } + public ModelTranslationId getTranslationId() { + return translationId; + } + public DottedPath getNamespace() { return getId().getNamespace(); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunctionOrigin.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunctionOrigin.java index c0ab57baf..a8f7e4c02 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunctionOrigin.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFunctionOrigin.java @@ -17,5 +17,5 @@ package com.regnosys.rosetta.types; public enum RFunctionOrigin { - FUNCTION,RULE,REPORT + FUNCTION,RULE,REPORT,TRANSLATION } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java index 1b6bedd00..72be6b345 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java @@ -26,26 +26,29 @@ import org.eclipse.xtext.EcoreUtil2; +import com.google.common.collect.Streams; import com.regnosys.rosetta.RosettaExtensions; import com.regnosys.rosetta.rosetta.RosettaCardinality; import com.regnosys.rosetta.rosetta.RosettaFactory; import com.regnosys.rosetta.rosetta.RosettaReport; import com.regnosys.rosetta.rosetta.RosettaRule; +import com.regnosys.rosetta.rosetta.TypeCall; import com.regnosys.rosetta.rosetta.expression.ExpressionFactory; import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference; +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation; import com.regnosys.rosetta.rosetta.simple.Attribute; import com.regnosys.rosetta.rosetta.simple.Data; import com.regnosys.rosetta.rosetta.simple.Function; import com.regnosys.rosetta.rosetta.simple.Operation; import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration; import com.regnosys.rosetta.rosetta.simple.SimpleFactory; +import com.regnosys.rosetta.rosetta.translate.TranslateInstruction; import com.regnosys.rosetta.rosetta.translate.TranslateSource; import com.regnosys.rosetta.rosetta.translate.Translation; import com.regnosys.rosetta.rosetta.translate.TranslationParameter; import com.regnosys.rosetta.utils.TranslateUtil; import com.rosetta.model.lib.ModelReportId; import com.rosetta.model.lib.ModelSymbolId; -import com.rosetta.model.lib.ModelTranslationId; import com.rosetta.util.DottedPath; public class RObjectFactory { @@ -163,6 +166,7 @@ private ROperation generateOperationForRuleReference(Attribute inputAttribute, R inputAttributeSymbolRef.setSymbol(inputAttribute); RosettaSymbolReference symbolRef = ExpressionFactory.eINSTANCE.createRosettaSymbolReference(); + symbolRef.setGenerated(true); symbolRef.setSymbol(rule); symbolRef.setExplicitArguments(true); symbolRef.getArgs().add(inputAttributeSymbolRef); @@ -171,13 +175,25 @@ private ROperation generateOperationForRuleReference(Attribute inputAttribute, R } public RFunction buildRFunction(Translation translation) { - List inputs = translation.getParameters().stream().map(this::buildRAttribute).collect(Collectors.toList()); + List inputs = translation.getParameters().stream().map(this::buildRAttribute).collect(Collectors.toList()); RType outputRType = typeSystem.typeCallToRType(translation.getResultType()); RAttribute outputAttribute = new RAttribute("output", null, outputRType, List.of(), false); - List operations = translation.getTypeInstructions() - .stream() - .map(instr -> new ROperation(ROperationType.SET, outputAttribute, List.of(), instr.getExpressions().get(0))) // TODO - .collect(Collectors.toList()); + List operations = + Streams.concat( + // type level instructions + translation.getTypeInstructions() + .stream() + .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), translation.getResultType(), outputAttribute, List.of(), false)), + // attribute level instructions + translation.getRules() + .stream() + .flatMap(rule -> { + RAttribute attr = buildRAttribute(rule.getAttribute()); + return rule.getInstructions() + .stream() + .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), rule.getAttribute().getTypeCall(), outputAttribute, List.of(attr), attr.isMulti())); + }) + ).collect(Collectors.toList()); return new RFunction( translateUtil.toTranslationId(translation), @@ -193,7 +209,15 @@ public RFunction buildRFunction(Translation translation) { ); } - public List + public ROperation generateOperationForTranslateInstruction(TranslateInstruction instr, TranslateSource source, TypeCall outputType, RAttribute outputAttribute, List assignPath, boolean isMulti) { + TranslateDispatchOperation op = ExpressionFactory.eINSTANCE.createTranslateDispatchOperation(); + op.setGenerated(true); + op.setOutputType(outputType); + op.setSource(source); + op.getInputs().addAll(instr.getExpressions()); + instr.set_internalDispatchExpression(op); + return new ROperation(isMulti ? ROperationType.ADD : ROperationType.SET, outputAttribute, assignPath, op); + } public RAttribute buildRAttribute(Attribute attribute) { RType rType = typeProvider.getRTypeOfSymbol(attribute); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend index 038449554..291c9afa3 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RosettaTypeProvider.xtend @@ -79,6 +79,7 @@ import com.regnosys.rosetta.rosetta.expression.ToZonedDateTimeOperation import com.regnosys.rosetta.rosetta.translate.TranslationParameter import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall import com.regnosys.rosetta.rosetta.expression.DefaultOperation +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation class RosettaTypeProvider extends RosettaExpressionSwitch> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" @@ -519,4 +520,8 @@ class RosettaTypeProvider extends RosettaExpressionSwitch context) { + expr.outputType.typeCallToRType + } + } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaExpressionSwitch.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaExpressionSwitch.java index b2b7e0e18..5201a8503 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaExpressionSwitch.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaExpressionSwitch.java @@ -72,6 +72,7 @@ import com.regnosys.rosetta.rosetta.expression.ToStringOperation; import com.regnosys.rosetta.rosetta.expression.ToTimeOperation; import com.regnosys.rosetta.rosetta.expression.ToZonedDateTimeOperation; +import com.regnosys.rosetta.rosetta.expression.TranslateDispatchOperation; public abstract class RosettaExpressionSwitch { @@ -94,6 +95,8 @@ protected Return doSwitch(RosettaExpression expr, Context context) { return doSwitch((RosettaReference)expr, context); } else if (expr instanceof RosettaOperation) { return doSwitch((RosettaOperation)expr, context); + } else if (expr instanceof TranslateDispatchOperation) { + return caseTranslateDispatchOperation((TranslateDispatchOperation)expr, context); } throw errorMissedCase(expr); } @@ -281,6 +284,8 @@ private UnsupportedOperationException errorMissedCase(RosettaExpression expr) { protected abstract Return caseImplicitVariable(RosettaImplicitVariable expr, Context context); protected abstract Return caseSymbolReference(RosettaSymbolReference expr, Context context); + protected abstract Return caseTranslateDispatchOperation(TranslateDispatchOperation expr, Context context); + protected abstract Return caseAddOperation(ArithmeticOperation expr, Context context); protected abstract Return caseSubtractOperation(ArithmeticOperation expr, Context context); protected abstract Return caseMultiplyOperation(ArithmeticOperation expr, Context context); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java index 520250239..5639c73ee 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/TranslateUtil.java @@ -7,11 +7,15 @@ import javax.inject.Inject; import com.google.common.collect.Streams; +import com.regnosys.rosetta.rosetta.RosettaType; import com.regnosys.rosetta.rosetta.translate.TranslateSource; import com.regnosys.rosetta.rosetta.translate.Translation; import com.regnosys.rosetta.types.RType; import com.regnosys.rosetta.types.RosettaTypeProvider; import com.regnosys.rosetta.types.TypeSystem; +import com.rosetta.model.lib.ModelSymbolId; +import com.rosetta.model.lib.ModelTranslationId; +import com.rosetta.util.DottedPath; public class TranslateUtil { private final TypeSystem typeSystem; @@ -62,4 +66,16 @@ public boolean matches(Translation translation, RType resultType, List in return true; } + + public ModelTranslationId toTranslationId(Translation translation) { + TranslateSource source = translation.getSource(); + return new ModelTranslationId( + new ModelSymbolId(DottedPath.splitOnDots(source.getModel().getName()), source.getName()), + translation.getParameters().stream().map(p -> toModelSymbolId(p.getTypeCall().getType())).collect(Collectors.toList()), + toModelSymbolId(translation.getResultType().getType()) + ); + } + private ModelSymbolId toModelSymbolId(RosettaType t) { + return new ModelSymbolId(DottedPath.splitOnDots(t.getModel().getName()), t.getName()); + } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend index 4e8df0623..20204c8d4 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend @@ -97,7 +97,7 @@ class RosettaTranslateValidator extends AbstractDeclarativeRosettaValidator { def void checkTranslateInstruction(TranslateInstruction instruction) { val container = instruction.eContainer if (container instanceof TranslationRule) { - translateToFeatureCheck(container.feature, instruction.expressions, instruction) + translateToFeatureCheck(container.attribute, instruction.expressions, instruction) } else if (container instanceof Translation) { translateToTypeCheck(container.resultType, instruction.expressions, instruction) } diff --git a/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java b/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java index a806c9819..f1445b709 100644 --- a/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java +++ b/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java @@ -16,9 +16,14 @@ package com.rosetta.util.types.generated; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + import com.fasterxml.jackson.core.type.TypeReference; import com.rosetta.model.lib.ModelReportId; import com.rosetta.model.lib.ModelSymbolId; +import com.rosetta.model.lib.ModelTranslationId; import com.rosetta.model.lib.RosettaModelObject; import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.model.lib.reports.ReportFunction; @@ -56,6 +61,15 @@ public JavaClass toJavaFunction(ModelSymbolId id) { return new GeneratedJavaClass<>(packageName, simpleName, new TypeReference>() {}); } + public JavaClass toJavaTranslationFunction(ModelTranslationId translationId) { + DottedPath packageName = translationId.getNamespace().child("translate"); + String inputNames = translationId.getInputTypes().stream().map(id -> StringUtils.capitalize(id.getName())).collect(Collectors.joining("And")); + String outputName = StringUtils.capitalize(translationId.getOutputType().getName()); + String sourceName = StringUtils.capitalize(translationId.getTranslateSource().getName()); + String simpleName = String.format("Translate%sTo%sUsing%s", inputNames, outputName, sourceName); + return new GeneratedJavaClass<>(packageName, simpleName, RosettaFunction.class); + } + public JavaClass toJavaType(ModelSymbolId id) { DottedPath packageName = id.getNamespace(); String simpleName = id.getName(); diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index c6aea84f5..90ba3ad30 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -48,11 +48,11 @@ class TranslateTest { val bar = classes.createInstanceUsingBuilder("Bar", #{ "b" -> classes.createInstanceUsingBuilder("Qux", #{ - "c" -> "My favorite number is " + "c" -> "My favourite number is " }) }) val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ - "a" -> "My favorite number is 42" + "a" -> "My favourite number is 42" }) val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); @@ -75,6 +75,7 @@ class TranslateTest { Foo from Bar: + a [from bs, [1, 2]] + [from "Another string"] string from Qux, context number: [from cs join ", " + ": " + context to-string] @@ -97,7 +98,7 @@ class TranslateTest { ] }) val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ - "a" -> #["a, b: 1", ": 2"] + "a" -> #["a, b: 1", ": 2", "Another string"] }) val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java index d4cce3c0c..6d71b222a 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTestUtil.java @@ -9,6 +9,7 @@ import com.google.inject.Injector; import com.regnosys.rosetta.tests.util.ModelHelper; import com.rosetta.model.lib.ModelSymbolId; +import com.rosetta.model.lib.ModelTranslationId; import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.util.types.JavaClass; import com.rosetta.util.types.generated.GeneratedJavaClassService; @@ -26,11 +27,12 @@ public TranslateTestUtil(Injector injector, GeneratedJavaClassService generatedJ } public RosettaFunction createTranslation(Map> classes, String sourceName, List inputNames, String outputName) { - JavaClass classRepr = generatedJavaClassService.toJavaTranslationFunction( - new ModelSymbolId(modelHelper.rootPackage(), sourceName), - inputNames.stream().map(n -> new ModelSymbolId(modelHelper.rootPackage(), n)).collect(Collectors.toList()), - new ModelSymbolId(modelHelper.rootPackage(), outputName) - ); + ModelTranslationId id = new ModelTranslationId( + new ModelSymbolId(modelHelper.rootPackage(), sourceName), + inputNames.stream().map(n -> new ModelSymbolId(modelHelper.rootPackage(), n)).collect(Collectors.toList()), + new ModelSymbolId(modelHelper.rootPackage(), outputName) + ); + JavaClass classRepr = generatedJavaClassService.toJavaTranslationFunction(id); return (RosettaFunction)injector.getInstance(classes.get(classRepr.getCanonicalName().toString())); } } From 47e95c3c8972c2ce1daf3e7143bf4ada62e37b09 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 5 Jul 2024 14:39:46 +0100 Subject: [PATCH 22/37] use standard import normalizer when alias not present --- .../regnosys/rosetta/scoping/AliasAwareImportNormalizer.java | 5 +++-- .../com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java index 3d298748d..7188a536d 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/AliasAwareImportNormalizer.java @@ -5,6 +5,7 @@ public class AliasAwareImportNormalizer extends ImportNormalizer { private final QualifiedName namespaceAlias; + private static final String NULL_ALIAS_ERROR = AliasAwareImportNormalizer.class.getSimpleName() + " does not support null namespaceAlias values"; public AliasAwareImportNormalizer(QualifiedName importedNamespace, String namespaceAlias, boolean wildCard, boolean ignoreCase) { @@ -21,7 +22,7 @@ public QualifiedName deresolve(QualifiedName fullyQualifiedName) { } return null; } else { - return super.deresolve(fullyQualifiedName); + throw new IllegalStateException(NULL_ALIAS_ERROR); } } @@ -34,7 +35,7 @@ public QualifiedName resolve(QualifiedName relativeName) { && relativeName.getSegmentCount() != namespaceAlias.getSegmentCount()) { return super.resolve(relativeName.skipFirst(namespaceAlias.getSegmentCount())); } else { - return super.resolve(relativeName); + throw new IllegalStateException(NULL_ALIAS_ERROR); } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index 44c33a62b..7e7932322 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -311,6 +311,9 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { } private def ImportNormalizer doCreateImportNormalizer(QualifiedName importedNamespace, String namespaceAlias, boolean wildcard, boolean ignoreCase) { + if (namespaceAlias === null) { + return doCreateImportNormalizer(importedNamespace, wildcard, ignoreCase); + } return new AliasAwareImportNormalizer(importedNamespace, namespaceAlias, wildcard, ignoreCase); } From 2b225b03e5fde4dee6323ecc85d6596127b266b4 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Fri, 5 Jul 2024 18:19:29 +0200 Subject: [PATCH 23/37] Corrections --- .../java/types/JavaTypeTranslator.java | 4 +--- .../validation/RosettaSimpleValidator.xtend | 1 - .../RosettaTranslateValidator.xtend | 15 ++++++++----- .../tests/util/CodeGeneratorTestHelper.xtend | 15 +++++++++---- .../java/translate/TranslateTest.xtend | 22 +++---------------- 5 files changed, 25 insertions(+), 32 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java index 3cc0e6cfa..e973d4cbc 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java @@ -66,7 +66,6 @@ import com.regnosys.rosetta.utils.RosettaTypeSwitch; import com.rosetta.model.lib.ModelReportId; import com.rosetta.model.lib.ModelSymbolId; -import com.rosetta.model.lib.ModelTranslationId; import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.model.lib.reports.ReportFunction; import com.rosetta.model.lib.reports.Tabulator; @@ -120,8 +119,7 @@ public JavaClass toFunctionJavaClass(RFunction func) case RULE: return generatedJavaClassService.toJavaRule(func.getSymbolId()); case TRANSLATION: - ModelTranslationId id = func.getTranslationId(); - return generatedJavaClassService.toJavaTranslationFunction(id.getTranslateSource(), id.getInputTypes(), id.getOutputType()); + return generatedJavaClassService.toJavaTranslationFunction(func.getTranslationId()); default: throw new IllegalStateException("Unknown origin of RFunction: " + func.getOrigin()); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend index c5cca7c2e..c014c1031 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaSimpleValidator.xtend @@ -1411,7 +1411,6 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def checkImport(RosettaModel model) { - var usedNames = model.eAllContents.flatMap[ eCrossReferences.filter(RosettaRootElement).filter[isResolved].iterator ].map[ diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend index 20204c8d4..f61249350 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/RosettaTranslateValidator.xtend @@ -16,7 +16,6 @@ import java.util.List import com.regnosys.rosetta.rosetta.translate.TranslateSource import com.regnosys.rosetta.rosetta.RosettaFeature import com.regnosys.rosetta.rosetta.expression.RosettaExpression -import org.eclipse.emf.ecore.EObject import org.eclipse.xtext.EcoreUtil2 import com.regnosys.rosetta.rosetta.TypeCall import com.regnosys.rosetta.rosetta.translate.BaseTranslateInstruction @@ -58,11 +57,11 @@ class RosettaTranslateValidator extends AbstractDeclarativeRosettaValidator { } } - private def void translateToFeatureCheck(RosettaFeature feature, List inputs, BaseTranslateInstruction instruction) { + private def void translateToFeatureCheck(RosettaFeature feature, boolean isFeatureMulti, List inputs, BaseTranslateInstruction instruction) { // - For single cardinality attributes, all expressions should be single. // - For multi cardinality attributes, all expressions should be of the same cardinality. // - If the translation calls another translation, there should be at least one matching translation in the same translate source. - if (!feature.isFeatureMulti) { + if (!isFeatureMulti) { inputs.forEach[ if (isMulti) { error('''Expression must be of single cardinality when mapping to attribute `«feature.name»` of single cardinality.''', it, null); @@ -97,7 +96,7 @@ class RosettaTranslateValidator extends AbstractDeclarativeRosettaValidator { def void checkTranslateInstruction(TranslateInstruction instruction) { val container = instruction.eContainer if (container instanceof TranslationRule) { - translateToFeatureCheck(container.attribute, instruction.expressions, instruction) + translateToFeatureCheck(container.attribute, container.attribute.isFeatureMulti, instruction.expressions, instruction) } else if (container instanceof Translation) { translateToTypeCheck(container.resultType, instruction.expressions, instruction) } @@ -105,6 +104,12 @@ class RosettaTranslateValidator extends AbstractDeclarativeRosettaValidator { @Check def void checkTranslateMetaInstruction(TranslateMetaInstruction metaInstruction) { - translateToFeatureCheck(metaInstruction.metaFeature, metaInstruction.expressions, metaInstruction) + val container = metaInstruction.eContainer + val isFeatureMulti = if (container instanceof TranslationRule) { + container.attribute.isFeatureMulti + } else { + false + } + translateToFeatureCheck(metaInstruction.metaFeature, isFeatureMulti, metaInstruction.expressions, metaInstruction) } } diff --git a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend index b6aa51e20..8e2d6d56e 100644 --- a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend +++ b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend @@ -108,16 +108,23 @@ class CodeGeneratorTestHelper { } def createInstanceUsingBuilder(Map> classes, RootPackage namespace, String className, Map itemsToSet, Map> itemsToAddToList) { + val clazz = classes.get(namespace + '.' + className) + if (clazz === null) { + throw new RuntimeException('''Class «namespace + '.' + className» not found''') + } val rosettaClassBuilderInstance = classes.get(namespace + '.' + className).getMethod( "builder").invoke(null); itemsToSet.forEach [ name, value | - rosettaClassBuilderInstance.class.getMatchingMethod('set' + name.toFirstUpper, #[value?.class]).invoke( - rosettaClassBuilderInstance, value); + val setter = rosettaClassBuilderInstance.class.getMatchingMethod('set' + name.toFirstUpper, #[value?.class]) + if (setter === null) { + throw new RuntimeException('''No method #«'set' + name.toFirstUpper»(«value?.class?.simpleName») in «rosettaClassBuilderInstance.class»''') + } + setter.invoke(rosettaClassBuilderInstance, value); ] itemsToAddToList.forEach [ name, objectsToAdd | objectsToAdd.forEach [ value | - val clazz = rosettaClassBuilderInstance.class - val meth = getMatchingMethod(clazz, 'add' + name.toFirstUpper, #[value].map[class]) + val builderClazz = rosettaClassBuilderInstance.class + val meth = getMatchingMethod(builderClazz, 'add' + name.toFirstUpper, #[value].map[class]) meth.invoke( rosettaClassBuilderInstance, value); ] diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index 90ba3ad30..d5e87201f 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -122,8 +122,6 @@ class TranslateTest { [metadata scheme] type Bar: - v string (1..1) - t string (1..1) translate source FooBar { Foo from Bar: @@ -141,19 +139,7 @@ class TranslateTest { val classes = code.compileToClasses - val bar = classes.createInstanceUsingBuilder("Bar", #{ - "bs" -> #[ - classes.createInstanceUsingBuilder("Qux", #{ - "cs" -> #["a", "b"] - }), - classes.createInstanceUsingBuilder("Qux", #{ - "cs" -> #[] - }), - classes.createInstanceUsingBuilder("Qux", #{ - "cs" -> #["This", "is", "ignored"] - }) - ] - }) + val bar = classes.createInstanceUsingBuilder("Bar", #{}) val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ "key" -> #[Key.builder.setKeyValue("self")] @@ -164,10 +150,8 @@ class TranslateTest { "id" -> "favoriteNumber" }) }), - "self" -> classes.createInstanceUsingBuilder("Foo", #{ - "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "reference" -> Reference.builder.setReference("self") - }) + "self" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "reference" -> Reference.builder.setReference("self") }), "value" -> #[ classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ From 54c286bfe6bc719be1f0520c16fd896fd5770ef4 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 19 Jul 2024 11:19:23 +0100 Subject: [PATCH 24/37] Fix incorrectly updated test expectation --- .../rosetta/validation/RosettaTranslateValidationTest.xtend | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend index 07930760d..272f61fba 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaTranslateValidationTest.xtend @@ -38,7 +38,7 @@ class RosettaTranslateValidationTest implements RosettaIssueCodes { '''.parseRosetta model.assertError(TRANSLATION_PARAMETER, null, - '"as" statement can only be used with wildcard imports' + "Duplicate parameter name `var`." ) } From e12dfe88bae6d88652998ee7d9b838f1e2446cb4 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 19 Jul 2024 13:57:24 +0100 Subject: [PATCH 25/37] fix issue with importing same namespace from two models --- .../scoping/RosettaScopeProvider.xtend | 5 ++++- .../rosetta/tests/RosettaParsingTest.xtend | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index 01a12e81a..f674e1144 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -281,7 +281,10 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { override protected internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase) { return if (context instanceof RosettaModel) { - return context.imports.map [createImportedNamespaceResolver(importedNamespace, namespaceAlias, ignoreCase)] + val List imports = newArrayList(context.imports.map [createImportedNamespaceResolver(importedNamespace, namespaceAlias, ignoreCase)]) + //This import allows two models with the same namespace to reference each other + imports.add(doCreateImportNormalizer(getQualifiedNameConverter.toQualifiedName(context.name), true, ignoreCase)) + return imports } else emptyList } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend index f07e1a9b2..a78c899bf 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend @@ -34,6 +34,26 @@ class RosettaParsingTest { @Inject extension ValidationTestHelper @Inject extension ExpressionParser + @Test + def void testTwoModelsSameNamespaceReferencesEachOther() { + val model1 = ''' + namespace test + + type A: + id string (1..1) + ''' + + val model2 = ''' + namespace test + + + type B: + a A (1..1) + ''' + + #[model1, model2].parseRosettaWithNoIssues + } + @Test def void testScopingForImplicitFeatureWithSameNameAsAnnotation() { val model = ''' From bbee34614503302fa4bb77ca0a31cabb04529393 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 19 Jul 2024 14:35:27 +0100 Subject: [PATCH 26/37] fix alias validation error assert --- .../regnosys/rosetta/validation/RosettaValidatorTest.xtend | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index ed1d6a1c0..67819ce6b 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -44,6 +44,8 @@ class RosettaValidatorTest implements RosettaIssueCodes { ) } + + //TODO: write a validation for when the user forgets the alias @Test def void testCanUserImportAlisesWhenWildcardPresent() { val model1 = ''' @@ -61,7 +63,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { type B: - a A (1..1) + a someAlias.A (1..1) ''' #[model1, model2].parseRosettaWithNoIssues From 12f4e995163513d54a2d1fc56d45f7ca9a6490f8 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Fri, 19 Jul 2024 14:41:11 +0100 Subject: [PATCH 27/37] disable translate with meta test to run build --- .../rosetta/generator/java/translate/TranslateTest.xtend | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index d5e87201f..cf2026fa7 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.* import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage import com.rosetta.model.lib.meta.Key import com.rosetta.model.lib.meta.Reference +import org.junit.jupiter.api.Disabled @InjectWith(RosettaInjectorProvider) @ExtendWith(InjectionExtension) @@ -105,6 +106,8 @@ class TranslateTest { assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) } + //TODO: implement translate with meta + @Disabled @Test def void testTranslationWithMetadata() { val code = ''' From 14ac5585f7810bccc95c82474a208eebc615caef Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Mon, 22 Jul 2024 17:49:26 +0100 Subject: [PATCH 28/37] Limit class name sizes when they would cause file length over 255 --- .../generated/GeneratedJavaClassService.java | 22 +++++++- .../java/translate/TranslateTest.xtend | 55 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java b/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java index 4e0a39b7c..f5765e6c1 100644 --- a/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java +++ b/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java @@ -70,13 +70,33 @@ public JavaClass toJavaFunction(ModelSymbolId id) { public JavaClass toJavaTranslationFunction(ModelTranslationId translationId) { DottedPath packageName = translationId.getNamespace().child("translate"); - String inputNames = translationId.getInputTypes().stream().map(id -> StringUtils.capitalize(id.getName())).collect(Collectors.joining("And")); + String inputNames = generateInputName(translationId, false); String outputName = StringUtils.capitalize(translationId.getOutputType().getName()); String sourceName = StringUtils.capitalize(translationId.getTranslateSource().getName()); String simpleName = String.format("Translate%sTo%sUsing%s", inputNames, outputName, sourceName); + //Max Linux and Mac file length is 255 chars. A translator called `TranslatorFunc` will result in an inner default file called `TranslatorFunc$TranslatorFuncDefault` + //So our max translator name can be (255 - 7 for default - 4 for ext - 1 for $)/2 = 121.5. So our char limit for function names is 121 + //TOOD: A better solution here would be to stop generating the full name for the default inner class + if (simpleName.length() > 121) { + inputNames = generateInputName(translationId, true); + simpleName = String.format("Translate%sTo%sUsing%s", inputNames, outputName, sourceName); + } return new GeneratedJavaClass<>(packageName, simpleName, RosettaFunction.class); } + private String generateInputName(ModelTranslationId translationId, boolean compress) { + return translationId.getInputTypes().stream() + .map(id -> { + if (compress) { + return id.getName().substring(0, 2); + } else { + return id.getName(); + } + }) + .map(name -> StringUtils.capitalize(name)) + .collect(Collectors.joining("And")); + } + public JavaClass toJavaType(ModelSymbolId id) { DottedPath packageName = id.getNamespace(); String simpleName = id.getName(); diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index cf2026fa7..9cd055044 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -60,6 +60,61 @@ class TranslateTest { assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) } + @Test + def void testTranslateClassNameIsLegalLength() { + val code = ''' + type Foo: + a1 string (1..1) + a2 string (1..1) + a3 string (1..1) + a4 string (1..1) + a5 string (1..1) + a6 string (1..1) + + type BarOneWithVeryVeryLongName: + b string (1..1) + + type BarTwoWithVeryVeryLongName: + b string (1..1) + + type BarThreeWithVeryVeryLongName: + b string (1..1) + + type BarFourWithVeryVeryLongName: + b string (1..1) + + type BarFiveWithVeryVeryLongName: + b string (1..1) + + type BarSixWithVeryVeryLongName: + b string (1..1) + + translate source LongTranslate { + Foo from BarOneWithVeryVeryLongName, bar2 BarTwoWithVeryVeryLongName, bar3 BarThreeWithVeryVeryLongName, bar4 BarFourWithVeryVeryLongName, bar5 BarFiveWithVeryVeryLongName: + + a1 + [from b] + + a2 + [from bar2 -> b] + + a3 + [from bar3 -> b] + + a4 + [from bar4 -> b] + + a5 + [from bar5 -> b] + } + '''.generateCode + + val classes = code.compileToClasses + + + val translation = classes.createTranslation("LongTranslate", #["BarOneWithVeryVeryLongName", "BarTwoWithVeryVeryLongName", "BarThreeWithVeryVeryLongName", "BarFourWithVeryVeryLongName", "BarFiveWithVeryVeryLongName"], "Foo"); + + val className = translation.class.canonicalName.replaceAll("^com\\.rosetta\\.test\\.model\\.translate\\.", "") + System.out.println(className) + + assertTrue(className.length + 4 <= 255, "Translator class name too long") + } + @Test def void testTranslationWithMultiCardinality() { val code = ''' From 49f8d9e905edff161d721ac0e1918d4ac89c7090 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Mon, 22 Jul 2024 18:06:36 +0100 Subject: [PATCH 29/37] remove print --- .../rosetta/generator/java/translate/TranslateTest.xtend | 1 - 1 file changed, 1 deletion(-) diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index 9cd055044..1b28f9fe8 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -110,7 +110,6 @@ class TranslateTest { val translation = classes.createTranslation("LongTranslate", #["BarOneWithVeryVeryLongName", "BarTwoWithVeryVeryLongName", "BarThreeWithVeryVeryLongName", "BarFourWithVeryVeryLongName", "BarFiveWithVeryVeryLongName"], "Foo"); val className = translation.class.canonicalName.replaceAll("^com\\.rosetta\\.test\\.model\\.translate\\.", "") - System.out.println(className) assertTrue(className.length + 4 <= 255, "Translator class name too long") } From 1fa75913e0991aefba41526c60df62019f0ae44a Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Tue, 23 Jul 2024 17:05:18 +0100 Subject: [PATCH 30/37] Update ROperation to handle meta feature --- .../rosetta/types/RObjectFactory.java | 23 ++++++++++ .../regnosys/rosetta/types/ROperation.java | 31 +++++++++---- .../java/translate/TranslateTest.xtend | 45 ++++++++++++++++++- 3 files changed, 89 insertions(+), 10 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java index 72be6b345..9dd3d5cc4 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java @@ -43,6 +43,7 @@ import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration; import com.regnosys.rosetta.rosetta.simple.SimpleFactory; import com.regnosys.rosetta.rosetta.translate.TranslateInstruction; +import com.regnosys.rosetta.rosetta.translate.TranslateMetaInstruction; import com.regnosys.rosetta.rosetta.translate.TranslateSource; import com.regnosys.rosetta.rosetta.translate.Translation; import com.regnosys.rosetta.rosetta.translate.TranslationParameter; @@ -192,6 +193,15 @@ public RFunction buildRFunction(Translation translation) { return rule.getInstructions() .stream() .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), rule.getAttribute().getTypeCall(), outputAttribute, List.of(attr), attr.isMulti())); + }), + // attribute level instructions + translation.getRules() + .stream() + .flatMap(rule -> { + RAttribute attr = buildRAttribute(rule.getAttribute()); + return rule.getMetaInstructions() + .stream() + .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), rule.getAttribute().getTypeCall(), outputAttribute, List.of(attr), attr.isMulti())); }) ).collect(Collectors.toList()); @@ -209,6 +219,19 @@ public RFunction buildRFunction(Translation translation) { ); } + public ROperation generateOperationForTranslateInstruction(TranslateMetaInstruction instr, TranslateSource source, TypeCall outputType, RAttribute outputAttribute, List assignPath, boolean isMulti) { + TranslateDispatchOperation op = ExpressionFactory.eINSTANCE.createTranslateDispatchOperation(); + op.setGenerated(true); + op.setOutputType(outputType); + op.setSource(source); + op.getInputs().addAll(instr.getExpressions()); + instr.set_internalDispatchExpression(op); + RType metaRType = typeSystem.typeCallToRType(instr.getMetaFeature().getTypeCall()); + RAttribute metaFeature = new RAttribute(instr.getMetaFeature().getName(), null, metaRType, List.of(), false); + return new ROperation(isMulti ? ROperationType.ADD : ROperationType.SET, outputAttribute, assignPath, metaFeature, op); + } + + public ROperation generateOperationForTranslateInstruction(TranslateInstruction instr, TranslateSource source, TypeCall outputType, RAttribute outputAttribute, List assignPath, boolean isMulti) { TranslateDispatchOperation op = ExpressionFactory.eINSTANCE.createTranslateDispatchOperation(); op.setGenerated(true); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java index 547aef7d1..46fa84f7f 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java @@ -22,16 +22,26 @@ import com.regnosys.rosetta.rosetta.expression.RosettaExpression; public class ROperation { - private ROperationType rOperationType; - private RAssignedRoot pathHead; - private List pathTail; - private RosettaExpression expression; + private final ROperationType rOperationType; + private final RAssignedRoot pathHead; + private final List pathTail; + private final RosettaExpression expression; + private final RAttribute metaFeature; public ROperation(ROperationType rOperationType, RAssignedRoot pathHead, List pathTail, RosettaExpression expression) { this.rOperationType = rOperationType; this.pathHead = pathHead; this.pathTail = pathTail; this.expression = expression; + this.metaFeature = null; + } + + public ROperation(ROperationType rOperationType, RAssignedRoot pathHead, List pathTail, RAttribute metaFeature, RosettaExpression expression) { + this.rOperationType = rOperationType; + this.pathHead = pathHead; + this.pathTail = pathTail; + this.expression = expression; + this.metaFeature = metaFeature; } public ROperationType getROperationType() { @@ -49,10 +59,14 @@ public List getPathTail() { public RosettaExpression getExpression() { return expression; } + + public boolean isMetaOperation() { + return metaFeature != null; + } @Override public int hashCode() { - return Objects.hash(expression, pathHead, pathTail, rOperationType); + return Objects.hash(expression, metaFeature, pathHead, pathTail, rOperationType); } @Override @@ -64,9 +78,10 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; ROperation other = (ROperation) obj; - return Objects.equals(expression, other.expression) && Objects.equals(pathHead, other.pathHead) - && Objects.equals(pathTail, other.pathTail) && rOperationType == other.rOperationType; + return Objects.equals(expression, other.expression) && Objects.equals(metaFeature, other.metaFeature) + && Objects.equals(pathHead, other.pathHead) && Objects.equals(pathTail, other.pathTail) + && rOperationType == other.rOperationType; } - + } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index 1b28f9fe8..40a912d16 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -160,7 +160,48 @@ class TranslateTest { assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) } - //TODO: implement translate with meta + @Disabled + @Test + def void testTranslationWithMetaSchemeOnly() { + val code = ''' + metaType key string + metaType id string + metaType reference string + + type Foo: + a string (1..1) + [metadata scheme] + + type Bar: + + translate source FooBar { + Foo from Bar: + + a + [from "a"] + [meta scheme from "schemeA"] + } + '''.generateCode + + val classes = code.compileToClasses + + code.writeClasses("testTranslationWithMetaSchemeOnly") + + val bar = classes.createInstanceUsingBuilder("Bar", #{}) + val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ + "a" -> + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "a", + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "schemeA" + }) + }) + + }) + + val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); + assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) + } + @Disabled @Test def void testTranslationWithMetadata() { @@ -193,7 +234,7 @@ class TranslateTest { [meta scheme from ["schemeA", "schemeB"]] } '''.generateCode - + val classes = code.compileToClasses val bar = classes.createInstanceUsingBuilder("Bar", #{}) From d4e98478dec44af5d88d51254bdc304c6cef0dfe Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Tue, 23 Jul 2024 18:29:55 +0100 Subject: [PATCH 31/37] Build generation, still need import --- .../generator/java/function/FunctionGenerator.xtend | 12 +++++++++--- .../java/com/regnosys/rosetta/types/ROperation.java | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend index 83ddba9e5..fdb79ff8f 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend @@ -438,11 +438,17 @@ class FunctionGenerator { JavaExpression.from( ''' «op.assignTarget(function, outs, scope)» - «FOR seg : op.pathTail.indexed» - «IF seg.key < op.pathTail.size - 1» + «FOR seg : op.pathTail.indexed» + «IF seg.key < op.pathTail.size - 1» .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF isReference(seg.value)».getOrCreateValue()«ENDIF» + «ELSE» + «IF op.isMetaOperation» + .«IF op.ROperationType == ROperationType.SET»getOrCreate«seg.value.name.toFirstUpper»().setMeta(MetaFields.builder().set«op.metaFeature.name.toFirstUpper»(«it»))«ENDIF» «ELSE» - .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it»)«ENDIF»«ENDFOR»''', + .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it») + «ENDIF» + «ENDIF» + «ENDFOR»''', JavaPrimitiveType.VOID ) ].completeAsExpressionStatement diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java index 46fa84f7f..5cd810372 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ROperation.java @@ -63,6 +63,10 @@ public RosettaExpression getExpression() { public boolean isMetaOperation() { return metaFeature != null; } + + public RAttribute getMetaFeature() { + return metaFeature; + } @Override public int hashCode() { From bc1531c7c940c9316881b2f3e4864210ce5d657a Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Tue, 23 Jul 2024 18:31:04 +0100 Subject: [PATCH 32/37] got it working! --- .../rosetta/generator/java/function/FunctionGenerator.xtend | 2 +- .../rosetta/generator/java/translate/TranslateTest.xtend | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend index fdb79ff8f..f3d767ebb 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend @@ -443,7 +443,7 @@ class FunctionGenerator { .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF isReference(seg.value)».getOrCreateValue()«ENDIF» «ELSE» «IF op.isMetaOperation» - .«IF op.ROperationType == ROperationType.SET»getOrCreate«seg.value.name.toFirstUpper»().setMeta(MetaFields.builder().set«op.metaFeature.name.toFirstUpper»(«it»))«ENDIF» + .«IF op.ROperationType == ROperationType.SET»getOrCreate«seg.value.name.toFirstUpper»().setMeta(com.rosetta.model.metafields.MetaFields.builder().set«op.metaFeature.name.toFirstUpper»(«it»))«ENDIF» «ELSE» .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it») «ENDIF» diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index 40a912d16..787c89eb7 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -160,7 +160,6 @@ class TranslateTest { assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) } - @Disabled @Test def void testTranslationWithMetaSchemeOnly() { val code = ''' From c0bad4a65d586f1c1c95c1ba6a3467979dcd1f1a Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Wed, 24 Jul 2024 10:24:37 +0100 Subject: [PATCH 33/37] Test that other meta types are not set --- .../java/function/FunctionGenerator.xtend | 26 ++++++++++++------- .../java/translate/TranslateTest.xtend | 10 ++++++- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend index f3d767ebb..090a1460d 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend @@ -432,28 +432,36 @@ class FunctionGenerator { } } else { // assign an attribute of the function output object + if (op.isMetaOperation && !isSupportedMetaOperation(op)) { + return JavaExpression.from('''''', JavaPrimitiveType.VOID).completeAsExpressionStatement + } assignValue(scope, op, op.assignAsKey, op.pathTail.last.multi) .collapseToSingleExpression(scope) .mapExpression[ JavaExpression.from( ''' «op.assignTarget(function, outs, scope)» - «FOR seg : op.pathTail.indexed» - «IF seg.key < op.pathTail.size - 1» - .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF isReference(seg.value)».getOrCreateValue()«ENDIF» - «ELSE» - «IF op.isMetaOperation» - .«IF op.ROperationType == ROperationType.SET»getOrCreate«seg.value.name.toFirstUpper»().setMeta(com.rosetta.model.metafields.MetaFields.builder().set«op.metaFeature.name.toFirstUpper»(«it»))«ENDIF» + «FOR seg : op.pathTail.indexed» + «IF seg.key < op.pathTail.size - 1» + .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF isReference(seg.value)».getOrCreateValue()«ENDIF» «ELSE» - .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it») + «IF op.isMetaOperation» + .getOrCreate«seg.value.name.toFirstUpper»().setMeta(com.rosetta.model.metafields.MetaFields.builder().set«op.metaFeature.name.toFirstUpper»(«it»)) + «ELSE» + .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it») + «ENDIF» «ENDIF» - «ENDIF» - «ENDFOR»''', + «ENDFOR» + ''', JavaPrimitiveType.VOID ) ].completeAsExpressionStatement } } + + private def boolean isSupportedMetaOperation(ROperation op) { + op.isMetaOperation && op.ROperationType == ROperationType.SET && op.metaFeature.name.toLowerCase.equals("scheme") + } private def JavaStatementBuilder assignValue(JavaScope scope, ROperation op, boolean assignAsKey, boolean isAssigneeMulti) { if (assignAsKey) { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index 787c89eb7..1ccd782f7 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -170,6 +170,8 @@ class TranslateTest { type Foo: a string (1..1) [metadata scheme] + b string (1..1) + [metadata reference] type Bar: @@ -178,6 +180,9 @@ class TranslateTest { + a [from "a"] [meta scheme from "schemeA"] + + b + [from "b"] + [meta reference from "referenceB"] } '''.generateCode @@ -193,8 +198,11 @@ class TranslateTest { "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ "scheme" -> "schemeA" }) + }), + "b" -> + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaString", #{ + "value" -> "b" }) - }) val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); From bd51037702536cb8208defbbf34873547f0df6e2 Mon Sep 17 00:00:00 2001 From: David Al-Kanani Date: Wed, 24 Jul 2024 11:03:10 +0100 Subject: [PATCH 34/37] Use generated import for MetaFields --- .../generator/java/function/FunctionGenerator.xtend | 13 ++++--------- .../generator/java/translate/TranslateTest.xtend | 4 +--- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend index 090a1460d..9e9f27927 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend @@ -68,6 +68,7 @@ import java.util.Collections import com.fasterxml.jackson.core.type.TypeReference import com.rosetta.util.types.JavaGenericTypeDeclaration import com.regnosys.rosetta.generator.java.expression.JavaDependencyProvider +import com.regnosys.rosetta.generator.java.RosettaJavaPackages class FunctionGenerator { @@ -85,6 +86,7 @@ class FunctionGenerator { @Inject ImplicitVariableUtil implicitVariableUtil @Inject extension JavaTypeUtil @Inject TypeCoercionService coercionService + @Inject RosettaJavaPackages rosettaJavaPackages def void generate(RootPackage root, IFileSystemAccess2 fsa, Function func, String version) { val fileName = root.functions.withForwardSlashes + '/' + func.name + '.java' @@ -433,7 +435,7 @@ class FunctionGenerator { } else { // assign an attribute of the function output object if (op.isMetaOperation && !isSupportedMetaOperation(op)) { - return JavaExpression.from('''''', JavaPrimitiveType.VOID).completeAsExpressionStatement + return JavaExpression.from('''//Meta Operation to set feature `«op.metaFeature.name»` is not currently supported''', JavaPrimitiveType.VOID).completeAsExpressionStatement } assignValue(scope, op, op.assignAsKey, op.pathTail.last.multi) .collapseToSingleExpression(scope) @@ -445,14 +447,7 @@ class FunctionGenerator { «IF seg.key < op.pathTail.size - 1» .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF isReference(seg.value)».getOrCreateValue()«ENDIF» «ELSE» - «IF op.isMetaOperation» - .getOrCreate«seg.value.name.toFirstUpper»().setMeta(com.rosetta.model.metafields.MetaFields.builder().set«op.metaFeature.name.toFirstUpper»(«it»)) - «ELSE» - .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it») - «ENDIF» - «ENDIF» - «ENDFOR» - ''', + «IF op.isMetaOperation».getOrCreate«seg.value.name.toFirstUpper»().setMeta(«new GeneratedJavaClass(rosettaJavaPackages.basicMetafields, "MetaFields", Object)».builder().set«op.metaFeature.name.toFirstUpper»(«it»))«ELSE».«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it»)«ENDIF»«ENDIF»«ENDFOR»''', JavaPrimitiveType.VOID ) ].completeAsExpressionStatement diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index 1ccd782f7..7f4950134 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -187,9 +187,7 @@ class TranslateTest { '''.generateCode val classes = code.compileToClasses - - code.writeClasses("testTranslationWithMetaSchemeOnly") - + val bar = classes.createInstanceUsingBuilder("Bar", #{}) val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ "a" -> From ef3252f469d702c810011ffa4a12324b614767d5 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Mon, 29 Jul 2024 14:25:31 +0200 Subject: [PATCH 35/37] Implemented meta operation Java generation --- .../java/function/FunctionGenerator.xtend | 56 ++++++-- .../regnosys/rosetta/types/RAssignedRoot.java | 1 + .../regnosys/rosetta/types/RAttribute.java | 1 + .../rosetta/types/RObjectFactory.java | 38 ++--- .../com/regnosys/rosetta/types/RShortcut.java | 11 +- .../generated/GeneratedJavaClassService.java | 2 +- .../java/translate/TranslateTest.xtend | 130 ++++++++---------- 7 files changed, 127 insertions(+), 112 deletions(-) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend index 9e9f27927..9d6d6cced 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend @@ -69,6 +69,8 @@ import com.fasterxml.jackson.core.type.TypeReference import com.rosetta.util.types.JavaGenericTypeDeclaration import com.regnosys.rosetta.generator.java.expression.JavaDependencyProvider import com.regnosys.rosetta.generator.java.RosettaJavaPackages +import com.rosetta.model.lib.meta.Reference +import com.rosetta.model.lib.meta.Key class FunctionGenerator { @@ -392,7 +394,7 @@ class FunctionGenerator { private def JavaStatement assign(JavaScope scope, ROperation op, RFunction function, Map outs, RAttribute attribute) { - if (op.pathTail.isEmpty) { + if (op.pathTail.isEmpty && !op.isMetaOperation) { // assign function output object val expressionType = attribute.attributeToJavaType var javaExpr = expressionGenerator.javaCode(op.expression, expressionType, scope) @@ -434,31 +436,57 @@ class FunctionGenerator { } } else { // assign an attribute of the function output object - if (op.isMetaOperation && !isSupportedMetaOperation(op)) { - return JavaExpression.from('''//Meta Operation to set feature `«op.metaFeature.name»` is not currently supported''', JavaPrimitiveType.VOID).completeAsExpressionStatement - } - assignValue(scope, op, op.assignAsKey, op.pathTail.last.multi) + val StringConcatenationClient assignPathCode = + ''' + «op.assignTarget(function, outs, scope)» + «FOR seg : op.pathTail.indexed» + «IF seg.key < op.pathTail.size - 1 || op.isMetaOperation» + .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF isReference(seg.value) && seg.key < op.pathTail.size - 1».getOrCreateValue()«ENDIF» + «ENDIF» + «ENDFOR» + ''' + assignValue(scope, op, op.assignAsKey) .collapseToSingleExpression(scope) .mapExpression[ JavaExpression.from( ''' - «op.assignTarget(function, outs, scope)» - «FOR seg : op.pathTail.indexed» - «IF seg.key < op.pathTail.size - 1» - .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF isReference(seg.value)».getOrCreateValue()«ENDIF» - «ELSE» - «IF op.isMetaOperation».getOrCreate«seg.value.name.toFirstUpper»().setMeta(«new GeneratedJavaClass(rosettaJavaPackages.basicMetafields, "MetaFields", Object)».builder().set«op.metaFeature.name.toFirstUpper»(«it»))«ELSE».«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.isReference && !op.assignAsKey»Value«ENDIF»(«it»)«ENDIF»«ENDIF»«ENDFOR»''', + «assignPathCode» + «IF op.isMetaOperation» + «IF op.metaFeature.name != "reference" && op.metaFeature.name != "address"» + .getOrCreateMeta() + «ENDIF» + «op.metaFeature.metaFeatureSetterCode(it)»« + ELSE» + .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«op.pathTail.last.name.toFirstUpper»«IF op.pathTail.last.isReference && !op.assignAsKey»Value«ENDIF»(«it»)« + ENDIF»''', JavaPrimitiveType.VOID ) ].completeAsExpressionStatement } } - private def boolean isSupportedMetaOperation(ROperation op) { - op.isMetaOperation && op.ROperationType == ROperationType.SET && op.metaFeature.name.toLowerCase.equals("scheme") + private def String metaFeatureToJavaField(RAttribute metaFeature) { + switch metaFeature.name { + case "key": "globalKey" + case "id": "globalKey" + case "reference": "globalReference" + case "scheme": "scheme" + case "template": "template" + case "address": "reference" + case "location": "key" + } + } + private def StringConcatenationClient metaFeatureSetterCode(RAttribute metaFeature, JavaExpression v) { + if (metaFeature.name == "address") { + '''.set«metaFeature.metaFeatureToJavaField.toFirstUpper»(«Reference».builder().setScope("DOCUMENT").setReference(«v»))''' + } else if (metaFeature.name == "location") { + '''.add«metaFeature.metaFeatureToJavaField.toFirstUpper»(«Key».builder().setScope("DOCUMENT").setKeyValue(«v»))''' + } else { + '''.set«metaFeature.metaFeatureToJavaField.toFirstUpper»(«v»)''' + } } - private def JavaStatementBuilder assignValue(JavaScope scope, ROperation op, boolean assignAsKey, boolean isAssigneeMulti) { + private def JavaStatementBuilder assignValue(JavaScope scope, ROperation op, boolean assignAsKey) { if (assignAsKey) { val metaClass = op.operationToReferenceWithMetaType if (cardinality.isMulti(op.expression)) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAssignedRoot.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAssignedRoot.java index 96efc7127..d373e93b9 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAssignedRoot.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAssignedRoot.java @@ -18,4 +18,5 @@ public interface RAssignedRoot { String getName(); + boolean isMulti(); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java index 9975cb9f4..29c1c9667 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAttribute.java @@ -43,6 +43,7 @@ public RType getRType() { return rType; } + @Override public boolean isMulti() { return isMulti; } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java index 9dd3d5cc4..0f4149cb0 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RObjectFactory.java @@ -185,23 +185,24 @@ public RFunction buildRFunction(Translation translation) { translation.getTypeInstructions() .stream() .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), translation.getResultType(), outputAttribute, List.of(), false)), - // attribute level instructions - translation.getRules() + // type level meta instructions + translation.getTypeMetaInstructions() .stream() - .flatMap(rule -> { - RAttribute attr = buildRAttribute(rule.getAttribute()); - return rule.getInstructions() - .stream() - .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), rule.getAttribute().getTypeCall(), outputAttribute, List.of(attr), attr.isMulti())); - }), + .map(instr -> generateOperationForTranslateMetaInstruction(instr, translation.getSource(), instr.getMetaFeature().getTypeCall(), outputAttribute, List.of(), false)), // attribute level instructions translation.getRules() .stream() .flatMap(rule -> { RAttribute attr = buildRAttribute(rule.getAttribute()); - return rule.getMetaInstructions() - .stream() - .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), rule.getAttribute().getTypeCall(), outputAttribute, List.of(attr), attr.isMulti())); + return Streams.concat( + rule.getInstructions() + .stream() + .map(instr -> generateOperationForTranslateInstruction(instr, translation.getSource(), rule.getAttribute().getTypeCall(), outputAttribute, List.of(attr), attr.isMulti())), + // attribute level meta instructions + rule.getMetaInstructions() + .stream() + .map(instr -> generateOperationForTranslateMetaInstruction(instr, translation.getSource(), instr.getMetaFeature().getTypeCall(), outputAttribute, List.of(attr), attr.isMulti())) + ); }) ).collect(Collectors.toList()); @@ -219,27 +220,26 @@ public RFunction buildRFunction(Translation translation) { ); } - public ROperation generateOperationForTranslateInstruction(TranslateMetaInstruction instr, TranslateSource source, TypeCall outputType, RAttribute outputAttribute, List assignPath, boolean isMulti) { + public ROperation generateOperationForTranslateInstruction(TranslateInstruction instr, TranslateSource source, TypeCall outputType, RAttribute outputAttribute, List assignPath, boolean isMulti) { TranslateDispatchOperation op = ExpressionFactory.eINSTANCE.createTranslateDispatchOperation(); op.setGenerated(true); op.setOutputType(outputType); op.setSource(source); op.getInputs().addAll(instr.getExpressions()); instr.set_internalDispatchExpression(op); - RType metaRType = typeSystem.typeCallToRType(instr.getMetaFeature().getTypeCall()); - RAttribute metaFeature = new RAttribute(instr.getMetaFeature().getName(), null, metaRType, List.of(), false); - return new ROperation(isMulti ? ROperationType.ADD : ROperationType.SET, outputAttribute, assignPath, metaFeature, op); + return new ROperation(isMulti ? ROperationType.ADD : ROperationType.SET, outputAttribute, assignPath, op); } - - public ROperation generateOperationForTranslateInstruction(TranslateInstruction instr, TranslateSource source, TypeCall outputType, RAttribute outputAttribute, List assignPath, boolean isMulti) { + public ROperation generateOperationForTranslateMetaInstruction(TranslateMetaInstruction instr, TranslateSource source, TypeCall outputType, RAttribute outputAttribute, List assignPath, boolean isMulti) { TranslateDispatchOperation op = ExpressionFactory.eINSTANCE.createTranslateDispatchOperation(); op.setGenerated(true); op.setOutputType(outputType); op.setSource(source); op.getInputs().addAll(instr.getExpressions()); instr.set_internalDispatchExpression(op); - return new ROperation(isMulti ? ROperationType.ADD : ROperationType.SET, outputAttribute, assignPath, op); + RType metaRType = typeSystem.typeCallToRType(instr.getMetaFeature().getTypeCall()); + RAttribute metaFeature = new RAttribute(instr.getMetaFeature().getName(), null, metaRType, List.of(), false); + return new ROperation(isMulti ? ROperationType.ADD : ROperationType.SET, outputAttribute, assignPath, metaFeature, op); } public RAttribute buildRAttribute(Attribute attribute) { @@ -264,7 +264,7 @@ public RAttribute buildRAttribute(TranslationParameter parameter) { } public RShortcut buildRShortcut(ShortcutDeclaration shortcut) { - return new RShortcut(shortcut.getName(), shortcut.getDefinition(), shortcut.getExpression()); + return new RShortcut(shortcut.getName(), cardinalityProvider.isSymbolMulti(shortcut), shortcut.getDefinition(), shortcut.getExpression()); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RShortcut.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RShortcut.java index 19ec65fa4..a9bd8791a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RShortcut.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RShortcut.java @@ -22,11 +22,13 @@ public class RShortcut implements RAssignedRoot { private String name; + private boolean isMulti; private String definition; private RosettaExpression expression; - public RShortcut(String name, String definition, RosettaExpression expression) { + public RShortcut(String name, boolean isMulti, String definition, RosettaExpression expression) { this.name = name; + this.isMulti = isMulti; this.definition = definition; this.expression = expression; } @@ -35,6 +37,11 @@ public RShortcut(String name, String definition, RosettaExpression expression) { public String getName() { return name; } + + @Override + public boolean isMulti() { + return isMulti; + } public String getDefinition() { return definition; @@ -60,5 +67,5 @@ public boolean equals(Object obj) { RShortcut other = (RShortcut) obj; return Objects.equals(definition, other.definition) && Objects.equals(expression, other.expression) && Objects.equals(name, other.name); - } + } } diff --git a/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java b/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java index f5765e6c1..a684069fb 100644 --- a/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java +++ b/rosetta-runtime/src/main/java/com/rosetta/util/types/generated/GeneratedJavaClassService.java @@ -74,7 +74,7 @@ public JavaClass toJavaTranslationFunction(ModelTranslationId t String outputName = StringUtils.capitalize(translationId.getOutputType().getName()); String sourceName = StringUtils.capitalize(translationId.getTranslateSource().getName()); String simpleName = String.format("Translate%sTo%sUsing%s", inputNames, outputName, sourceName); - //Max Linux and Mac file length is 255 chars. A translator called `TranslatorFunc` will result in an inner default file called `TranslatorFunc$TranslatorFuncDefault` + //Max Unix file length is 255 chars. A translator called `TranslatorFunc` will result in an inner default file called `TranslatorFunc$TranslatorFuncDefault` //So our max translator name can be (255 - 7 for default - 4 for ext - 1 for $)/2 = 121.5. So our char limit for function names is 121 //TOOD: A better solution here would be to stop generating the full name for the default inner class if (simpleName.length() > 121) { diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index 7f4950134..a4969e41a 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -160,60 +160,15 @@ class TranslateTest { assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) } - @Test - def void testTranslationWithMetaSchemeOnly() { - val code = ''' - metaType key string - metaType id string - metaType reference string - - type Foo: - a string (1..1) - [metadata scheme] - b string (1..1) - [metadata reference] - - type Bar: - - translate source FooBar { - Foo from Bar: - + a - [from "a"] - [meta scheme from "schemeA"] - + b - [from "b"] - [meta reference from "referenceB"] - } - '''.generateCode - - val classes = code.compileToClasses - - val bar = classes.createInstanceUsingBuilder("Bar", #{}) - val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ - "a" -> - classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ - "value" -> "a", - "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "scheme" -> "schemeA" - }) - }), - "b" -> - classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaString", #{ - "value" -> "b" - }) - }) - - val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); - assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) - } - - @Disabled @Test def void testTranslationWithMetadata() { val code = ''' metaType key string metaType id string metaType reference string + metaType template string + metaType location string + metaType address string type Foo: [metadata key] @@ -221,13 +176,21 @@ class TranslateTest { [metadata id] self Foo (1..1) [metadata reference] - value string (0..*) + value string (0..1) [metadata scheme] type Bar: + [metadata key] + [metadata template] + a int (1..1) + [metadata address] + b Foo (1..1) + [metadata location] + + type Inp: translate source FooBar { - Foo from Bar: + Foo from Inp: [meta key from "self"] + a [from 42] @@ -235,48 +198,63 @@ class TranslateTest { + self [meta reference from "self"] + value - [from ["a", "b", "c"]] - [meta scheme from ["schemeA", "schemeB"]] + [from "a"] + [meta scheme from "schemeA"] + + Bar from Inp: + [meta key from "My key"] + [meta template from "My template"] + + a + [from 42] + [meta address from "Some address"] + + b + [from item] + [meta location from "My location"] } '''.generateCode val classes = code.compileToClasses - val bar = classes.createInstanceUsingBuilder("Bar", #{}) - val expectedResult = classes.createInstanceUsingBuilder("Foo", #{ + val inp = classes.createInstanceUsingBuilder("Inp", #{}) + val expectedFoo = classes.createInstanceUsingBuilder("Foo", #{ "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "key" -> #[Key.builder.setKeyValue("self")] + "globalKey" -> "self" }), "a" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaInteger", #{ "value" -> 42, "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "id" -> "favoriteNumber" + "globalKey" -> "favoriteNumber" }) }), "self" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ - "reference" -> Reference.builder.setReference("self") + "globalReference" -> "self" }), - "value" -> #[ - classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ - "value" -> "a", - "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "scheme" -> "schemeA" - }) - }), - classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ - "value" -> "b", - "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "scheme" -> "schemeB" - }) - }), - classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ - "value" -> "c" - }) - ] + "value" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "a", + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "schemeA" + }) + }) }) + val expectedBar = classes.createInstanceUsingBuilder("Bar", #{ + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaAndTemplateFields", #{ + "globalKey" -> "My key", + "template" -> "My template" + }), + "a" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaInteger", #{ + "value" -> 42, + "reference" -> Reference.builder.setScope("DOCUMENT").setReference("Some address") + }), + "b" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "FieldWithMetaFoo", #{ + "value" -> expectedFoo, + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "key" -> #[Key.builder.setScope("DOCUMENT").setKeyValue("My location")] + }) + }) + }) - val translation = classes.createTranslation("FooBar", #["Bar"], "Foo"); - assertEquals(expectedResult, translation.invokeFunc(expectedResult.class, #[bar])) + val translation = classes.createTranslation("FooBar", #["Inp"], "Bar"); + assertEquals(expectedBar, translation.invokeFunc(expectedBar.class, #[inp])) } } \ No newline at end of file From 59453872a8ad33d54d5e6957d9b3ce2731008e54 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Mon, 29 Jul 2024 17:45:18 +0200 Subject: [PATCH 36/37] Fixed multi meta generation --- .../rosetta/generator/java/types/JavaTypeTranslator.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java index 39c8bd927..2d188053a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeTranslator.java @@ -270,6 +270,9 @@ public JavaClass operationToReferenceWithMetaType(Operation op) { public JavaReferenceType operationToJavaType(ROperation op) { RAttribute attr; + if (op.isMetaOperation()) { + return attributeToJavaType(op.getMetaFeature()); + } if (op.getPathTail().isEmpty()) { attr = (RAttribute)op.getPathHead(); // TODO: this won't work when assigning to an alias } else { From 0c61417c56a38991d4cc83fb4492b8e2a0090209 Mon Sep 17 00:00:00 2001 From: Simon Cockx Date: Tue, 30 Jul 2024 13:21:04 +0200 Subject: [PATCH 37/37] Fixed external keys/references --- .github/workflows/branch-deploy.yml | 8 ++++---- .../generator/java/function/FunctionGenerator.xtend | 6 +++--- .../rosetta/generator/java/translate/TranslateTest.xtend | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/branch-deploy.yml b/.github/workflows/branch-deploy.yml index 12a154f70..e62853366 100644 --- a/.github/workflows/branch-deploy.yml +++ b/.github/workflows/branch-deploy.yml @@ -35,10 +35,10 @@ jobs: uses: ./.github/actions/normalize-branch-name - name: Set version run: mvn -B versions:set -DgenerateBackupPoms=false -DnewVersion=0.0.0.${{ steps.normalize-branch-name.outputs.normalized }}-SNAPSHOT - - name: Publish package - run: mvn -B -U clean deploy + - uses: ./.github/actions/maven-build + with: + build-command: deploy + run-tests: ${{ inputs.run-tests }} env: - GPG_KEYNAME: ${{ secrets.RUNE_GPG_KEYNAME }} - GPG_PASSPHRASE: ${{ secrets.RUNE_GPG_PASSPHRASE }} CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend index 9d6d6cced..caab278ea 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend @@ -467,9 +467,9 @@ class FunctionGenerator { private def String metaFeatureToJavaField(RAttribute metaFeature) { switch metaFeature.name { - case "key": "globalKey" - case "id": "globalKey" - case "reference": "globalReference" + case "key": "externalKey" + case "id": "externalKey" + case "reference": "externalReference" case "scheme": "scheme" case "template": "template" case "address": "reference" diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend index a4969e41a..cbbe9052d 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/translate/TranslateTest.xtend @@ -218,16 +218,16 @@ class TranslateTest { val inp = classes.createInstanceUsingBuilder("Inp", #{}) val expectedFoo = classes.createInstanceUsingBuilder("Foo", #{ "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "globalKey" -> "self" + "externalKey" -> "self" }), "a" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaInteger", #{ "value" -> 42, "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ - "globalKey" -> "favoriteNumber" + "externalKey" -> "favoriteNumber" }) }), "self" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ - "globalReference" -> "self" + "externalReference" -> "self" }), "value" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ "value" -> "a", @@ -238,7 +238,7 @@ class TranslateTest { }) val expectedBar = classes.createInstanceUsingBuilder("Bar", #{ "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaAndTemplateFields", #{ - "globalKey" -> "My key", + "externalKey" -> "My key", "template" -> "My template" }), "a" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaInteger", #{