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 3f5dbdc58..75af51b2a 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 @@ -222,9 +222,9 @@ public List markOperation(Operation operation) { } } for (Segment seg : operation.pathAsSegmentList()) { - Attribute segAttr = seg.getAttribute(); + RosettaFeature segAttr = seg.getFeature(); if (extensions.isResolved(segAttr)) { - SemanticToken segmentToken = markAttribute(seg, SEGMENT__ATTRIBUTE, segAttr, AttributeType.OUTPUT); + SemanticToken segmentToken = markFeature(seg, SEGMENT__FEATURE, segAttr, AttributeType.OUTPUT); if (segmentToken != null) { result.add(segmentToken); } diff --git a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorMetaTest.java b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorMetaTest.java new file mode 100644 index 000000000..961e12896 --- /dev/null +++ b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorMetaTest.java @@ -0,0 +1,249 @@ +package com.regnosys.rosetta.generator.java.function; + +import com.regnosys.rosetta.generator.java.RosettaJavaPackages; +import com.regnosys.rosetta.tests.RosettaTestInjectorProvider; +import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper; +import com.rosetta.model.lib.RosettaModelObject; +import com.rosetta.model.lib.meta.FieldWithMeta; +import com.rosetta.model.lib.meta.Reference; +import com.rosetta.model.lib.meta.ReferenceWithMeta; +import com.rosetta.model.metafields.MetaFields; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import javax.inject.Inject; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(InjectionExtension.class) +@InjectWith(RosettaTestInjectorProvider.class) +public class FunctionGeneratorMetaTest { + + @Inject + FunctionGeneratorHelper functionGeneratorHelper; + @Inject + CodeGeneratorTestHelper generatorTestHelper; + + //TODO:canSetMetaLocationOnFunctionBasicOutput + + + @Test + void canSetMetaAddressOnFunctionBasicOutput() { + var model = """ + metaType address string + + func MyFunc: + output: + result string (1..1) + [metadata address] + set result: "someValue" + set result -> address: "someLocation" + """; + + var code = generatorTestHelper.generateCode(model); + + var classes = generatorTestHelper.compileToClasses(code); + + + var myFunc = functionGeneratorHelper.createFunc(classes, "MyFunc"); + + var result = functionGeneratorHelper.invokeFunc(myFunc, ReferenceWithMeta.class); + + var expected = generatorTestHelper.createInstanceUsingBuilder(classes, new RosettaJavaPackages.RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaString", Map.of( + "value", "someValue", + "reference", Reference.builder().setReference("someLocation") + )); + + assertEquals(expected, result); + } + + + @Test + void canSetExternalIdOnFunctionObjectOutput() { + var model = """ + metaType id string + + func MyFunc: + inputs: + myValue string (1..1) + myReference string (1..1) + + output: + result string (1..1) + [metadata id] + set result: myValue + set result -> id: myReference + """; + + var code = generatorTestHelper.generateCode(model); + var classes = generatorTestHelper.compileToClasses(code); + var myFunc = functionGeneratorHelper.createFunc(classes, "MyFunc"); + + var result = functionGeneratorHelper.invokeFunc(myFunc, FieldWithMeta.class, "someValue", "someExternalKey"); + + var expected = generatorTestHelper.createInstanceUsingBuilder(classes, new RosettaJavaPackages.RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", Map.of( + "value", "someValue", + "meta", MetaFields.builder().setExternalKey("someExternalKey") + )); + + assertEquals(expected, result); + } + + + @Disabled //TODO: implement nested setting of meta and then complete this + @Test + void canSetExternalKeyOnFunctionObjectOutput() { + var model = """ + + metaType key string + metaType reference string + + type Foo: + [metadata key] + a string (1..1) + + func MyFunc: + inputs: + myReference string (1..1) + + output: + result Foo (1..1) + set result -> key: myReference + """; + + var code = generatorTestHelper.generateCode(model); + } + + @Test + void canSetExternalReferenceOnFunctionObjectOutput() { + var model = """ + metaType key string + metaType reference string + + type Foo: + [metadata key] + a string (1..1) + + func MyFunc: + inputs: + myKey string (1..1) + + output: + result Foo (1..1) + [metadata reference] + set result -> reference: myKey + """; + + var code = generatorTestHelper.generateCode(model); + + var classes = generatorTestHelper.compileToClasses(code); + var myFunc = functionGeneratorHelper.createFunc(classes, "MyFunc"); + + var result = functionGeneratorHelper.invokeFunc(myFunc, ReferenceWithMeta.class, "someExternalReference"); + + var expected = generatorTestHelper.createInstanceUsingBuilder(classes, new RosettaJavaPackages.RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", Map.of( + "externalReference", "someExternalReference" + )); + + assertEquals(expected, result); + } + + @Disabled //TODO: implement setting nested meta + @Test + void canSetMetaOnFunctionObjectOutputAndNestedMetaField() { + var model = """ + type Foo: + a string (1..1) + b string (1..1) + [metadata scheme] + + func MyFunc: + output: + result Foo (1..1) + [metadata scheme] + set result -> scheme: "outerScheme" + set result -> a: "someValueA" + set result -> b: "someValueB" + set result -> b -> scheme: "innerScheme" + """; + + var code = generatorTestHelper.generateCode(model); + var classes = generatorTestHelper.compileToClasses(code); + var myFunc = functionGeneratorHelper.createFunc(classes, "MyFunc"); + + var result = functionGeneratorHelper.invokeFunc(myFunc, RosettaModelObject.class); + + var expected = generatorTestHelper.createInstanceUsingBuilder(classes, new RosettaJavaPackages.RootPackage("com.rosetta.test.model.metafields"), "FieldWithMetaFoo", Map.of( + "value", generatorTestHelper.createInstanceUsingBuilder(classes, "Foo", Map.of( + "a", "someValueA", + "b", generatorTestHelper.createInstanceUsingBuilder(classes, new RosettaJavaPackages.RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", Map.of( + "value", "someValueB", + "meta", MetaFields.builder().setScheme("innerScheme") + )) + )), + "meta", MetaFields.builder().setScheme("outerScheme") + )); + + assertEquals(expected, result); + } + + @Test + void canSetMetaSchemeOnFunctionObjectOutput() { + var model = """ + type Foo: + a string (1..1) + b string (1..1) + + func MyFunc: + output: + result Foo (1..1) + [metadata scheme] + set result -> scheme: "outerScheme" + set result -> a: "someValueA" + set result -> b: "someValueB" + """; + + var code = generatorTestHelper.generateCode(model); + var classes = generatorTestHelper.compileToClasses(code); + var myFunc = functionGeneratorHelper.createFunc(classes, "MyFunc"); + + var result = functionGeneratorHelper.invokeFunc(myFunc, RosettaModelObject.class); + + var expected = generatorTestHelper.createInstanceUsingBuilder(classes, new RosettaJavaPackages.RootPackage("com.rosetta.test.model.metafields"), "FieldWithMetaFoo", Map.of( + "value", generatorTestHelper.createInstanceUsingBuilder(classes, "Foo", Map.of( + "a", "someValueA", + "b", "someValueB" + )), + "meta", MetaFields.builder().setScheme("outerScheme") + )); + + assertEquals(expected, result); + } + + @Test + void canSetMetaSchemeOnFunctionBasicOutput() { + var model = """ + func MyFunc: + output: + result string (1..1) + [metadata scheme] + set result: "someValue" + set result -> scheme: "someScheme" + """; + + var code = generatorTestHelper.generateCode(model); + var classes = generatorTestHelper.compileToClasses(code); + + var myFunc = functionGeneratorHelper.createFunc(classes, "MyFunc"); + + var result = functionGeneratorHelper.invokeFunc(myFunc, FieldWithMeta.class); + + var expected = generatorTestHelper.createFieldWithMetaString(classes, "someValue", "someScheme"); + + assertEquals(expected, result); + } +} diff --git a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend index 975d32609..fc030b2ad 100644 --- a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend +++ b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend @@ -1,20 +1,25 @@ package com.regnosys.rosetta.generator.java.function import com.google.common.collect.ImmutableList -import com.regnosys.rosetta.rosetta.simple.SimplePackage +import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage import com.regnosys.rosetta.tests.RosettaTestInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper -import com.regnosys.rosetta.validation.RosettaIssueCodes import com.rosetta.model.lib.RosettaModelObject +import com.rosetta.model.lib.meta.FieldWithMeta +import com.rosetta.model.lib.meta.Key +import com.rosetta.model.lib.meta.Reference import com.rosetta.model.lib.records.Date +import com.rosetta.model.metafields.MetaFields import java.math.BigDecimal +import java.time.LocalDateTime import java.time.LocalTime import java.time.ZoneId import java.time.ZonedDateTime import java.util.Arrays import java.util.List import java.util.Map +import javax.inject.Inject import org.eclipse.xtext.testing.InjectWith import org.eclipse.xtext.testing.extensions.InjectionExtension import org.eclipse.xtext.testing.validation.ValidationTestHelper @@ -26,14 +31,8 @@ import static com.google.common.collect.ImmutableMap.* import static com.regnosys.rosetta.rosetta.expression.ExpressionPackage.Literals.* import static org.hamcrest.MatcherAssert.assertThat import static org.hamcrest.core.IsCollectionContaining.hasItems -import static org.junit.jupiter.api.Assertions.* import static org.junit.Assert.assertThrows -import javax.inject.Inject -import java.time.LocalDateTime -import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage -import com.rosetta.model.lib.meta.Key -import com.rosetta.model.lib.meta.Reference -import com.rosetta.model.metafields.MetaFields +import static org.junit.jupiter.api.Assertions.* @ExtendWith(InjectionExtension) @InjectWith(RosettaTestInjectorProvider) @@ -43,7 +42,7 @@ class FunctionGeneratorTest { @Inject extension CodeGeneratorTestHelper @Inject extension ModelHelper @Inject extension ValidationTestHelper - + @Test def void reportingRuleSupportsRecursion() { val code = ''' @@ -3148,6 +3147,7 @@ class FunctionGeneratorTest { ''' ].generateCode + code.compileToClasses } diff --git a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend index 54f8d3b22..75bd3d8fa 100644 --- a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend +++ b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/tests/RosettaParsingTest.xtend @@ -35,6 +35,21 @@ class RosettaParsingTest { @Inject extension ValidationTestHelper @Inject extension ExpressionParser + @Test + def void testCannotSetEnumAttribute() { + ''' + enum FooEnum: + VALUE1 + VALUE2 + + func MyFunc: + output: + result FooEnum (0..1) + set result -> VALUE1: empty + '''.parseRosetta + .assertError(SEGMENT, Diagnostic.LINKING_DIAGNOSTIC, "Couldn't resolve reference to RosettaFeature 'VALUE1'.") + } + @Test def void testLabelAnnotation() { ''' diff --git a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend index b2f48bdc8..09a92fea5 100644 --- a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend +++ b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend @@ -52,7 +52,7 @@ class RosettaTypeProviderTest { RMetaAttribute SCHEME @BeforeAll def void setup() { - SCHEME = new RMetaAttribute("scheme", UNCONSTRAINED_STRING, null) + SCHEME = new RMetaAttribute("scheme", UNCONSTRAINED_STRING) } private def void assertIsValidWithType(CharSequence expr, RMetaAnnotatedType expectedType, boolean expectedIsMulti, List context, String... attributes) { diff --git a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend index 7efccbaed..eda3dbf87 100644 --- a/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend +++ b/rosetta-integration-tests/src/test/java/com/regnosys/rosetta/validation/RosettaValidatorTest.xtend @@ -2179,6 +2179,30 @@ class RosettaValidatorTest implements RosettaIssueCodes { "'as-key' can only be used with attributes annotated with [metadata reference] annotation.") } + @Test + def checkAsKeyUsage_03() { + val model = ''' + type WithKey: + [metadata key] + + type TypeToUse: + attr WithKey (0..1) + [metadata reference] + attr2 TypeToUse (0..1) + + func Bar: + inputs: + in0 WithKey (1..1) + in1 TypeToUse (1..1) + output: result TypeToUse (1..1) + [metadata scheme] + set result -> scheme: + in1 as-key + '''.parseRosetta + model.assertError(AS_KEY_OPERATION, null, + "'as-key' can only be used with attributes annotated with [metadata reference] annotation.") + } + @Test def checkAsKeyUsage_04() { val model = ''' diff --git a/rosetta-lang/model/RosettaSimple.xcore b/rosetta-lang/model/RosettaSimple.xcore index ddc1ba5eb..5ef2c0718 100644 --- a/rosetta-lang/model/RosettaSimple.xcore +++ b/rosetta-lang/model/RosettaSimple.xcore @@ -23,6 +23,7 @@ import com.regnosys.rosetta.rosetta.RosettaAttributeReferenceSegment import com.regnosys.rosetta.rosetta.RosettaCardinality import com.regnosys.rosetta.rosetta.RosettaFactory import com.regnosys.rosetta.rosetta.expression.ExpressionFactory +import com.regnosys.rosetta.rosetta.RosettaFeature import org.eclipse.xtext.nodemodel.util.NodeModelUtils import org.eclipse.emf.common.util.EList @@ -183,7 +184,7 @@ class Operation extends RosettaDefinable { } class Segment { - refers Attribute attribute + refers RosettaFeature feature contains Segment next opposite prev container Segment prev opposite next 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 8d2e341ea..e0dba0f6a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/Rosetta.xtext @@ -139,7 +139,7 @@ Operation: ; Segment: - '->' attribute = [Attribute|ValidID] (next = Segment)?; + '->' feature = [RosettaTypedFeature|ValidID] (next = Segment)?; EnumValueReference returns RosettaEnumValueReference: enumeration=[RosettaEnumeration|QualifiedName] '->' value=[RosettaEnumValue|ValidID] diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend index 8a85e4665..8ac167b82 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend @@ -30,6 +30,7 @@ import org.eclipse.emf.ecore.resource.ResourceSet import org.eclipse.emf.ecore.util.EcoreUtil import com.regnosys.rosetta.rosetta.simple.Annotated +import java.util.function.Predicate @Singleton // see `metaFieldsCache` class RosettaEcoreUtil { @@ -46,9 +47,19 @@ class RosettaEcoreUtil { allFeatures(t.RType, context?.eResource?.resourceSet) + metas } + def Iterable allFeatures(RMetaAnnotatedType t, EObject context, Predicate restrictType) { + val List metas = getMetaDescriptions(t, context) + allFeatures(t.RType, context, restrictType) + metas + } + def Iterable allFeatures(RType t, EObject context) { allFeatures(t, context?.eResource?.resourceSet) } + + def Iterable allFeatures(RType t, EObject context, Predicate restrictType) { + restrictType.test(t) ? allFeatures(t, context?.eResource?.resourceSet) : #[] + } + def Iterable allFeatures(RType t, ResourceSet resourceSet) { switch t { RDataType: 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 2dace6a1f..87df8fdc8 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 @@ -12,8 +12,13 @@ import com.regnosys.rosetta.generator.java.expression.TypeCoercionService import com.regnosys.rosetta.generator.java.statement.JavaStatement import com.regnosys.rosetta.generator.java.statement.builder.JavaExpression import com.regnosys.rosetta.generator.java.statement.builder.JavaStatementBuilder +import com.regnosys.rosetta.generator.java.statement.builder.JavaVariable +import com.regnosys.rosetta.generator.java.types.JavaPojoInterface +import com.regnosys.rosetta.generator.java.types.JavaPojoProperty import com.regnosys.rosetta.generator.java.types.JavaTypeTranslator import com.regnosys.rosetta.generator.java.types.JavaTypeUtil +import com.regnosys.rosetta.generator.java.types.RJavaFieldWithMeta +import com.regnosys.rosetta.generator.java.types.RJavaWithMetaValue import com.regnosys.rosetta.generator.java.util.ImportManagerExtension import com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions @@ -33,8 +38,10 @@ import com.regnosys.rosetta.rosetta.simple.FunctionDispatch import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration import com.regnosys.rosetta.types.CardinalityProvider import com.regnosys.rosetta.types.RAttribute +import com.regnosys.rosetta.types.RFeature import com.regnosys.rosetta.types.RFunction import com.regnosys.rosetta.types.RFunctionOrigin +import com.regnosys.rosetta.types.RMetaAttribute import com.regnosys.rosetta.types.RObjectFactory import com.regnosys.rosetta.types.ROperation import com.regnosys.rosetta.types.ROperationType @@ -44,6 +51,7 @@ import com.regnosys.rosetta.utils.ExpressionHelper import com.regnosys.rosetta.utils.ImplicitVariableUtil import com.regnosys.rosetta.utils.ModelIdProvider import com.rosetta.model.lib.ModelSymbolId +import com.rosetta.model.lib.annotations.RuneLabelProvider import com.rosetta.model.lib.functions.ConditionValidator import com.rosetta.model.lib.functions.IQualifyFunctionExtension import com.rosetta.model.lib.functions.ModelObjectValidator @@ -69,10 +77,7 @@ import static com.regnosys.rosetta.generator.java.enums.EnumHelper.* import static com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil.* import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta -import com.regnosys.rosetta.generator.java.statement.builder.JavaVariable -import com.regnosys.rosetta.generator.java.types.JavaPojoInterface -import com.regnosys.rosetta.generator.java.types.RJavaWithMetaValue -import com.rosetta.model.lib.annotations.RuneLabelProvider +import com.regnosys.rosetta.generator.java.types.RJavaReferenceWithMeta class FunctionGenerator { @@ -295,7 +300,7 @@ class FunctionGenerator { «doEvaluateScope.getIdentifierOrThrow(input)» = «Collections».emptyList(); } «ENDFOR» - «output.toBuilderType» «doEvaluateScope.getIdentifierOrThrow(output)» = «IF output.multi»new «ArrayList»<>()«ELSEIF output.needsBuilder»«output.RMetaAnnotatedType.RType.toListOrSingleJavaType(output.multi)».builder()«ELSE»null«ENDIF»; + «output.toBuilderType» «doEvaluateScope.getIdentifierOrThrow(output)» = «IF output.multi»new «ArrayList»<>()«ELSEIF output.needsBuilder»«output.RMetaAnnotatedType.toListOrSingleJavaType(output.multi)».builder()«ELSE»null«ENDIF»; return assignOutput(«doEvaluateScope.getIdentifierOrThrow(output)»«IF !inputs.empty», «ENDIF»«inputs.inputsAsArguments(doEvaluateScope)»); } @@ -401,7 +406,6 @@ class FunctionGenerator { } private def JavaStatement assign(JavaScope scope, ROperation op, RFunction function, Map outs, RAttribute attribute) { - if (op.pathTail.isEmpty) { // assign function output object val expressionType = attribute.toMetaJavaType @@ -448,34 +452,86 @@ class FunctionGenerator { .collapseToSingleExpression(scope) .mapExpression[ var expr = op.assignTarget(function, outs, scope) - for (seg : op.pathTail.indexed) { + + // path intermediary + val intermediarySegmentSize = op.pathTail.length - 1 + for (var pathIndex=0; pathIndex < intermediarySegmentSize; pathIndex++) { + + val seg = op.pathTail.get(pathIndex) + val prop = findPojoProperty(seg, expr.expressionType.itemType) + val oldExpr = expr - val prop = (expr.expressionType as JavaPojoInterface).findProperty(seg.value.name) val itemType = prop.type.itemType - if (seg.key < op.pathTail.size - 1) { - expr = JavaExpression.from( - ''' - «oldExpr» - .«prop.getOrCreateName»(«IF prop.type.isList»0«ENDIF»)''', - itemType - ) - if (itemType instanceof RJavaWithMetaValue) { - val metaExpr = expr - expr = JavaExpression.from('''«metaExpr».getOrCreateValue()''', itemType.valueType) - } - } else { - expr = JavaExpression.from( - ''' - «oldExpr» - .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«prop.name.toFirstUpper»«IF itemType instanceof RJavaWithMetaValue && !op.assignAsKey»Value«ENDIF»(«it»)''', - JavaPrimitiveType.VOID - ) + expr = JavaExpression.from( + ''' + «oldExpr» + .«prop.getOrCreateName»(«IF prop.type.isList»0«ENDIF»)''', + itemType + ) + if (itemType instanceof RJavaWithMetaValue) { + val metaExpr = expr + expr = JavaExpression.from('''«metaExpr».getOrCreateValue()''', itemType.valueType) } } + + //end of path + val seg = op.pathTail.get(op.pathTail.length - 1) + + val oldExpr = expr + val outputExpressionType = expr.expressionType.itemType + val prop = findPojoProperty(seg, outputExpressionType) + val pojoPropertyType = prop.type.itemType + + if (outputExpressionType instanceof RJavaWithMetaValue) { + expr = JavaExpression.from( + ''' + «oldExpr» + «generateMetaWrapperCreator(prop, outputExpressionType)».«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.toPojoPropertyNames.toFirstUpper»(«it»)''', + JavaPrimitiveType.VOID + ) + } else { + expr = JavaExpression.from( + ''' + «oldExpr» + .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«prop.name.toFirstUpper»«IF pojoPropertyType instanceof RJavaWithMetaValue && !op.assignAsKey»Value«ENDIF»(«it»)''', + JavaPrimitiveType.VOID + ) + } + expr ].completeAsExpressionStatement } } + + private def StringConcatenationClient generateMetaWrapperCreator(JavaPojoProperty prop, RJavaWithMetaValue outputExpressionType) { + switch (outputExpressionType) { + RJavaFieldWithMeta: '''.«prop.getOrCreateName»()''' + RJavaReferenceWithMeta: '''«IF prop.name === "reference"».«prop.getOrCreateName»()«ENDIF»''' + } + } + + //The type of the output expression to be set and the pojo property type are not the same when working with meta + private def JavaPojoProperty findPojoProperty(RFeature seg, JavaType outputExpressionType) { + if (seg instanceof RMetaAttribute && outputExpressionType.itemType instanceof RJavaFieldWithMeta) { + (outputExpressionType as JavaPojoInterface).findProperty("meta") + } else if (seg instanceof RMetaAttribute && outputExpressionType.itemType instanceof RJavaReferenceWithMeta) { + (outputExpressionType as JavaPojoInterface).findProperty(toPojoPropertyNames(seg)) + } else if (outputExpressionType.itemType instanceof RJavaWithMetaValue) { + (outputExpressionType as JavaPojoInterface).findProperty("value") + } else { + (outputExpressionType as JavaPojoInterface).findProperty(seg.name) + } + } + + private def String toPojoPropertyNames(RFeature seg) { + return switch(seg.name) { + case "reference": "externalReference" + case "id": "externalKey" + case "address": "reference" + default: seg.name + } + } + private def JavaStatementBuilder assignValue(JavaScope scope, ROperation op, boolean assignAsKey) { if (assignAsKey) { @@ -636,7 +692,7 @@ class FunctionGenerator { private def JavaType toBuilderItemType(RAttribute rAttribute) { var javaType = rAttribute.RMetaAnnotatedType.toJavaReferenceType as JavaClass - if(rAttribute.needsBuilder) javaType = javaType.toBuilderType + if(javaType.needsBuilder) javaType = javaType.toBuilderType javaType } private def JavaType toBuilderType(RAttribute rAttribute) { @@ -647,4 +703,16 @@ class FunctionGenerator { return javaType } } + + private def needsBuilder(RAttribute rAttribute) { + var javaType = rAttribute.RMetaAnnotatedType.toJavaReferenceType as JavaClass + javaType.needsBuilder + } + + private def boolean needsBuilder(JavaClass javaClass) { + switch (javaClass) { + JavaPojoInterface: true + default: false + } + } } 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 e866650d0..983f449ae 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 @@ -16,43 +16,14 @@ package com.regnosys.rosetta.generator.java.types; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZonedDateTime; -import java.util.List; -import java.util.Optional; - -import javax.inject.Inject; - import com.regnosys.rosetta.generator.java.RosettaJavaPackages; import com.regnosys.rosetta.rosetta.RosettaExternalFunction; import com.regnosys.rosetta.rosetta.RosettaExternalRuleSource; +import com.regnosys.rosetta.rosetta.RosettaFeature; import com.regnosys.rosetta.rosetta.RosettaReport; -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.Segment; -import com.regnosys.rosetta.types.RAliasType; -import com.regnosys.rosetta.types.RAttribute; -import com.regnosys.rosetta.types.RChoiceType; -import com.regnosys.rosetta.types.RDataType; -import com.regnosys.rosetta.types.REnumType; -import com.regnosys.rosetta.types.RFunction; -import com.regnosys.rosetta.types.RMetaAnnotatedType; -import com.regnosys.rosetta.types.ROperation; -import com.regnosys.rosetta.types.RType; -import com.regnosys.rosetta.types.RosettaTypeProvider; -import com.regnosys.rosetta.types.TypeSystem; -import com.regnosys.rosetta.types.builtin.RBasicType; -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; -import com.regnosys.rosetta.types.builtin.RDateTimeType; -import com.regnosys.rosetta.types.builtin.RDateType; -import com.regnosys.rosetta.types.builtin.RNumberType; -import com.regnosys.rosetta.types.builtin.RStringType; -import com.regnosys.rosetta.types.builtin.RZonedDateTimeType; +import com.regnosys.rosetta.rosetta.simple.*; +import com.regnosys.rosetta.types.*; +import com.regnosys.rosetta.types.builtin.*; import com.regnosys.rosetta.utils.ModelIdProvider; import com.regnosys.rosetta.utils.RosettaTypeSwitch; import com.rosetta.model.lib.ModelSymbolId; @@ -63,15 +34,20 @@ import com.rosetta.model.lib.reports.ReportFunction; import com.rosetta.model.lib.reports.Tabulator; import com.rosetta.util.DottedPath; -import com.rosetta.util.types.JavaClass; -import com.rosetta.util.types.JavaParameterizedType; -import com.rosetta.util.types.JavaPrimitiveType; -import com.rosetta.util.types.JavaReferenceType; -import com.rosetta.util.types.JavaType; +import com.rosetta.util.types.*; import com.rosetta.util.types.generated.GeneratedJavaClass; import com.rosetta.util.types.generated.GeneratedJavaClassService; import com.rosetta.util.types.generated.GeneratedJavaGenericTypeDeclaration; +import javax.inject.Inject; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Optional; + public class JavaTypeTranslator extends RosettaTypeSwitch { @Inject public JavaTypeTranslator(RBuiltinTypeService builtins) { @@ -199,6 +175,9 @@ public JavaClass toDeepPathUtilJavaClass(RDataType choiceType) { String simpleName = typeId.getName() + "DeepPathUtil"; return new GeneratedJavaClass<>(packageName, simpleName, Object.class); } + public JavaClass toItemJavaType(RMetaAttribute attr) { + return toJavaReferenceType(attr.getRType()); + } public JavaClass toItemJavaType(RAttribute attr) { return toJavaReferenceType(attr.getRMetaAnnotatedType().getRType()); } @@ -236,36 +215,50 @@ public JavaClass toJavaType(RAttribute attr) { } return itemType; } + public JavaClass toJavaType(RFeature feature) { + if (feature instanceof RAttribute) { + return toJavaType((RAttribute) feature); + } else if (feature instanceof RMetaAttribute) { + return toItemJavaType((RMetaAttribute) feature); + } else { + throw new UnsupportedOperationException("No JavaType exists for feature: " + feature.getName()); + } + } public JavaClass operationToReferenceWithMetaType(Operation op) { - Attribute attr; + RosettaFeature feature; if (op.getPath() == null) { - attr = (Attribute)op.getAssignRoot(); // TODO: this won't work when assigning to an alias + feature = (RosettaFeature)op.getAssignRoot(); // TODO: this won't work when assigning to an alias } else { List segments = op.pathAsSegmentList(); - attr = segments.get(segments.size() - 1).getAttribute(); + feature = segments.get(segments.size() - 1).getFeature(); } - return toJavaReferenceType(typeProvider.getRTypeOfSymbol(attr)); + return toJavaReferenceType(typeProvider.getRTypeOfFeature(feature, null)); } - public JavaReferenceType operationToJavaType(ROperation op) { - RAttribute attr; + RFeature feature; if (op.getPathTail().isEmpty()) { - attr = (RAttribute)op.getPathHead(); // TODO: this won't work when assigning to an alias + feature = (RFeature)op.getPathHead(); // TODO: this won't work when assigning to an alias } else { - List segments = op.getPathTail(); - attr = segments.get(segments.size() - 1); + List segments = op.getPathTail(); + feature = segments.get(segments.size() - 1); } - return toJavaType(attr); + return toJavaType(feature); } public JavaClass operationToReferenceWithMetaType(ROperation op) { - RAttribute attr; + RFeature feature; if (op.getPathTail().isEmpty()) { - attr = (RAttribute)op.getPathHead(); // TODO: this won't work when assigning to an alias + feature = (RFeature)op.getPathHead(); // TODO: this won't work when assigning to an alias } else { - List segments = op.getPathTail(); - attr = segments.get(segments.size() - 1); + List segments = op.getPathTail(); + feature = segments.get(segments.size() - 1); + } + if (feature instanceof RAttribute) { + return toJavaReferenceType(((RAttribute)feature).getRMetaAnnotatedType()); + } else if (feature instanceof RMetaAttribute) { + return toJavaReferenceType(((RMetaAttribute)feature).getRType()); + } else { + throw new UnsupportedOperationException("No JavaReferenceType exists for feature: " + feature.getName()); } - return toJavaReferenceType(attr.getRMetaAnnotatedType()); } private String getTypeDebugInfo(RType type) { 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 011840a20..ce09426b3 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 @@ -141,18 +141,21 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { } return IScope.NULLSCOPE } - case SEGMENT__ATTRIBUTE: { + case SEGMENT__FEATURE: { switch (context) { + // Case handles the head of the segment Operation: { val receiverType = typeProvider.getRTypeOfSymbol(context.assignRoot) - return Scopes.scopeFor(receiverType.RType.allFeatures(context)) + val features = receiverType.allFeatures(context, [t| !(t instanceof REnumType)]) + return Scopes.scopeFor(features) //TODO: don't get all features (i.e enum atrs) } + // Case handles the tail of the segment Segment: { val prev = context.prev if (prev !== null) { - if (prev.attribute.isResolved) { - val receiverType = typeProvider.getRTypeOfSymbol(prev.attribute) - return Scopes.scopeFor(receiverType.RType.allFeatures(context)) + if (prev.feature.isResolved) { + val receiverType = typeProvider.getRTypeOfFeature(prev.feature, context) + return Scopes.scopeFor(receiverType.allFeatures(context, [t| !(t instanceof REnumType)])) //TODO: don't get all features (i.e enum atrs) } } if (context.eContainer instanceof Operation) { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ExpectedTypeProvider.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ExpectedTypeProvider.java index 47a85abf8..92161d420 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ExpectedTypeProvider.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/ExpectedTypeProvider.java @@ -131,7 +131,7 @@ public RMetaAnnotatedType getExpectedType(EObject owner, EReference reference, i return typeProvider.getRTypeOfSymbol(op.getAssignRoot()); } List path = op.pathAsSegmentList(); - return typeProvider.getRTypeOfSymbol(path.get(path.size() - 1).getAttribute()); + return typeProvider.getRTypeOfFeature(path.get(path.size() - 1).getFeature(), null); } else if (CONSTRUCTOR_KEY_VALUE_PAIR__VALUE.equals(reference) && owner instanceof ConstructorKeyValuePair) { ConstructorKeyValuePair pair = (ConstructorKeyValuePair) owner; return typeProvider.getRTypeOfFeature(pair.getKey(), null); 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 7aeb1fc8c..478d99cf7 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 @@ -25,7 +25,7 @@ import com.regnosys.rosetta.rosetta.simple.Attribute; import com.regnosys.rosetta.rosetta.simple.LabelAnnotation; -public class RAttribute implements RAssignedRoot { +public class RAttribute implements RAssignedRoot, RFeature { private final boolean isOverride; private final String name; private final String definition; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFeature.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFeature.java new file mode 100644 index 000000000..48574588c --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RFeature.java @@ -0,0 +1,5 @@ +package com.regnosys.rosetta.types; + +public interface RFeature { + String getName(); +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAttribute.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAttribute.java index e669984b9..153cb73d8 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAttribute.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAttribute.java @@ -6,15 +6,13 @@ import com.regnosys.rosetta.rosetta.simple.Attribute; -public class RMetaAttribute implements RObject { +public class RMetaAttribute implements RFeature { private final String name; private final RType type; - private Attribute attribute; - public RMetaAttribute(String name, RType type, Attribute attribute) { + public RMetaAttribute(String name, RType type) { this.name = name; this.type = type; - this.attribute = attribute; } public String getName() { @@ -47,10 +45,5 @@ public String toString() { return "RMetaAttribute [name=" + name + ", type=" + type + "]"; } - @Override - public EObject getEObject() { - return attribute; - } - } 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 e16ec0140..992e4b32d 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 @@ -31,6 +31,8 @@ import com.regnosys.rosetta.rosetta.RosettaCardinality; import com.regnosys.rosetta.rosetta.RosettaEnumeration; import com.regnosys.rosetta.rosetta.RosettaFactory; +import com.regnosys.rosetta.rosetta.RosettaFeature; +import com.regnosys.rosetta.rosetta.RosettaMetaType; import com.regnosys.rosetta.rosetta.RosettaReport; import com.regnosys.rosetta.rosetta.RosettaRule; import com.regnosys.rosetta.rosetta.expression.ExpressionFactory; @@ -135,12 +137,12 @@ public RFunction buildRFunction(RosettaReport report) { ); } - private List generateReportOperations(RDataType reportDataType, Map attributeToRuleMap, Attribute inputAttribute, List assignPath) { + private List generateReportOperations(RDataType reportDataType, Map attributeToRuleMap, Attribute inputAttribute, List assignPath) { Collection attributes = reportDataType.getAllAttributes(); List operations = new ArrayList<>(); for (RAttribute attribute : attributes) { - List newAssignPath = new ArrayList<>(assignPath); + List newAssignPath = new ArrayList<>(assignPath); newAssignPath.add(attribute); if (attributeToRuleMap.containsKey(attribute)) { operations.add(generateOperationForRuleReference(inputAttribute, attributeToRuleMap.get(attribute), newAssignPath)); @@ -155,9 +157,9 @@ private List generateReportOperations(RDataType reportDataType, Map< return operations; } - private ROperation generateOperationForRuleReference(Attribute inputAttribute, RosettaRule rule, List assignPath) { - RAttribute pathHead = assignPath.get(0); - List pathTail = assignPath.subList(1, assignPath.size()); + private ROperation generateOperationForRuleReference(Attribute inputAttribute, RosettaRule rule, List assignPath) { + RAssignedRoot pathHead = (RAssignedRoot) assignPath.get(0); + List pathTail = assignPath.subList(1, assignPath.size()); RosettaSymbolReference inputAttributeSymbolRef = ExpressionFactory.eINSTANCE.createRosettaSymbolReference(); inputAttributeSymbolRef.setSymbol(inputAttribute); @@ -218,7 +220,18 @@ public ROperation buildROperation(Operation operation) { pathHead = buildRShortcut((ShortcutDeclaration) operation.getAssignRoot()); } - List pathTail = operation.pathAsSegmentList().stream().map(s -> buildRAttribute(s.getAttribute())) + List pathTail = operation.pathAsSegmentList() + .stream() + .map(s -> { + RosettaFeature feature = s.getFeature(); + if (feature instanceof Attribute) { + return buildRAttribute((Attribute) feature); + } + if (feature instanceof RosettaMetaType) { + return buildRMetaAttribute((RosettaMetaType) feature); + } + return null; + }) .collect(Collectors.toList()); return new ROperation(operationType, pathHead, pathTail, operation.getExpression()); @@ -233,4 +246,8 @@ public RChoiceType buildRChoiceType(Choice choice) { public REnumType buildREnumType(RosettaEnumeration enumeration) { return new REnumType(enumeration, modelIdProvider, this); } + public RMetaAttribute buildRMetaAttribute(RosettaMetaType rosettaMetaType) { + return new RMetaAttribute(rosettaMetaType.getName(), typeSystem.typeCallToRType(rosettaMetaType.getTypeCall())); + } + } 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 09a13cfc0..5a3490540 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 @@ -24,10 +24,10 @@ public class ROperation { private final ROperationType rOperationType; private final RAssignedRoot pathHead; - private final List pathTail; + private final List pathTail; private final RosettaExpression expression; - public ROperation(ROperationType rOperationType, RAssignedRoot pathHead, List pathTail, RosettaExpression expression) { + public ROperation(ROperationType rOperationType, RAssignedRoot pathHead, List pathTail, RosettaExpression expression) { this.rOperationType = rOperationType; this.pathHead = pathHead; this.pathTail = pathTail; @@ -42,7 +42,7 @@ public RAssignedRoot getPathHead() { return pathHead; } - public List getPathTail() { + public List getPathTail() { return pathTail; } 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 c87fb04cc..4dff20eb0 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 @@ -2,6 +2,7 @@ package com.regnosys.rosetta.types import com.regnosys.rosetta.RosettaEcoreUtil import com.regnosys.rosetta.cache.IRequestScopedCache +import com.regnosys.rosetta.rosetta.simple.AssignPathRoot import com.regnosys.rosetta.rosetta.RosettaAttributeReference import com.regnosys.rosetta.rosetta.RosettaAttributeReferenceSegment import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs @@ -10,6 +11,7 @@ import com.regnosys.rosetta.rosetta.RosettaEnumValue import com.regnosys.rosetta.rosetta.RosettaEnumeration import com.regnosys.rosetta.rosetta.RosettaExternalFunction import com.regnosys.rosetta.rosetta.RosettaFeature +import com.regnosys.rosetta.rosetta.RosettaParameter import com.regnosys.rosetta.rosetta.RosettaRule import com.regnosys.rosetta.rosetta.RosettaSymbol import com.regnosys.rosetta.rosetta.RosettaTypedFeature @@ -56,6 +58,7 @@ import com.regnosys.rosetta.rosetta.expression.RosettaStringLiteral import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference import com.regnosys.rosetta.rosetta.expression.SortOperation import com.regnosys.rosetta.rosetta.expression.SumOperation +import com.regnosys.rosetta.rosetta.expression.SwitchCaseOrDefault import com.regnosys.rosetta.rosetta.expression.SwitchOperation import com.regnosys.rosetta.rosetta.expression.ThenOperation import com.regnosys.rosetta.rosetta.expression.ToDateOperation @@ -68,26 +71,26 @@ import com.regnosys.rosetta.rosetta.expression.ToTimeOperation import com.regnosys.rosetta.rosetta.expression.ToZonedDateTimeOperation import com.regnosys.rosetta.rosetta.simple.Annotated import com.regnosys.rosetta.rosetta.simple.AnnotationRef -import com.regnosys.rosetta.rosetta.simple.AssignPathRoot +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.ShortcutDeclaration import com.regnosys.rosetta.types.builtin.RBuiltinTypeService +import com.regnosys.rosetta.types.builtin.RNumberType +import com.regnosys.rosetta.types.builtin.RStringType import com.regnosys.rosetta.utils.ImplicitVariableUtil +import com.regnosys.rosetta.utils.OptionalUtil import com.regnosys.rosetta.utils.RosettaExpressionSwitch import java.math.BigInteger import java.util.List +import java.util.Map import java.util.Optional import javax.inject.Inject import javax.inject.Provider import org.eclipse.emf.ecore.EObject + import com.regnosys.rosetta.rosetta.expression.SwitchCaseOrDefault import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withMeta -import com.regnosys.rosetta.rosetta.RosettaParameter -import com.regnosys.rosetta.types.builtin.RStringType -import com.regnosys.rosetta.types.builtin.RNumberType -import com.regnosys.rosetta.utils.OptionalUtil -import java.util.Map import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.AnnotationPathExpression @@ -147,7 +150,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch getRMetaAttributesOfSymbol(RosettaSymbol symbol) { if (symbol instanceof Attribute) { if (symbol.isOverride) { @@ -173,7 +176,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch getRMetaAttributes(List annotations) { annotations .filter[it.annotation.name.equals("metadata") && it.attribute !== null] - .map[new RMetaAttribute(it.attribute.name, it.attribute.RTypeOfSymbol.RType, it.attribute)] + .map[new RMetaAttribute(it.attribute.name, it.attribute.RTypeOfSymbol.RType)] .toList } 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 95da69d85..8ca2d71ec 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 @@ -108,8 +108,10 @@ import com.regnosys.rosetta.rosetta.expression.ComparingFunctionalOperation import com.regnosys.rosetta.rosetta.expression.AsKeyOperation import com.regnosys.rosetta.rosetta.expression.ConstructorKeyValuePair import com.regnosys.rosetta.rosetta.expression.CanHandleListOfLists +import com.regnosys.rosetta.types.RMetaAttribute import com.regnosys.rosetta.utils.ImportManagementService import com.regnosys.rosetta.utils.ConstructorManagementService +import com.regnosys.rosetta.rosetta.RosettaMetaType // TODO: split expression validator // TODO: type check type call arguments @@ -134,7 +136,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Inject ImportManagementService importManagementService; @Inject ConstructorManagementService constructorManagementService; - + @Check def void deprecatedWarning(EObject object) { val crossRefs = (object.eClass.EAllStructuralFeatures as EClassImpl.FeatureSubsetSupplier).crossReferences as List @@ -1173,11 +1175,13 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { return } val segments = container.path.asSegmentList(container.path) - val attr = segments?.last?.attribute - if (!attr.hasReferenceAnnotation) { + val feature = segments?.last?.feature + if ((feature instanceof RosettaMetaType || !(feature as Attribute).hasReferenceAnnotation)) { error(''''«o.operator»' can only be used with attributes annotated with [metadata reference] annotation.''', o, ROSETTA_OPERATION__OPERATOR) } + + } else if (container instanceof ConstructorKeyValuePair) { val attr = container.key if (!(attr instanceof Attribute) || !(attr as Attribute).hasReferenceAnnotation) { @@ -1201,13 +1205,13 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } } } - - val unused = importManagementService.findUnused(model); + + val unused = importManagementService.findUnused(model); for (ns: unused) { warning('''Unused import «ns.importedNamespace»''', ns, IMPORT__IMPORTED_NAMESPACE, UNUSED_IMPORT) } - - val duplicates = importManagementService.findDuplicates(model.imports); + + val duplicates = importManagementService.findDuplicates(model.imports); for (imp : duplicates) { warning('''Duplicate import «imp.importedNamespace»''', imp, IMPORT__IMPORTED_NAMESPACE, DUPLICATE_IMPORT) } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java index 74f7023a8..55cb7a9a9 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/expression/ExpressionValidator.java @@ -58,6 +58,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; // TODO: move over expression validations from RosettaSimpleValidator @ComposedChecks(validators = { ConstructorValidator.class, SwitchValidator.class }) @@ -83,11 +84,19 @@ public void checkFunctionOperation(Operation op) { if (expr != null && cardinalityProvider.isOutputListOfLists(expr)) { error("Assign expression contains a list of lists, use flatten to create a list", op, OPERATION__EXPRESSION); } - List segments = op.pathAsSegmentList(); - AssignPathRoot attr = op.getPath() != null - ? segments.get(segments.size() - 1).getAttribute() + + //Any segments that are not symbols will be picked up by the syntax checker, this is to stop noisy errors + List segmentsAsSymbols = op.pathAsSegmentList() + .stream() + .map(s -> s.getFeature()) + .filter(f -> f instanceof RosettaSymbol) + .map(f -> (RosettaSymbol)f) + .collect(Collectors.toList()); + + RosettaSymbol attr = op.getPath() != null && !segmentsAsSymbols.isEmpty() + ? segmentsAsSymbols.get(segmentsAsSymbols.size() - 1) : op.getAssignRoot(); - subtypeCheck(typeProvider.getRTypeOfSymbol(attr), expr, op, OPERATION__EXPRESSION, actual -> "Cannot assign `" + actual + "` to output `" + attr.getName() + "`"); + subtypeCheck(typeProvider.getRTypeOfSymbol(attr, null), expr, op, OPERATION__EXPRESSION, actual -> "Cannot assign `" + actual + "` to output `" + attr.getName() + "`"); boolean isList = cardinalityProvider.isSymbolMulti(attr); if (op.isAdd() && !isList) { error("`add` must be used with a list", op, OPERATION__ASSIGN_ROOT);