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 index a7ea966e2..f10ef0e51 100644 --- 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 @@ -1,5 +1,16 @@ package com.regnosys.rosetta.generator.java.function; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Map; + +import javax.inject.Inject; + +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 com.regnosys.rosetta.generator.java.RosettaJavaPackages; import com.regnosys.rosetta.tests.RosettaTestInjectorProvider; import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper; @@ -8,16 +19,6 @@ 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) @@ -185,12 +186,9 @@ result string (1..1) assertEquals(expected, result); } - - @Disabled //TODO: is this syntax needed? if so we need to find a way to get key as a feature from a Data type @Test void canSetExternalKeyOnFunctionObjectOutput() { var model = """ - metaType key string metaType reference string @@ -204,10 +202,25 @@ myReference string (1..1) output: result Foo (1..1) + set result -> a: "someA" set result -> key: myReference """; var code = generatorTestHelper.generateCode(model); + + generatorTestHelper.writeClasses(code, "canSetExternalKeyOnFunctionObjectOutput"); + + var classes = generatorTestHelper.compileToClasses(code); + var myFunc = functionGeneratorHelper.createFunc(classes, "MyFunc"); + + var result = functionGeneratorHelper.invokeFunc(myFunc, RosettaModelObject.class, "someExternalReference"); + + var expected = generatorTestHelper.createInstanceUsingBuilder(classes, new RosettaJavaPackages.RootPackage("com.rosetta.test.model"), "Foo", Map.of( + "a", "someA", + "meta", MetaFields.builder().setExternalKey("someExternalReference") + )); + + assertEquals(expected, result); } @Test 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 8ac167b82..9bb869081 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend @@ -31,6 +31,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 +import com.regnosys.rosetta.types.RMetaAttribute @Singleton // see `metaFieldsCache` class RosettaEcoreUtil { @@ -80,6 +81,30 @@ class RosettaEcoreUtil { } } + /* + * This method is resolving references during scoping which is not an advised approach. + * It could lead to poor performance as it is possible that it could be called upon to + * resolve across multiple files. For now this is acceptable as in reality it's not going + * going to get called to run across multiple files. + * + * TODO: find an alternative approach to this. + * + */ + def List getMetaDescriptions(List metaAttributes, EObject context) { + val metas = metaAttributes.map[it.name].toList + if (!metas.isEmpty) { + configs.findMetaTypes(context).filter[ + metas.contains(it.name.lastSegment.toString) + ] + .map[it.EObjectOrProxy] + .map[EcoreUtil.resolve(it, context)] + .filter(RosettaFeature) + .toList + } else { + emptyList + } + } + @Deprecated // Use RDataType#getAllSuperTypes instead def List getAllSuperTypes(Data data) { val reversedResult = newLinkedHashSet @@ -243,28 +268,8 @@ class RosettaEcoreUtil { return '''«containerName»«name»''' } - /* - * This method is resolving references during scoping which is not an advised approach. - * It could lead to poor performance as it is possible that it could be called upon to - * resolve across multiple files. For now this is acceptable as in reality it's not going - * going to get called to run across multiple files. - * - * TODO: find an alternative approach to this. - * - */ private def List getMetaDescriptions(RMetaAnnotatedType type, EObject context) { - val metas = type.metaAttributes.map[it.name].toList - if (!metas.isEmpty) { - configs.findMetaTypes(context).filter[ - metas.contains(it.name.lastSegment.toString) - ] - .map[it.EObjectOrProxy] - .map[EcoreUtil.resolve(it, context)] - .filter(RosettaFeature) - .toList - } else { - emptyList - } + type.metaAttributes.getMetaDescriptions(context) } @Deprecated 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 8caaf0c07..f8df4f7ba 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 @@ -78,6 +78,8 @@ import static com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil.* import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta import com.regnosys.rosetta.generator.java.types.RJavaReferenceWithMeta +import com.regnosys.rosetta.types.RDataType +import com.regnosys.rosetta.generator.java.types.RJavaPojoInterface class FunctionGenerator { @@ -496,7 +498,7 @@ class FunctionGenerator { } private def String getPropertySetterName(JavaType outputExpressionType, JavaPojoProperty prop, RFeature segment) { - if (outputExpressionType instanceof RJavaWithMetaValue) { + if (outputExpressionType instanceof RJavaWithMetaValue || (segment instanceof RMetaAttribute && outputExpressionType instanceof RJavaPojoInterface)) { segment.toPojoPropertyNames.toFirstUpper } else { prop.name.toFirstUpper @@ -520,17 +522,18 @@ class FunctionGenerator { RJavaFieldWithMeta: '''«IF seg instanceof RMetaAttribute».getOrCreateMeta()«ELSE».«prop.getOrCreateName»()«ENDIF»''' RJavaReferenceWithMeta case seg instanceof RMetaAttribute && seg.name == "address": '''.«prop.getOrCreateName»()''' RJavaReferenceWithMeta case !(seg instanceof RMetaAttribute): '''.getOrCreateValue()''' + RJavaPojoInterface case seg instanceof RMetaAttribute: '''.«prop.getOrCreateName»()''' default: '''''' } } //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 getPojoProperty(RFeature seg, JavaType outputExpressionType) { - if (seg instanceof RMetaAttribute && outputExpressionType.itemType instanceof RJavaFieldWithMeta) { + if (seg instanceof RMetaAttribute && (outputExpressionType instanceof RJavaFieldWithMeta || outputExpressionType instanceof RJavaPojoInterface)) { (outputExpressionType as JavaPojoInterface).findProperty("meta") - } else if (seg instanceof RMetaAttribute && outputExpressionType.itemType instanceof RJavaReferenceWithMeta) { + } else if (seg instanceof RMetaAttribute && outputExpressionType instanceof RJavaReferenceWithMeta) { (outputExpressionType as JavaPojoInterface).findProperty("reference") - } else if (outputExpressionType.itemType instanceof RJavaWithMetaValue) { + } else if (outputExpressionType instanceof RJavaWithMetaValue) { (outputExpressionType as JavaPojoInterface).findProperty("value") } else { (outputExpressionType as JavaPojoInterface).findProperty(seg.name) @@ -541,6 +544,7 @@ class FunctionGenerator { return switch(seg.name) { case "reference": "externalReference" case "id": "externalKey" + case "key": "externalKey" case "address": "reference" default: seg.name } 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 f82552f91..dded28dd9 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 @@ -66,6 +66,8 @@ import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withNoMeta import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.AnnotationPath import com.regnosys.rosetta.rosetta.simple.AnnotationDeepPath +import java.util.Random +import com.regnosys.rosetta.types.RAttribute /** * This class contains custom scoping description. @@ -146,7 +148,16 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { // Case handles the head of the segment Operation: { val receiverType = typeProvider.getRTypeOfSymbol(context.assignRoot) - return Scopes.scopeFor(receiverType.allFeatures(context, [t| !(t instanceof REnumType)])) + + // All features accessible from reciever type including meta attributes + var features = receiverType.allFeatures(context, [t| !(t instanceof REnumType)]) + + // We also want to allow the scope provider to return the meta for type of the attribute (e.g. metatda key) + if (receiverType.RType instanceof RDataType) { + features = features + (receiverType.RType as RDataType).metaAttributes.getMetaDescriptions(context) + } + + return Scopes.scopeFor(features) } // Case handles the tail of the segment Segment: {