diff --git a/docs/rune-modelling-component.md b/docs/rune-modelling-component.md index 06ccbc2dc..35a0cd972 100644 --- a/docs/rune-modelling-component.md +++ b/docs/rune-modelling-component.md @@ -425,6 +425,33 @@ Each attribute of the `metadata` annotation corresponds to a different qualifier - The `template` qualifier indicates that a data type is eligible to be used as a [data template](#data-template). Data templates provide a way to store data which may be duplicated across multiple objects into a single template, to be referenced by all these objects. - the other metadata annotations are used for [cross-referencing](#cross-referencing). +#### Meta-Data Use In Functions And Expressions + +It is possible to use `metadata` annotated function inputs and expressions outputs to access the value of a piece of metadata. The following is an example of using `metadata` that has been passed into a function: + +```Haskell +func MyFunc: + inputs: + myInput string (1..1) + [metadata scheme] + output: + myResult string (1..1) + + set myResult: myInput -> scheme +``` + +Additionally here is an example of how to work with metadata that is the output of an expression: + +```Haskell +func MyFunc: + inputs: + myInput string (1..*) + [metadata scheme] + output: + myResult string (1..*) + set myResult: myInput extract scheme +``` + ### Document Reference #### Purpose diff --git a/rosetta-ide/rosetta.tmLanguage.yaml b/rosetta-ide/rosetta.tmLanguage.yaml index cce98bd30..81e65de19 100644 --- a/rosetta-ide/rosetta.tmLanguage.yaml +++ b/rosetta-ide/rosetta.tmLanguage.yaml @@ -1038,19 +1038,22 @@ repository: - include: '#documentationFollowedByExpression' expression: - include: '#parameterizedExpression' - arguments: - extraEnd: '' + patterns: + - include: '#parameterizedExpression' + arguments: + extraEnd: '' expressionWithoutThenOperation: - include: '#parameterizedExpression' - arguments: - extraEnd: '(?={{wordStart}}then{{wordEnd}})|' + patterns: + - include: '#parameterizedExpression' + arguments: + extraEnd: '(?={{wordStart}}then{{wordEnd}})|' expressionWithoutThenAndDefaultOperation: - include: '#parameterizedExpression' - arguments: - extraEnd: '(?={{wordStart}}then|default{{wordEnd}})|' + patterns: + - include: '#parameterizedExpression' + arguments: + extraEnd: '(?={{wordStart}}then|default{{wordEnd}})|' parameterizedExpression: parameters: ['extraEnd'] @@ -1167,7 +1170,7 @@ repository: - name: meta.switch-operation.rosetta begin: '{{wordStart}}switch{{wordEnd}}' beginCaptures: - 0: { name: keyword.operator.word.rosetta } + 0: { name: keyword.control.conditional.switch.rosetta } end: '{{extraEnd}}{{expressionEndIgnoringComma}}' patterns: - include: '#comment' @@ -1370,4 +1373,4 @@ repository: comma: name: punctuation.separator.comma.rosetta - match: ',' + match: ',' \ No newline at end of file diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.java index e285c38f4..5a03e02af 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/hover/RosettaDocumentationProvider.java @@ -57,7 +57,7 @@ public List getDocumentationFromOwner(EObject o) { if (o instanceof RosettaSymbolReference) { RosettaSymbol symbol = ((RosettaSymbolReference)o).getSymbol(); if (symbol instanceof RosettaEnumValue) { - RType t = expectedTypeProvider.getExpectedTypeFromContainer(o); + RType t = expectedTypeProvider.getExpectedTypeFromContainer(o).getRType(); docs.add(t.toString()); } } diff --git a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/inlayhints/RosettaInlayHintsService.java b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/inlayhints/RosettaInlayHintsService.java index bf66695d1..ff91cc738 100644 --- a/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/inlayhints/RosettaInlayHintsService.java +++ b/rosetta-ide/src/main/java/com/regnosys/rosetta/ide/inlayhints/RosettaInlayHintsService.java @@ -58,7 +58,7 @@ public InlayHint checkFunctionalOperation(RosettaFunctionalOperation op) { if (op.getFunction() != null && operationHasBrackets(op.getFunction())) { if (op instanceof ReduceOperation || op instanceof MapOperation) { if (extensions.isResolved(op.getFunction())) { - RType outputType = types.getRType(op); + RType outputType = types.getRMetaAnnotatedType(op).getRType(); boolean outputMulti = card.isMulti(op); if (outputType != null) { 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 1594bde78..3f5dbdc58 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 @@ -54,7 +54,7 @@ import com.regnosys.rosetta.rosetta.simple.Segment; import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration; import com.regnosys.rosetta.types.CardinalityProvider; -import com.regnosys.rosetta.types.RType; +import com.regnosys.rosetta.types.RMetaAnnotatedType; import com.regnosys.rosetta.types.RosettaTypeProvider; import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.*; @@ -263,7 +263,7 @@ public SemanticToken markFeature(RosettaFeatureCall featureCall) { private SemanticToken markSymbol(EObject objectToMark, EStructuralFeature featureToMark, RosettaSymbol symbol) { if (symbol instanceof Attribute) { - RType implicitType = typeProvider.typeOfImplicitVariable(objectToMark); + RMetaAnnotatedType implicitType = typeProvider.typeOfImplicitVariable(objectToMark); if (implicitType != null) { Set implicitFeatures = Sets.newHashSet(extensions.allFeatures(implicitType, objectToMark)); if (implicitFeatures.contains(symbol)) { 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 30ba4a4c3..1235a382d 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 @@ -287,6 +287,11 @@ private void validatePattern(TmValue pattern, Map namedP // - begin/while // - list of patterns if (tmMap.value.get("include") != null) { + if (!tmMap.path.get(tmMap.path.size() - 1).equals("patterns")) { + // Note: this check is only necessary for Monaco. See https://github.com/zikaari/monaco-textmate/issues/13. + // VS Code supports direct includes. + throw new ConfigurationException("Validation failed on include: may only be used inside 'patterns'. " + tmMap.getPath()); + } runValidators(tmMap, Map.of("include", include, "comment", comment, "repository", repository)); } else if (tmMap.value.get("match") != null) { runValidators(tmMap, Map.of("name", scopes, "match", regex, "captures", captures, "comment", comment, "repository", repository)); diff --git a/rosetta-lang/model/Rosetta.xcore b/rosetta-lang/model/Rosetta.xcore index 2ed692bde..90d00c185 100644 --- a/rosetta-lang/model/Rosetta.xcore +++ b/rosetta-lang/model/Rosetta.xcore @@ -126,7 +126,7 @@ class RosettaTypeAlias extends RosettaRootElement, RosettaType, RosettaTyped, Ro } -class RosettaMetaType extends RosettaRootElement, RosettaTypedFeature, RosettaType { +class RosettaMetaType extends RosettaRootElement, RosettaTypedFeature, RosettaType, RosettaSymbol { } 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 0308b0436..126b9266a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/RosettaEcoreUtil.xtend @@ -2,6 +2,7 @@ package com.regnosys.rosetta import com.google.common.base.CaseFormat import com.regnosys.rosetta.rosetta.RosettaEnumeration +import com.regnosys.rosetta.rosetta.RosettaFactory import com.regnosys.rosetta.rosetta.RosettaFeature import com.regnosys.rosetta.rosetta.RosettaRecordType import com.regnosys.rosetta.rosetta.RosettaSynonym @@ -12,41 +13,48 @@ 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 +import com.regnosys.rosetta.rosetta.simple.SimpleFactory +import com.regnosys.rosetta.scoping.RosettaScopeProvider 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.RMetaAnnotatedType +import com.regnosys.rosetta.types.RObjectFactory import com.regnosys.rosetta.types.RType +import com.regnosys.rosetta.types.builtin.RBuiltinTypeService +import com.regnosys.rosetta.types.builtin.RRecordType +import com.regnosys.rosetta.utils.PositiveIntegerInterval +import com.regnosys.rosetta.utils.RosettaConfigExtension import java.util.Collection +import java.util.LinkedHashSet +import java.util.List import java.util.Set import javax.inject.Inject +import javax.inject.Singleton import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.resource.ResourceSet - -import com.regnosys.rosetta.types.builtin.RRecordType -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import javax.inject.Singleton -import java.util.List -import com.regnosys.rosetta.utils.PositiveIntegerInterval import org.eclipse.xtext.util.SimpleCache -import com.regnosys.rosetta.rosetta.RosettaFactory -import com.regnosys.rosetta.scoping.RosettaScopeProvider -import com.regnosys.rosetta.rosetta.simple.SimpleFactory -import com.regnosys.rosetta.types.RObjectFactory -import java.util.LinkedHashSet -import com.regnosys.rosetta.types.TypeSystem -import com.regnosys.rosetta.types.RChoiceType + +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withMeta +import org.eclipse.emf.ecore.util.EcoreUtil @Singleton // see `metaFieldsCache` class RosettaEcoreUtil { @Inject RBuiltinTypeService builtins - @Inject RObjectFactory objectFactory - @Inject extension TypeSystem typeSystem + @Inject extension RObjectFactory objectFactory + @Inject extension RosettaConfigExtension configs def boolean isResolved(EObject obj) { obj !== null && !obj.eIsProxy } + def Iterable allFeatures(RMetaAnnotatedType t, EObject context) { + val List metas = getMetaDescriptions(t, context) + allFeatures(t.RType, context?.eResource?.resourceSet) + metas + } + def Iterable allFeatures(RType t, EObject context) { allFeatures(t, context?.eResource?.resourceSet) } @@ -134,10 +142,6 @@ class RosettaEcoreUtil { metaAnnotations.exists[attribute?.name == "template"] } - def boolean hasMetaDataAnnotations(RAttribute attribute) { - attribute.metaAnnotations.exists[name == "reference" || name == "location" || name == "scheme" || name == "id"] - } - def boolean hasMetaDataAnnotations(Annotated it) { metaAnnotations.exists[attribute?.name == "reference" || attribute?.name == "location" || attribute?.name == "scheme" || attribute?.name == "id"] } @@ -146,8 +150,12 @@ class RosettaEcoreUtil { metaAnnotations.exists[attribute?.name != "reference" && attribute?.name != "address"] } + def boolean hasMetaDataReference(RAttribute attribute) { + attribute.RMetaAnnotatedType.getMetaAttributes.exists[name == "reference"] + } + def boolean hasMetaDataAddress(RAttribute attribute) { - attribute.metaAnnotations.exists[name == "address"] + attribute.RMetaAnnotatedType.getMetaAttributes.exists[name == "address"] } def boolean hasMetaDataAddress(Annotated it) { @@ -158,7 +166,7 @@ class RosettaEcoreUtil { metaAnnotations.exists[attribute?.name == "id"] } def boolean hasIdAnnotation(RAttribute it) { - metaAnnotations.exists[name == "id"] + RMetaAnnotatedType.getMetaAttributes.exists[name == "id"] } def boolean hasReferenceAnnotation(Annotated it) { metaAnnotations.exists[attribute?.name == "reference"] @@ -167,11 +175,8 @@ class RosettaEcoreUtil { allAnnotations.exists[annotation?.name == "calculation"] } - def boolean isReference(Attribute attribute) { - return attribute.hasMetaDataAnnotations || attribute.hasMetaDataAddress - } def boolean isReference(RAttribute attribute) { - return attribute.hasMetaDataAnnotations || attribute.hasMetaDataAddress + return attribute.hasMetaDataReference || attribute.hasMetaDataAddress } def private allAnnotations(Annotated withAnnotations) { @@ -222,6 +227,30 @@ 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 + } + } + @Deprecated def String toConditionJavaType(String conditionName) { val allUnderscore = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, conditionName) @@ -231,9 +260,9 @@ class RosettaEcoreUtil { @Deprecated def String javaAnnotation(RAttribute attr) { - if (attr.name == "key" && attr.RType.name == "Key" && attr.RType.namespace.toString == "com.rosetta.model.lib.meta") { + if (attr.name == "key" && attr.RMetaAnnotatedType.RType.name == "Key" && attr.RMetaAnnotatedType.RType.namespace.toString == "com.rosetta.model.lib.meta") { return 'location' - } else if (attr.name == "reference" && attr.RType.name == "Reference" && attr.RType.namespace.toString == "com.rosetta.model.lib.meta") { + } else if (attr.name == "reference" && attr.RMetaAnnotatedType.RType.name == "Reference" && attr.RMetaAnnotatedType.RType.namespace.toString == "com.rosetta.model.lib.meta") { return 'address' } else return attr.name @@ -247,8 +276,7 @@ class RosettaEcoreUtil { 'meta', null, emptyList, - provideMetaFieldsType(t), - emptyList, + provideMetaFieldsType(t).withMeta(#[]), PositiveIntegerInterval.bounded(0, 1), null, null diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend index 3374281b2..3c4037bdf 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/expression/DeepPathUtilGenerator.xtend @@ -24,6 +24,7 @@ import java.util.HashSet import com.regnosys.rosetta.generator.java.statement.builder.JavaVariable import com.regnosys.rosetta.types.RAttribute import com.regnosys.rosetta.types.RChoiceType +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.* class DeepPathUtilGenerator { @Inject extension ImportManagerExtension @@ -54,7 +55,7 @@ class DeepPathUtilGenerator { val deepFeatures = choiceType.findDeepFeatures val dependencies = new HashSet>() val recursiveDeepFeaturesMap = choiceType.allNonOverridenAttributes.toMap([it], [ - val attrType = it.RType + val attrType = it.RMetaAnnotatedType.RType deepFeatures.toMap([it], [ var t = attrType if (t instanceof RChoiceType) { @@ -98,13 +99,12 @@ class DeepPathUtilGenerator { } private def JavaStatementBuilder deepFeatureToStatement(RDataType choiceType, JavaVariable inputParameter, RAttribute deepFeature, Map> recursiveDeepFeaturesMap, JavaScope scope) { - val deepFeatureHasMeta = !deepFeature.metaAnnotations.empty val attrs = choiceType.allNonOverridenAttributes.toList var JavaStatementBuilder acc = JavaExpression.NULL for (a : attrs.reverseView) { val currAcc = acc acc = inputParameter - .featureCall(choiceType, a, false, scope, true) + .attributeCall(choiceType.withEmptyMeta, a, false, scope) .declareAsVariable(true, a.name.toFirstLower, scope) .mapExpression[attrVar| attrVar.exists(ExistsModifier.NONE, scope) @@ -114,7 +114,8 @@ class DeepPathUtilGenerator { val deepFeatureExpr = if (deepFeature.match(a)) { attrVar } else { - var attrType = a.RType + val metaRType = a.RMetaAnnotatedType + var attrType = metaRType.RType if (attrType instanceof RChoiceType) { attrType = attrType.asRDataType } @@ -124,7 +125,7 @@ class DeepPathUtilGenerator { } else { (attrType as RDataType).allNonOverridenAttributes.findFirst[name.equals(deepFeature.name)] } - attrVar.featureCall(attrType, actualFeature, needsToGoDownDeeper, scope, !deepFeatureHasMeta) + attrVar.attributeCall(metaRType, actualFeature, needsToGoDownDeeper, scope) } new JavaIfThenElseBuilder(it, deepFeatureExpr, currAcc, typeUtil) ] 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 3426aeb33..8580199e9 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 @@ -3,6 +3,7 @@ package com.regnosys.rosetta.generator.java.expression import com.regnosys.rosetta.RosettaEcoreUtil import com.regnosys.rosetta.generator.java.JavaIdentifierRepresentationService import com.regnosys.rosetta.generator.java.JavaScope +import com.regnosys.rosetta.generator.java.statement.JavaLocalVariableDeclarationStatement import com.regnosys.rosetta.generator.java.statement.builder.JavaConditionalExpression import com.regnosys.rosetta.generator.java.statement.builder.JavaExpression import com.regnosys.rosetta.generator.java.statement.builder.JavaIfThenElseBuilder @@ -71,6 +72,7 @@ import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference import com.regnosys.rosetta.rosetta.expression.RosettaUnaryOperation import com.regnosys.rosetta.rosetta.expression.SortOperation import com.regnosys.rosetta.rosetta.expression.SumOperation +import com.regnosys.rosetta.rosetta.expression.SwitchCase import com.regnosys.rosetta.rosetta.expression.SwitchOperation import com.regnosys.rosetta.rosetta.expression.ThenOperation import com.regnosys.rosetta.rosetta.expression.ToDateOperation @@ -82,20 +84,24 @@ 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.simple.Attribute +import com.regnosys.rosetta.rosetta.simple.ChoiceOption 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.CardinalityProvider import com.regnosys.rosetta.types.RAttribute +import com.regnosys.rosetta.types.RChoiceOption +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.RObjectFactory import com.regnosys.rosetta.types.RShortcut -import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.RosettaOperators 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.RRecordType import com.regnosys.rosetta.utils.ExpressionHelper import com.regnosys.rosetta.utils.ImplicitVariableUtil @@ -107,6 +113,7 @@ import com.rosetta.model.lib.mapper.MapperC import com.rosetta.model.lib.mapper.MapperS import com.rosetta.model.lib.records.Date import com.rosetta.model.lib.validation.ChoiceRuleValidationMethod +import com.rosetta.util.types.JavaClass import com.rosetta.util.types.JavaGenericTypeDeclaration import com.rosetta.util.types.JavaPrimitiveType import com.rosetta.util.types.JavaType @@ -127,19 +134,12 @@ import org.apache.commons.text.StringEscapeUtils import org.eclipse.emf.ecore.EObject import org.eclipse.xtend2.lib.StringConcatenationClient import org.eclipse.xtext.EcoreUtil2 +import org.eclipse.xtext.xbase.lib.Functions.Function3 import static extension com.regnosys.rosetta.generator.java.enums.EnumHelper.convertValue -import com.regnosys.rosetta.rosetta.expression.SwitchCase -import com.regnosys.rosetta.types.RChoiceType -import com.regnosys.rosetta.types.builtin.RBasicType -import com.regnosys.rosetta.rosetta.expression.SwitchCaseGuard -import java.util.function.BiFunction -import com.regnosys.rosetta.generator.java.statement.JavaAssignment -import com.regnosys.rosetta.generator.java.statement.JavaLocalVariableDeclarationStatement -import com.regnosys.rosetta.rosetta.simple.ChoiceOption -import com.regnosys.rosetta.types.RChoiceOption -import org.eclipse.xtext.xbase.lib.Functions.Function2 -import org.eclipse.xtext.xbase.lib.Functions.Function3 +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta +import com.regnosys.rosetta.generator.java.types.RJavaFieldWithMeta +import com.regnosys.rosetta.generator.java.types.RJavaWithMetaValue class ExpressionGenerator extends RosettaExpressionSwitch { @@ -210,11 +210,11 @@ class ExpressionGenerator extends RosettaExpressionSwitchmap("«feature.name.toFirstUpper»", «recordUtil.recordFeatureToLambda(receiverType as RRecordType, feature, scope)»)''' - default: - throw new UnsupportedOperationException("Unsupported feature type of " + feature?.class?.name) - } + val StringConcatenationClient right = feature.buildMapFunc(scope) val mapperReceiverCode = typeCoercionService.addCoercions(receiverCode, MAPPER.wrapExtends(receiverCode.expressionType.itemType), scope) - val resultWrapper = if (mapperReceiverCode.expressionType.isMapperS && !cardinalityProvider.isFeatureMulti(feature)) { - MAPPER_S as JavaGenericTypeDeclaration - } else { - MAPPER_C as JavaGenericTypeDeclaration - } - val resultType = resultWrapper.wrap(resultItemType) - return mapperReceiverCode - .collapseToSingleExpression(scope) - .mapExpression[JavaExpression.from('''«it»«right»''', resultType)] + featureCall(mapperReceiverCode, resultItemType, right, receiverCode, receiverType, feature, scope) } - def JavaStatementBuilder featureCall(JavaStatementBuilder receiverCode, RType receiverType, RAttribute attr, boolean isDeepFeature, JavaScope scope, boolean autoValue) { - val resultItemType = if (!autoValue) { - attr.toMetaItemJavaType - } else { - attr.toItemJavaType - } - val StringConcatenationClient right = receiverType.buildMapFunc(attr, isDeepFeature, autoValue, scope) - val mapperReceiverCode = typeCoercionService.addCoercions(receiverCode, MAPPER.wrapExtends(receiverCode.expressionType.itemType), scope) - val resultWrapper = if (mapperReceiverCode.expressionType.isMapperS && !attr.isMulti) { + + def JavaStatementBuilder recordCall(JavaStatementBuilder receiverCode, RMetaAnnotatedType receiverType, RosettaRecordFeature feature, JavaScope scope) { + val resultItemType = typeProvider.getRTypeOfFeature(feature, null).toJavaReferenceType + val StringConcatenationClient right = '''.<«resultItemType»>map("«feature.name.toFirstUpper»", «recordUtil.recordFeatureToLambda(receiverType.RType as RRecordType, feature, scope)»)''' + val mapperReceiverCode = typeCoercionService.addCoercions(receiverCode, MAPPER.wrapExtendsWithoutMeta(receiverCode.expressionType.itemType), scope) + featureCall(mapperReceiverCode, resultItemType, right, receiverCode, receiverType, feature, scope) + } + + def JavaStatementBuilder attributeCall(JavaStatementBuilder receiverCode, RMetaAnnotatedType receiverType, RAttribute attr, boolean isDeepFeature, JavaScope scope) { + val resultItemType = attr.toMetaItemJavaType + + val StringConcatenationClient right = receiverType.buildMapFunc(attr, isDeepFeature, scope) + val mapperReceiverCode = typeCoercionService.addCoercions(receiverCode, MAPPER.wrapExtendsWithoutMeta(receiverCode.expressionType.itemType), scope) + featureCall(mapperReceiverCode, resultItemType, right, receiverCode, receiverType, attr.EObject, scope) + } + + private def JavaStatementBuilder featureCall(JavaStatementBuilder mapperReceiverCode, JavaClass resultItemType, StringConcatenationClient right, JavaStatementBuilder receiverCode, RMetaAnnotatedType receiverType, RosettaFeature feature, JavaScope scope) { + val resultWrapper = if (mapperReceiverCode.expressionType.isMapperS && !cardinalityProvider.isFeatureMulti(feature)) { MAPPER_S as JavaGenericTypeDeclaration } else { MAPPER_C as JavaGenericTypeDeclaration @@ -335,10 +327,10 @@ class ExpressionGenerator extends RosettaExpressionSwitchmapC(«mapFunc»)''' - else { - '''.<«resultType»>mapC(«mapFunc»).<«attribute.toItemJavaType»>map("getValue", _f->_f.getValue())''' - } + '''.<«resultType»>mapC(«mapFunc»)''' } else { - if (attribute.metaAnnotations.nullOrEmpty || !autoValue) { - '''.<«resultType»>map(«mapFunc»)''' - } else - '''.<«resultType»>map(«mapFunc»).<«attribute.toItemJavaType»>map("getValue", _f->_f.getValue())''' + '''.<«resultType»>map(«mapFunc»)''' } } @@ -489,8 +474,8 @@ class ExpressionGenerator extends RosettaExpressionSwitch usedAttributes, Necessity validationType, Context context) { - val argItemType = typeProvider.getRType(arg).toJavaReferenceType - arg.javaCode(MAPPER.wrapExtends(argItemType), context.scope) + val argItemType = typeProvider.getRMetaAnnotatedType(arg).RType.toJavaReferenceType + arg.javaCode(MAPPER.wrapExtendsWithoutMeta(argItemType), context.scope) .collapseToSingleExpression(context.scope) .mapExpression[JavaExpression.from('''«runtimeMethod('choice')»(«it», «Arrays».asList(«usedAttributes.join(", ")['"' + name + '"']»), «ChoiceRuleValidationMethod».«validationType.name()»)''', COMPARISON_RESULT)] } @@ -506,10 +491,15 @@ class ExpressionGenerator extends RosettaExpressionSwitch JavaType argumentAndBodyTypeToReturnType, JavaScope scope) { + private def JavaStatementBuilder buildSingleItemListOperationOptionalBody(RosettaFunctionalOperation op, String name, JavaType expectedArgumentType, JavaType expectedBodyType, (JavaType, JavaType) => JavaType argumentAndBodyTypeToReturnType, boolean autoUnwrapMeta, JavaScope scope) { if (op.function === null) { - buildListOperationNoBody(op, name, expectedArgumentType, [argumentAndBodyTypeToReturnType.apply(it, null)], scope) + if (autoUnwrapMeta && expectedArgumentType.itemType instanceof RJavaFieldWithMeta) { + buildUnwrappingListOperation(op, name, expectedArgumentType, expectedArgumentType.itemType as RJavaFieldWithMeta, expectedBodyType, argumentAndBodyTypeToReturnType, scope) + } else { + buildListOperationNoBody(op, name, expectedArgumentType, [argumentAndBodyTypeToReturnType.apply(it, null)], scope) + } } else { buildSingleItemListOperation(op, name, expectedArgumentType, expectedBodyType, argumentAndBodyTypeToReturnType, scope) } @@ -529,14 +519,31 @@ class ExpressionGenerator extends RosettaExpressionSwitch JavaType argumentAndBodyTypeToReturnType, JavaScope scope) { + val argCode = op.argument.javaCode(expectedArgumentType, scope) + .collapseToSingleExpression(scope) + val lambdaPara = new JavaVariable(scope.createUniqueIdentifier("lambdaParam"), MAPPER_S.wrap(expectedItemType)) + val unwrapCoerceon = typeCoercionService.addCoercions(lambdaPara, MAPPER_S.wrap(expectedItemType.valueType), scope) + + argCode + .mapExpression[JavaExpression.from( + ''' + «it» + .«name»(«lambdaPara» -> «unwrapCoerceon.toLambdaBody»)''', + argumentAndBodyTypeToReturnType.apply(argCode.expressionType, expectedItemType.valueType) + )] + } - private def StringConcatenationClient buildMapFuncAttribute(RType itemType, RAttribute attribute, boolean isDeepFeature, JavaScope scope) { + private def StringConcatenationClient buildMapFuncAttribute(RMetaAnnotatedType itemType, RAttribute attribute, boolean isDeepFeature, JavaScope scope) { val lambdaScope = scope.lambdaScope - val lambdaParam = lambdaScope.createUniqueIdentifier(itemType.name.toFirstLower) - val t = if (itemType instanceof RChoiceType) { - itemType.asRDataType + val itemRType = itemType.RType + val lambdaParam = lambdaScope.createUniqueIdentifier(itemRType.name.toFirstLower) + val t = if (itemRType instanceof RChoiceType) { + itemRType.asRDataType } else { - itemType + itemRType } if (isDeepFeature) { '''"choose«attribute.name.toFirstUpper»", «lambdaParam» -> «scope.getIdentifierOrThrow((t as RDataType).toDeepPathUtilJavaClass.toDependencyInstance)».choose«attribute.name.toFirstUpper»(«lambdaParam»)''' @@ -666,7 +673,7 @@ class ExpressionGenerator extends RosettaExpressionSwitch «LocalTime».parse(s, «DateTimeFormatter».ISO_LOCAL_TIME)''', + context, '''«lambdaParam» -> «LocalTime».parse(«lambdaParam», «DateTimeFormatter».ISO_LOCAL_TIME)''', DateTimeParseException) } override protected caseConstructorExpression(RosettaConstructorExpression expr, Context context) { - val t = typeProvider.getRType(expr).stripFromTypeAliases - val type = if (t instanceof RChoiceType) { - t.asRDataType + val metaAnnotatedType = typeProvider.getRMetaAnnotatedType(expr) + val rType = metaAnnotatedType.RType.stripFromTypeAliases + val type = if (rType instanceof RChoiceType) { + rType.asRDataType } else { - t + rType } - val clazz = type.toJavaReferenceType + val clazz = metaAnnotatedType.toJavaReferenceType if (type instanceof RDataType) { if (expr.values.empty) { JavaExpression.from(''' @@ -1127,11 +1132,11 @@ class ExpressionGenerator extends RosettaExpressionSwitch` to `ComparisonResult` * - `Void` to `LocalDate` * - `Void` to `MapperC` + * - `FieldWithMetaString` to `String` + * - `String` to `FieldWithMetaString` + * * Item to item coercions and item to wrapper coercions are performed null-safe. * * This service is auto-boxing aware. If the expected type is a wrapper class of a primitive type @@ -97,8 +104,9 @@ class TypeCoercionService { // - if it is null, return null, // - otherwise, convert variable to expected type. - getItemConversion(actual, expected) + getItemConversion(actual, expected, scope) .map[itemConversion| + convertNullSafe( expr, itemConversion, @@ -121,19 +129,19 @@ class TypeCoercionService { // Exception: wrapping to a MapperS or MapperC is null safe, so no need to do a null check. if (expected.extendsMapper) { - getItemConversion(actual, expectedItemType) + getItemConversion(actual, expectedItemType, scope) .map[itemConversion| convertNullSafe( expr, - wrapConversion.compose(itemConversion), + itemConversion.andThen[mapExpression(wrapConversion)], expected, scope ) ].orElse(expr.mapExpression(wrapConversion)) } else { - val totalConversion = getItemConversion(actual, expectedItemType) + val totalConversion = getItemConversion(actual, expectedItemType, scope) .map[itemConversion| - wrapConversion.compose(itemConversion) + itemConversion.andThen[mapExpression(wrapConversion)] as Function ].orElse(wrapConversion) convertNullSafe( @@ -191,7 +199,7 @@ class TypeCoercionService { return totalConversion.apply(expr) } - private def Optional> getItemConversion(JavaType actual, JavaType expected) { + private def Optional> getItemConversion(JavaType actual, JavaType expected, JavaScope scope) { if (actual == expected) { return Optional.empty } @@ -202,8 +210,12 @@ class TypeCoercionService { } else if (actual.toReferenceType.extendsNumber && expected.toReferenceType.extendsNumber) { // Number type to number type return Optional.of([getNumberConversionExpression(it, expected)]) - } - + } + else if (actual instanceof RJavaWithMetaValue) { + return Optional.of([metaToItemConversionExpression(it, expected, scope)]) + } else if (expected instanceof RJavaFieldWithMeta || expected instanceof RJavaReferenceWithMeta) { + return Optional.of([itemToMetaConversionExpression(it, expected as RJavaWithMetaValue, scope)]) + } return Optional.empty } private def Function getWrapConversion(JavaType wrapperType) { @@ -231,7 +243,7 @@ class TypeCoercionService { private def Optional> getWrappedItemConversion(JavaType actual, JavaType expectedItemType, JavaScope scope) { val actualItemType = actual.itemType - getItemConversion(actualItemType, expectedItemType) + getItemConversion(actualItemType, expectedItemType, scope) .map[itemConversion| if (actual.isList) { [getListItemConversionExpression(it, itemConversion, expectedItemType.toReferenceType, scope)] @@ -313,19 +325,32 @@ class TypeCoercionService { } ) } - private def JavaStatementBuilder convertNullSafe(JavaExpression expr, Function conversion, JavaType expected, JavaScope scope) { + private def JavaStatementBuilder convertNullSafe(JavaExpression expr, Function conversion, JavaType expected, JavaScope scope) { val actual = expr.expressionType if (actual instanceof JavaPrimitiveType) { return expr.mapExpression(conversion) } - return expr - .declareAsVariable(true, actual.simpleName.toFirstLower, scope) - .mapExpression[new JavaConditionalExpression( - JavaExpression.from('''«it» == null''', JavaPrimitiveType.BOOLEAN), + + expr + .declareAsVariable(true, actual.simpleName.toFirstLower, scope) + .mapExpression[varExpr| + val conditionExpr = JavaExpression.from('''«varExpr» == null''', JavaPrimitiveType.BOOLEAN) + val converted = conversion.apply(varExpr) + if (converted instanceof JavaExpression) { + return new JavaConditionalExpression( + conditionExpr, + expected.empty, + converted, + typeUtil + ) + } + return new JavaIfThenElseBuilder( + conditionExpr, expected.empty, - conversion.apply(it), + converted, typeUtil - )] + ) + ] } private def JavaExpression empty(JavaType expected) { @@ -347,6 +372,35 @@ class TypeCoercionService { } } + /* + * 1. Unwrap the meta by calling getValue() on the expression + * 2. Map expression to a call to itemToItem(it, expected) + */ + private def JavaStatementBuilder metaToItemConversionExpression(JavaExpression expression, JavaType expected, JavaScope scope) { + val actual = expression.expressionType + if (actual instanceof RJavaWithMetaValue) { + JavaExpression.from('''«expression».getValue()''', actual.valueType) + .mapExpression[itemToItem(it, expected, scope)] + } else { + JavaExpression.NULL + } + } + + /* + * 1. Get the item conversion expression lambda for the given expression + * 2. If the lambda exists then run it and wrap the response in RJavaWithMetaValue builder + * 3. If no lambda exists wrap the given expression in RJavaWithMetaValue builder + */ + private def JavaStatementBuilder itemToMetaConversionExpression(JavaExpression expression, RJavaWithMetaValue expected, JavaScope scope) { + val expectedValueType = expected.valueType + getItemConversion(expression.expressionType, expectedValueType, scope) + .map[itemConversion| + itemConversion.apply(expression) + .mapExpression[JavaExpression.from('''«expected».builder().setValue(«it»).build()''', expected)] + ] + .orElseGet[JavaExpression.from('''«expected».builder().setValue(«expression»).build()''', expected)] + } + private def JavaExpression getNumberConversionExpression(JavaExpression expression, JavaType expected) { val actual = expression.expressionType if (actual.toReferenceType.isInteger) { @@ -434,7 +488,7 @@ class TypeCoercionService { private def JavaExpression getMapperToItemConversionExpression(JavaExpression expression) { JavaExpression.from('''«expression».get()''', expression.expressionType.itemType) } - private def JavaExpression getListItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { + private def JavaExpression getListItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { val actualItemType = expression.expressionType.itemType val lambdaScope = scope.lambdaScope val lambdaParam = lambdaScope.createUniqueIdentifier(actualItemType.simpleName.toFirstLower) @@ -443,42 +497,43 @@ class TypeCoercionService { JavaExpression.from( ''' «expression».stream() - .<«expectedItemType»>map(«lambdaParam» -> «resultItem») + .<«expectedItemType»>map(«lambdaParam» -> «resultItem.toLambdaBody») .collect(«Collectors».toList()) ''', resultType ) } - private def JavaExpression getMapperSItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { + private def JavaExpression getMapperSItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { val actualItemType = expression.expressionType.itemType val lambdaScope = scope.lambdaScope val lambdaParam = lambdaScope.createUniqueIdentifier(actualItemType.simpleName.toFirstLower) - val resultItem = itemConversion.apply(new JavaVariable(lambdaParam, actualItemType)) + val inputToItem = new JavaVariable(lambdaParam, actualItemType) val resultType = MAPPER_S.wrap(expectedItemType) + val resultItemNullSafe = convertNullSafe(inputToItem, itemConversion, expectedItemType, scope) JavaExpression.from( - '''«expression».<«expectedItemType»>map("Type coercion", «lambdaParam» -> «lambdaParam» == null ? null : «resultItem»)''', + '''«expression».<«resultType.itemType»>map("Type coercion", «lambdaParam» -> «resultItemNullSafe.toLambdaBody»)''', resultType ) } - private def JavaExpression getMapperCItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { + private def JavaExpression getMapperCItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { val actualItemType = expression.expressionType.itemType val lambdaScope = scope.lambdaScope val lambdaParam = lambdaScope.createUniqueIdentifier(actualItemType.simpleName.toFirstLower) val resultItem = itemConversion.apply(new JavaVariable(lambdaParam, actualItemType)) val resultType = MAPPER_C.wrap(expectedItemType) JavaExpression.from( - '''«expression».<«expectedItemType»>map("Type coercion", «lambdaParam» -> «resultItem»)''', + '''«expression».<«expectedItemType»>map("Type coercion", «lambdaParam» -> «resultItem.toLambdaBody»)''', resultType ) } - private def JavaExpression getMapperListOfListsItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { + private def JavaExpression getMapperListOfListsItemConversionExpression(JavaExpression expression, Function itemConversion, JavaReferenceType expectedItemType, JavaScope scope) { val actualItemType = expression.expressionType.itemType val listToListLambdaScope = scope.lambdaScope val mapperCParam = listToListLambdaScope.createUniqueIdentifier("mapperC") val resultMapperC = getMapperCItemConversionExpression(new JavaVariable(mapperCParam, MAPPER_C.wrap(actualItemType)), itemConversion, expectedItemType, listToListLambdaScope) val resultType = MAPPER_LIST_OF_LISTS.wrap(expectedItemType) JavaExpression.from( - '''«expression».<«expectedItemType»>mapListToList(«mapperCParam» -> «resultMapperC»)''', + '''«expression».<«expectedItemType»>mapListToList(«mapperCParam» -> «resultMapperC.toLambdaBody»)''', resultType ) } 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 f5bb8a8e3..149418f0f 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 @@ -1,12 +1,19 @@ package com.regnosys.rosetta.generator.java.function +import com.fasterxml.jackson.core.type.TypeReference import com.google.inject.ImplementedBy import com.regnosys.rosetta.generator.GeneratedIdentifier import com.regnosys.rosetta.generator.java.JavaIdentifierRepresentationService import com.regnosys.rosetta.generator.java.JavaScope import com.regnosys.rosetta.generator.java.RosettaJavaPackages.RootPackage import com.regnosys.rosetta.generator.java.expression.ExpressionGenerator +import com.regnosys.rosetta.generator.java.expression.JavaDependencyProvider +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.types.JavaTypeTranslator +import com.regnosys.rosetta.generator.java.types.JavaTypeUtil import com.regnosys.rosetta.generator.java.util.ImportManagerExtension import com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions @@ -34,40 +41,34 @@ import com.regnosys.rosetta.types.ROperationType import com.regnosys.rosetta.types.RShortcut import com.regnosys.rosetta.types.RosettaTypeProvider 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.functions.ConditionValidator import com.rosetta.model.lib.functions.IQualifyFunctionExtension import com.rosetta.model.lib.functions.ModelObjectValidator import com.rosetta.model.lib.functions.RosettaFunction import com.rosetta.util.types.JavaClass +import com.rosetta.util.types.JavaGenericTypeDeclaration +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.generated.GeneratedJavaClass import java.util.ArrayList +import java.util.Collections import java.util.List import java.util.Map import java.util.Optional import java.util.stream.Collectors +import javax.inject.Inject import org.eclipse.xtend2.lib.StringConcatenationClient import org.eclipse.xtext.generator.IFileSystemAccess2 import static com.regnosys.rosetta.generator.java.enums.EnumHelper.* import static com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil.* -import com.regnosys.rosetta.utils.ImplicitVariableUtil -import com.rosetta.util.types.JavaParameterizedType -import javax.inject.Inject -import com.rosetta.model.lib.ModelSymbolId -import com.rosetta.util.types.JavaReferenceType -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.JavaStatement -import com.regnosys.rosetta.generator.java.types.JavaTypeUtil -import com.rosetta.util.types.generated.GeneratedJavaClass -import com.regnosys.rosetta.generator.java.expression.TypeCoercionService -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.utils.ModelIdProvider -import com.regnosys.rosetta.RosettaEcoreUtil + +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta class FunctionGenerator { @@ -75,7 +76,7 @@ class FunctionGenerator { @Inject JavaDependencyProvider dependencyProvider @Inject RosettaTypeProvider typeProvider @Inject extension RosettaFunctionExtensions - @Inject extension RosettaEcoreUtil + @Inject ExpressionHelper exprHelper @Inject extension ImportManagerExtension @Inject CardinalityProvider cardinality @@ -117,7 +118,7 @@ class FunctionGenerator { } private def getQualifyingFunctionInterface(List inputs) { - val parameterVariable = inputs.head.RType.toListOrSingleJavaType(inputs.head.multi) + val parameterVariable = inputs.head.RMetaAnnotatedType.toListOrSingleJavaType(inputs.head.multi) JavaParameterizedType.from(new TypeReference>() {}, parameterVariable) } @@ -245,8 +246,8 @@ class FunctionGenerator { if («outputBuilderId» == null) { «evaluateScope.getIdentifierOrThrow(output)» = null; } else { - «evaluateScope.getIdentifierOrThrow(output)» = «outputBuilderId»«IF output.isMulti».stream().map(«output.RType.toJavaReferenceType»::build).collect(«Collectors».toList())«ELSE».build()«ENDIF»; - «objectValidatorId».validate(«output.RType.toJavaReferenceType».class, «evaluateScope.getIdentifierOrThrow(output)»); + «evaluateScope.getIdentifierOrThrow(output)» = «outputBuilderId»«IF output.isMulti».stream().map(«output.RMetaAnnotatedType.toJavaReferenceType»::build).collect(«Collectors».toList())«ELSE».build()«ENDIF»; + «objectValidatorId».validate(«output.RMetaAnnotatedType.toJavaReferenceType».class, «evaluateScope.getIdentifierOrThrow(output)»); } «ENDIF» @@ -284,7 +285,7 @@ class FunctionGenerator { «doEvaluateScope.getIdentifierOrThrow(input)» = «Collections».emptyList(); } «ENDFOR» - «output.toBuilderType» «doEvaluateScope.getIdentifierOrThrow(output)» = «IF output.multi»new «ArrayList»<>()«ELSEIF output.needsBuilder»«output.RType.toListOrSingleJavaType(output.multi)».builder()«ELSE»null«ENDIF»; + «output.toBuilderType» «doEvaluateScope.getIdentifierOrThrow(output)» = «IF output.multi»new «ArrayList»<>()«ELSEIF output.needsBuilder»«output.RMetaAnnotatedType.RType.toListOrSingleJavaType(output.multi)».builder()«ELSE»null«ENDIF»; return assignOutput(«doEvaluateScope.getIdentifierOrThrow(output)»«IF !inputs.empty», «ENDIF»«inputs.inputsAsArguments(doEvaluateScope)»); } @@ -441,9 +442,9 @@ class FunctionGenerator { «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» + .getOrCreate«seg.value.name.toFirstUpper»(«IF seg.value.multi»0«ENDIF»)«IF seg.value.RMetaAnnotatedType.hasMeta».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»)«ENDIF»«ENDFOR»''', + .«IF op.ROperationType == ROperationType.ADD»add«ELSE»set«ENDIF»«seg.value.name.toFirstUpper»«IF seg.value.RMetaAnnotatedType.hasMeta && !op.assignAsKey»Value«ENDIF»(«it»)«ENDIF»«ENDFOR»''', JavaPrimitiveType.VOID ) ].completeAsExpressionStatement @@ -456,7 +457,7 @@ class FunctionGenerator { if (cardinality.isMulti(op.expression)) { val lambdaScope = scope.lambdaScope val item = lambdaScope.createUniqueIdentifier("item") - expressionGenerator.javaCode(op.expression, MAPPER_C.wrap(op.expression), scope) + expressionGenerator.javaCode(op.expression, MAPPER_C.wrapExtendsWithoutMeta(op.expression), scope) .collapseToSingleExpression(scope) .mapExpression[ JavaExpression.from( @@ -476,7 +477,7 @@ class FunctionGenerator { val lambdaScope = scope.lambdaScope val r = lambdaScope.createUniqueIdentifier("r") val m = lambdaScope.createUniqueIdentifier("m") - expressionGenerator.javaCode(op.expression, typeProvider.getRType(op.expression).toJavaReferenceType, scope) + expressionGenerator.javaCode(op.expression, typeProvider.getRMetaAnnotatedType(op.expression).RType.withEmptyMeta.toJavaReferenceType, scope) .declareAsVariable(true, op.pathHead.name + op.pathTail.map[name.toFirstUpper].join, scope) .mapExpression[ JavaExpression.from( @@ -571,9 +572,9 @@ class FunctionGenerator { JavaPrimitiveType.VOID } else { if (out.typeCall.type.needsBuilder) { - typeProvider.getRTypeOfSymbol(out).toPolymorphicListOrSingleJavaType(out.card.isMany) + typeProvider.getRTypeOfSymbol(out).RType.toPolymorphicListOrSingleJavaType(out.card.isMany) } else { - typeProvider.getRTypeOfSymbol(out).toListOrSingleJavaType(out.card.isMany) + typeProvider.getRTypeOfSymbol(out).RType.toListOrSingleJavaType(out.card.isMany) } } } @@ -591,7 +592,7 @@ class FunctionGenerator { } private def StringConcatenationClient inputsAsParameters(List inputs, JavaScope scope) { - '''«FOR input : inputs SEPARATOR ', '»«input.toJavaType» «scope.getIdentifierOrThrow(input)»«ENDFOR»''' + '''«FOR input : inputs SEPARATOR ', '»«input.toMetaJavaType» «scope.getIdentifierOrThrow(input)»«ENDFOR»''' } private def JavaReferenceType shortcutJavaType(RShortcut feature) { @@ -602,12 +603,12 @@ class FunctionGenerator { javaType } private def JavaReferenceType shortcutExpressionJavaType(RShortcut feature) { - val rType = typeProvider.getRType(feature.expression) - rType.toJavaReferenceType + val metaRType = typeProvider.getRMetaAnnotatedType(feature.expression) + metaRType.toJavaReferenceType } private def JavaType toBuilderItemType(RAttribute rAttribute) { - var javaType = rAttribute.RType.toJavaReferenceType as JavaClass + var javaType = rAttribute.RMetaAnnotatedType.toJavaReferenceType as JavaClass if(rAttribute.needsBuilder) javaType = javaType.toBuilderType javaType } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/MetaFieldGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/MetaFieldGenerator.xtend index 264c0613c..89c84fdcd 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/MetaFieldGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/MetaFieldGenerator.xtend @@ -16,6 +16,10 @@ import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.SimpleFactory import com.regnosys.rosetta.scoping.RosettaScopeProvider +import com.regnosys.rosetta.types.RAttribute +import com.regnosys.rosetta.types.RMetaAnnotatedType +import com.regnosys.rosetta.types.RObjectFactory +import com.regnosys.rosetta.utils.PositiveIntegerInterval import com.rosetta.model.lib.GlobalKey import com.rosetta.model.lib.meta.BasicRosettaMetaData import com.rosetta.model.lib.meta.FieldWithMeta @@ -23,7 +27,6 @@ import com.rosetta.model.lib.meta.GlobalKeyFields import com.rosetta.model.lib.meta.MetaDataFields import com.rosetta.model.lib.meta.ReferenceWithMeta import com.rosetta.model.lib.meta.TemplateFields -import com.rosetta.util.types.JavaClass import com.rosetta.util.types.JavaParameterizedType import com.rosetta.util.types.generated.GeneratedJavaClass import java.util.ArrayList @@ -34,10 +37,10 @@ import org.eclipse.emf.ecore.resource.Resource import org.eclipse.xtend2.lib.StringConcatenationClient import org.eclipse.xtext.generator.IFileSystemAccess2 import org.eclipse.xtext.generator.IGeneratorContext - -import com.regnosys.rosetta.types.RObjectFactory -import com.regnosys.rosetta.types.RAttribute -import com.regnosys.rosetta.utils.PositiveIntegerInterval +import com.regnosys.rosetta.generator.java.types.RJavaFieldWithMeta +import com.regnosys.rosetta.generator.java.types.RJavaReferenceWithMeta +import static extension org.eclipse.xtext.EcoreUtil2.* +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.* import com.regnosys.rosetta.types.RType class MetaFieldGenerator { @@ -71,34 +74,26 @@ class MetaFieldGenerator { // } // } - val modelClasses = model.elements.filter [ - it instanceof Data - ] - if (modelClasses.empty) { - return - } //find all the reference types - val namespaceClasses = Multimaps.index(modelClasses, [c|c.model]).asMap - for (nsc : namespaceClasses.entrySet) { + if (ctx.cancelIndicator.canceled) { + return + } + for (attr : model.eAllOfType(Attribute).map[buildRAttribute].filter[RMetaAnnotatedType.hasMeta]) { + val targetModel = attr.RMetaAnnotatedType.RType.namespace + val targetPackage = new RootPackage(targetModel) + val metaJt = attr.toForcedMetaItemJavaType + if (ctx.cancelIndicator.canceled) { return } - val attributesWithMeta = nsc.value.filter(Data).flatMap[buildRDataType.ownAttributes].filter[!metaAnnotations.empty] - for (attr : attributesWithMeta) { - val targetModel = attr.RType.namespace - val targetPackage = new RootPackage(targetModel) - val metaJt = attr.toForcedMetaItemJavaType - - if (ctx.cancelIndicator.canceled) { - return - } - if (attr.hasReferenceOrAddressMetadata) { - fsa.generateFile('''«metaJt.canonicalName.withForwardSlashes».java''', referenceWithMeta(targetPackage, metaJt, attr.RType)) - } else { - fsa.generateFile('''«metaJt.canonicalName.withForwardSlashes».java''', fieldWithMeta(targetPackage, metaJt, attr.RType)) - } + if (metaJt instanceof RJavaReferenceWithMeta) { + fsa.generateFile('''«metaJt.canonicalName.withForwardSlashes».java''', referenceWithMeta(targetPackage, metaJt, attr.RMetaAnnotatedType.RType)) + } else if (metaJt instanceof RJavaFieldWithMeta) { + fsa.generateFile('''«metaJt.canonicalName.withForwardSlashes».java''', fieldWithMeta(targetPackage, metaJt, attr.RMetaAnnotatedType.RType)) + } else { + throw new UnsupportedOperationException("Invalid JavaType: " + metaJt) } } } @@ -201,9 +196,9 @@ class MetaFieldGenerator { buildClass(packages.basicMetafields, body, scope) } - def CharSequence fieldWithMeta(RootPackage root, JavaClass metaJavaType, RType valueType) { + private def CharSequence fieldWithMeta(RootPackage root, RJavaFieldWithMeta metaJavaType, RType valueType) { val valueAttribute = new RAttribute( - "value", null, emptyList, valueType, emptyList, PositiveIntegerInterval.bounded(0, 1), null, null + "value", null, emptyList, valueType.withEmptyMeta, PositiveIntegerInterval.bounded(0, 1), null, null ) val metaType = SimpleFactory.eINSTANCE.createData() @@ -260,9 +255,9 @@ class MetaFieldGenerator { #[globalRefAttribute, externalRefAttribute, refAttribute] } - def referenceWithMeta(RootPackage root, JavaClass metaJavaType, RType valueType) { + private def referenceWithMeta(RootPackage root, RJavaReferenceWithMeta metaJavaType, RType valueType) { val valueAttribute = new RAttribute( - "value", null, emptyList, valueType, emptyList, PositiveIntegerInterval.bounded(0, 1), null, null + "value", null, emptyList, valueType.withEmptyMeta, PositiveIntegerInterval.bounded(0, 1), null, null ) val Data d = SimpleFactory.eINSTANCE.createData; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlate.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlate.xtend index e11ad314e..e16818bef 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlate.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBoilerPlate.xtend @@ -1,6 +1,13 @@ package com.regnosys.rosetta.generator.java.object +import com.regnosys.rosetta.RosettaEcoreUtil +import com.regnosys.rosetta.generator.java.JavaScope +import com.regnosys.rosetta.generator.java.types.JavaTypeTranslator import com.regnosys.rosetta.rosetta.simple.Data +import com.regnosys.rosetta.types.RAttribute +import com.regnosys.rosetta.types.RDataType +import com.regnosys.rosetta.types.REnumType +import com.regnosys.rosetta.types.TypeSystem import com.rosetta.model.lib.GlobalKey import com.rosetta.model.lib.GlobalKey.GlobalKeyBuilder import com.rosetta.model.lib.RosettaModelObject @@ -11,19 +18,10 @@ import com.rosetta.model.lib.process.AttributeMeta import com.rosetta.model.lib.process.BuilderProcessor import com.rosetta.model.lib.process.Processor import com.rosetta.util.ListEquals +import java.util.List import java.util.Objects -import org.eclipse.xtend2.lib.StringConcatenationClient - -import com.regnosys.rosetta.generator.java.JavaScope -import com.regnosys.rosetta.generator.java.types.JavaTypeTranslator import javax.inject.Inject -import com.regnosys.rosetta.types.RDataType -import com.regnosys.rosetta.types.TypeSystem -import com.regnosys.rosetta.types.RAttribute -import com.regnosys.rosetta.types.REnumType -import com.regnosys.rosetta.RosettaEcoreUtil -import java.util.List -import com.regnosys.rosetta.types.RChoiceType +import org.eclipse.xtend2.lib.StringConcatenationClient class ModelObjectBoilerPlate { @@ -74,8 +72,9 @@ class ModelObjectBoilerPlate { private def StringConcatenationClient contributeHashCode(RAttribute attr, JavaScope scope) { val id = scope.getIdentifierOrThrow(attr) + val rMetaAnnotatedType = attr.RMetaAnnotatedType ''' - «IF attr.RType instanceof REnumType» + «IF !rMetaAnnotatedType.hasMeta && rMetaAnnotatedType.RType instanceof REnumType» «IF attr.isMulti» _result = 31 * _result + («id» != null ? «id».stream().map(«Object»::getClass).map(«Class»::getName).mapToInt(«String»::hashCode).sum() : 0); «ELSE» diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend index e3d8136f1..a26abf5e0 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend @@ -131,7 +131,7 @@ class ModelObjectBuilderGenerator { } else { result = «scope.getIdentifierOrThrow(attribute)» = «attribute.toMetaItemJavaType».builder(); - «IF !attribute.metaAnnotations.filter[m|m.name=="location"].isEmpty» + «IF !attribute.RMetaAnnotatedType.metaAttributes.filter[m|m.name=="location"].isEmpty» result.getOrCreateMeta().toBuilder().addKey(«Key».builder().setScope("DOCUMENT")); «ENDIF» } @@ -149,7 +149,7 @@ class ModelObjectBuilderGenerator { «attribute.toBuilderTypeSingle» result; return getIndex(«scope.getIdentifierOrThrow(attribute)», _index, () -> { «attribute.toBuilderTypeSingle» new«attribute.name.toFirstUpper» = «attribute.toMetaItemJavaType».builder(); - «IF !attribute.metaAnnotations.filter[m|m.name=="location"].isEmpty» + «IF !attribute.RMetaAnnotatedType.metaAttributes.filter[m|m.name=="location"].isEmpty» new«attribute.name.toFirstUpper».getOrCreateMeta().addKey(«Key».builder().setScope("DOCUMENT")); «ENDIF» return new«attribute.name.toFirstUpper»; @@ -185,7 +185,7 @@ class ModelObjectBuilderGenerator { getIndex(this.«scope.getIdentifierOrThrow(attribute)», _idx, () -> «attribute.toBuilder(scope)»); return this; } - «IF !attribute.metaAnnotations.isEmpty» + «IF attribute.RMetaAnnotatedType.hasMeta» @Override public «thisName» add«attribute.name.toFirstUpper»Value(«attribute.toItemJavaType» «scope.getIdentifierOrThrow(attribute)») { @@ -221,7 +221,7 @@ class ModelObjectBuilderGenerator { } return this; } - «IF !attribute.metaAnnotations.isEmpty» + «IF attribute.RMetaAnnotatedType.hasMeta» @Override public «thisName» add«attribute.name.toFirstUpper»Value(«attribute.toJavaType» «scope.getIdentifierOrThrow(attribute)»s) { @@ -250,7 +250,7 @@ class ModelObjectBuilderGenerator { this.«scope.getIdentifierOrThrow(attribute)» = «scope.getIdentifierOrThrow(attribute)»==null?null:«attribute.toBuilder(scope)»; return this; } - «IF !attribute.metaAnnotations.isEmpty» + «IF attribute.RMetaAnnotatedType.hasMeta» @Override public «thisName» set«attribute.name.toFirstUpper»Value(«attribute.toJavaType» «scope.getIdentifierOrThrow(attribute)») { this.getOrCreate«attribute.name.toFirstUpper»().setValue(«scope.getIdentifierOrThrow(attribute)»); @@ -294,7 +294,7 @@ class ModelObjectBuilderGenerator { } def StringConcatenationClient toBuilderTypeSingle(RAttribute attribute) { - if (!attribute.metaAnnotations.isEmpty) { + if (attribute.RMetaAnnotatedType.hasMeta) { '''«attribute.toMetaItemJavaType.toBuilderType»''' } else { '''«attribute.toBuilderTypeUnderlying»''' @@ -302,7 +302,7 @@ class ModelObjectBuilderGenerator { } private def StringConcatenationClient toBuilderTypeUnderlying(RAttribute attribute) { - if (attribute.isRosettaModelObject) '''«attribute.RType.name».«attribute.RType.name»Builder''' + if (attribute.isRosettaModelObject) '''«attribute.RMetaAnnotatedType.RType.name».«attribute.RMetaAnnotatedType.RType.name»Builder''' else '''«attribute.toMetaItemJavaType»''' } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectGenerator.xtend index 928b55680..644a51e01 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ModelObjectGenerator.xtend @@ -115,15 +115,15 @@ class ModelObjectGenerator { «FOR attribute : t.allJavaAttributes» «IF !attribute.isMulti» «javaType.toBuilderType» set«attribute.name.toFirstUpper»(«attribute.toMetaJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); - «IF !attribute.metaAnnotations.isEmpty»«javaType.toBuilderType» set«attribute.name.toFirstUpper»Value(«attribute.toJavaType» «builderScope.createUniqueIdentifier(attribute.name)»);«ENDIF» + «IF attribute.RMetaAnnotatedType.hasMeta»«javaType.toBuilderType» set«attribute.name.toFirstUpper»Value(«attribute.toJavaType» «builderScope.createUniqueIdentifier(attribute.name)»);«ENDIF» «ELSE» «javaType.toBuilderType» add«attribute.name.toFirstUpper»(«attribute.toMetaItemJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); «javaType.toBuilderType» add«attribute.name.toFirstUpper»(«attribute.toMetaItemJavaType» «builderScope.createUniqueIdentifier(attribute.name)», int _idx); - «IF !attribute.metaAnnotations.isEmpty»«javaType.toBuilderType» add«attribute.name.toFirstUpper»Value(«attribute.toItemJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); + «IF attribute.RMetaAnnotatedType.hasMeta»«javaType.toBuilderType» add«attribute.name.toFirstUpper»Value(«attribute.toItemJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); «javaType.toBuilderType» add«attribute.name.toFirstUpper»Value(«attribute.toItemJavaType» «builderScope.createUniqueIdentifier(attribute.name)», int _idx);«ENDIF» «javaType.toBuilderType» add«attribute.name.toFirstUpper»(«attribute.toMetaJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); «javaType.toBuilderType» set«attribute.name.toFirstUpper»(«attribute.toMetaJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); - «IF !attribute.metaAnnotations.isEmpty»«javaType.toBuilderType» add«attribute.name.toFirstUpper»Value(«attribute.toJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); + «IF attribute.RMetaAnnotatedType.hasMeta»«javaType.toBuilderType» add«attribute.name.toFirstUpper»Value(«attribute.toJavaType» «builderScope.createUniqueIdentifier(attribute.name)»); «javaType.toBuilderType» set«attribute.name.toFirstUpper»Value(«attribute.toJavaType» «builderScope.createUniqueIdentifier(attribute.name)»);«ENDIF» «ENDIF» «ENDFOR» diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ValidatorsGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ValidatorsGenerator.xtend index 9ac4e671d..9c6367506 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ValidatorsGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/object/ValidatorsGenerator.xtend @@ -192,7 +192,7 @@ class ValidatorsGenerator { } private def StringConcatenationClient checkTypeFormat(RAttribute attr) { - val t = attr.RType.stripFromTypeAliases + val t = attr.RMetaAnnotatedType.RType.stripFromTypeAliases if (t instanceof RStringType) { if (t != UNCONSTRAINED_STRING) { val min = t.interval.minBound @@ -215,7 +215,7 @@ class ValidatorsGenerator { } private def StringConcatenationClient getAttributeValue(RAttribute attr) { - if (attr.metaAnnotations.empty) { + if (!attr.RMetaAnnotatedType.hasMeta) { '''o.get«attr.name?.toFirstUpper»()''' } else { val jt = attr.toMetaJavaType diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/reports/TabulatorGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/reports/TabulatorGenerator.xtend index 5086f17b4..a8ee3344b 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/reports/TabulatorGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/reports/TabulatorGenerator.xtend @@ -73,10 +73,11 @@ class TabulatorGenerator { isTabulated(attr, newHashSet) } private def boolean isTabulated(RAttribute attr, Set visited) { - val attrType = if (attr.RType instanceof RChoiceType) { - (attr.RType as RChoiceType).asRDataType + val rawAttrType = attr.RMetaAnnotatedType.RType + val attrType = if (rawAttrType instanceof RChoiceType) { + rawAttrType.asRDataType } else { - attr.RType + rawAttrType } if (attrType instanceof RDataType) { needsTabulator(attrType, visited) @@ -219,7 +220,7 @@ class TabulatorGenerator { val tabulatorClass = func.toApplicableTabulatorClass val topScope = new JavaScope(tabulatorClass.packageName) - val t = typeProvider.getRTypeOfSymbol(func.output) + val t = typeProvider.getRTypeOfSymbol(func.output).RType val functionOutputType = if (t instanceof RChoiceType) { t.asRDataType } else { @@ -228,12 +229,11 @@ class TabulatorGenerator { if (functionOutputType instanceof RDataType) { val context = createFunctionTabulatorContext(typeTranslator, func) - val type = functionOutputType - val classBody = type.mainTabulatorClassBody(context, topScope, tabulatorClass) + val classBody = functionOutputType.mainTabulatorClassBody(context, topScope, tabulatorClass) val content = buildClass(tabulatorClass.packageName, classBody, topScope) fsa.generateFile(tabulatorClass.canonicalName.withForwardSlashes + ".java", content) - recursivelyGenerateTabulators(fsa, type, context, newHashSet) + recursivelyGenerateTabulators(fsa, functionOutputType, context, newHashSet) } } } @@ -249,6 +249,7 @@ class TabulatorGenerator { type .allNonOverridenAttributes + .map[RMetaAnnotatedType] .map[RType] .map[it instanceof RChoiceType ? asRDataType : it] .filter(RDataType) @@ -418,6 +419,7 @@ class TabulatorGenerator { private def Set findNestedTabulatorsAndCreateIdentifiers(RDataType type, TabulatorContext context, JavaScope scope) { val result = type.allNonOverridenAttributes .filter[context.isTabulated(it)] + .map[RMetaAnnotatedType] .map[RType] .map[it instanceof RChoiceType ? asRDataType : it] .filter(RDataType) @@ -438,10 +440,11 @@ class TabulatorGenerator { } private def StringConcatenationClient fieldValue(RAttribute attr, GeneratedIdentifier inputParam, JavaScope scope) { - val rType = if (attr.RType instanceof RChoiceType) { - (attr.RType as RChoiceType).asRDataType + val rawAttr = attr.RMetaAnnotatedType.RType + val rType = if (rawAttr instanceof RChoiceType) { + rawAttr.asRDataType } else { - attr.RType + rawAttr } val resultId = scope.createIdentifier(attr.toComputedField, attr.name) @@ -458,7 +461,7 @@ class TabulatorGenerator { «FieldValue» «resultId» = «Optional».ofNullable(«inputParam».get«attr.name.toFirstUpper»()) «IF attr.isMulti» .map(«lambdaParam» -> «lambdaParam».stream() - «IF !attr.metaAnnotations.empty» + «IF attr.RMetaAnnotatedType.hasMeta» .map(«nestedLambdaParam» -> «nestedLambdaParam».getValue()) .filter(«Objects»::nonNull) «ENDIF» @@ -467,14 +470,14 @@ class TabulatorGenerator { .map(fieldValues -> new «MultiNestedFieldValueImpl»(«scope.getIdentifierOrThrow(attr)», Optional.of(fieldValues))) .orElse(new «MultiNestedFieldValueImpl»(«scope.getIdentifierOrThrow(attr)», Optional.empty())); «ELSE» - «IF !attr.metaAnnotations.empty».map(«lambdaParam» -> «lambdaParam».getValue())«ENDIF» + «IF attr.RMetaAnnotatedType.hasMeta».map(«lambdaParam» -> «lambdaParam».getValue())«ENDIF» .map(«lambdaParam» -> new «NestedFieldValueImpl»(«scope.getIdentifierOrThrow(attr)», Optional.of(«nestedTabulator».tabulate(«lambdaParam»)))) .orElse(new «NestedFieldValueImpl»(«scope.getIdentifierOrThrow(attr)», Optional.empty())); «ENDIF» ''' } else { ''' - «IF attr.metaAnnotations.empty» + «IF !attr.RMetaAnnotatedType.hasMeta» «FieldValue» «resultId» = new «FieldValueImpl»(«scope.getIdentifierOrThrow(attr)», «Optional».ofNullable(«inputParam».get«attr.name.toFirstUpper»())); «ELSEIF attr.isMulti» «FieldValue» «resultId» = new «FieldValueImpl»(«scope.getIdentifierOrThrow(attr)», «Optional».ofNullable(«inputParam».get«attr.name.toFirstUpper»()) diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaConditionalExpression.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaConditionalExpression.java index 80e18ef70..45f86ee1b 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaConditionalExpression.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/statement/builder/JavaConditionalExpression.java @@ -101,7 +101,10 @@ public JavaAssignment completeAsAssignment(GeneratedIdentifier variableId) { @Override public JavaStatementBuilder declareAsVariable(boolean isFinal, String variableId, JavaScope scope) { - return this.toExpression().declareAsVariable(isFinal, variableId, scope); + JavaExpression expression = this.toExpression(); + JavaStatementBuilder variable = expression.declareAsVariable(isFinal, variableId, scope); + scope.createKeySynonym(this, expression); + return variable; } @Override 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 cc397e1ee..aec3d1a0d 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 @@ -44,6 +44,7 @@ import com.regnosys.rosetta.types.REnumType; import com.regnosys.rosetta.types.RErrorType; 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; @@ -80,8 +81,6 @@ public JavaTypeTranslator(RBuiltinTypeService builtins) { @Inject private RosettaJavaPackages packages; @Inject - private RosettaEcoreUtil extensions; - @Inject private RosettaTypeProvider typeProvider; @Inject private TypeSystem typeSystem; @@ -97,10 +96,11 @@ private DottedPath getModelPackage(RosettaRootElement object) { } public boolean isRosettaModelObject(RAttribute attr) { - return isValueRosettaModelObject(attr) || !attr.getMetaAnnotations().isEmpty(); + RMetaAnnotatedType rMetaAnnotatedType = attr.getRMetaAnnotatedType(); + return isValueRosettaModelObject(attr) || rMetaAnnotatedType.hasMeta(); } public boolean isValueRosettaModelObject(RAttribute attr) { - RType t = attr.getRType(); + RType t = attr.getRMetaAnnotatedType().getRType(); return t instanceof RDataType || t instanceof RChoiceType; } @@ -184,26 +184,20 @@ public JavaClass toDeepPathUtilJavaClass(RDataType choiceType) { String simpleName = typeId.getName() + "DeepPathUtil"; return new GeneratedJavaClass<>(packageName, simpleName, Object.class); } - public JavaClass toMetaJavaType(Attribute attribute) { - JavaClass attrType = toJavaReferenceType(typeProvider.getRTypeOfSymbol(attribute)); - DottedPath namespace = getModelPackage(attribute.getTypeCall().getType()); - return toMetaJavaType(attrType, extensions.hasMetaFieldAnnotations(attribute), namespace); - } public JavaClass toItemJavaType(RAttribute attr) { - return toJavaReferenceType(attr.getRType()); + return toJavaReferenceType(attr.getRMetaAnnotatedType().getRType()); } public JavaClass toMetaItemJavaType(RAttribute attr) { - JavaClass itemType = toItemJavaType(attr); - if (attr.getMetaAnnotations().isEmpty()) { - return itemType; - } - DottedPath namespace = attr.getRType().getNamespace(); - return toMetaJavaType(itemType, !attr.hasReferenceOrAddressMetadata(), namespace); + return toJavaReferenceType(attr.getRMetaAnnotatedType()); } public JavaClass toForcedMetaItemJavaType(RAttribute attr) { - JavaClass itemType = toItemJavaType(attr); - DottedPath namespace = attr.getRType().getNamespace(); - return toMetaJavaType(itemType, !attr.hasReferenceOrAddressMetadata(), namespace); + JavaClass metaItemJavaType = toMetaItemJavaType(attr); + if (!attr.getRMetaAnnotatedType().hasMeta()) { + RType rType = typeSystem.stripFromTypeAliases(attr.getRMetaAnnotatedType().getRType()); + DottedPath namespace = metaField(rType.getNamespace()); + return new RJavaFieldWithMeta(metaItemJavaType, namespace, typeUtil); + } + return metaItemJavaType; } public JavaClass toMetaJavaType(RAttribute attr) { JavaClass itemType = toMetaItemJavaType(attr); @@ -227,17 +221,6 @@ public JavaClass toJavaType(RAttribute attr) { } return itemType; } - private JavaClass toMetaJavaType(JavaReferenceType base, boolean hasMetaFieldAnnotations, DottedPath namespace) { - String attributeTypeName = base.getSimpleName(); - String name; - if (hasMetaFieldAnnotations) { - name = "FieldWithMeta" + attributeTypeName; - } else { - name = "ReferenceWithMeta" + attributeTypeName; - } - DottedPath pkg = metaField(namespace); - return new GeneratedJavaClass<>(pkg, name, Object.class); - } public JavaClass operationToReferenceWithMetaType(Operation op) { Attribute attr; if (op.getPath() == null) { @@ -246,8 +229,7 @@ public JavaClass operationToReferenceWithMetaType(Operation op) { List segments = op.pathAsSegmentList(); attr = segments.get(segments.size() - 1).getAttribute(); } - DottedPath namespace = getModelPackage(attr.getTypeCall().getType()); - return toMetaJavaType(toJavaReferenceType(typeProvider.getRTypeOfSymbol(attr)), false, namespace); + return toJavaReferenceType(typeProvider.getRTypeOfSymbol(attr)); } public JavaReferenceType operationToJavaType(ROperation op) { @@ -268,12 +250,25 @@ public JavaClass operationToReferenceWithMetaType(ROperation op) { List segments = op.getPathTail(); attr = segments.get(segments.size() - 1); } - return toMetaJavaType(toJavaReferenceType(attr.getRType()), false, attr.getRType().getNamespace()); + return toJavaReferenceType(attr.getRMetaAnnotatedType()); } private String getTypeDebugInfo(RType type) { return type.toString() + " (" + type.getClass().getSimpleName() + ")"; } + private String getTypeDebugInfo(RMetaAnnotatedType type) { + return type.toString() + " (" + type.getClass().getSimpleName() + ")"; + } + public JavaClass toJavaReferenceType(RMetaAnnotatedType type) { + JavaType jt = toJavaType(type); + if (jt instanceof JavaPrimitiveType) { + return ((JavaPrimitiveType)jt).toReferenceType(); + } else if (jt instanceof JavaClass) { + return (JavaClass)jt; + } else { + throw new UnsupportedOperationException("Cannot convert type " + getTypeDebugInfo(type) + " to a Java reference type."); + } + } public JavaClass toJavaReferenceType(RType type) { JavaType jt = toJavaType(type); if (jt instanceof JavaPrimitiveType) { @@ -296,7 +291,18 @@ public JavaClass toJavaReferenceType(Optional type) { } return typeUtil.OBJECT; } - public JavaType toJavaType(RType type) { + public JavaType toJavaType(RMetaAnnotatedType type) { + JavaReferenceType javaType = toJavaReferenceType(type.getRType()); + if (type.hasMeta()) { + RType rType = typeSystem.stripFromTypeAliases(type.getRType()); + DottedPath namespace = metaField(rType.getNamespace()); + return hasReferenceOrAddressMetadata(type) ? + new RJavaReferenceWithMeta(javaType, namespace, typeUtil): + new RJavaFieldWithMeta(javaType, namespace, typeUtil); + } + return javaType; + } + private JavaType toJavaType(RType type) { return doSwitch(type, null); } public RJavaPojoInterface toJavaType(RDataType type) { @@ -309,12 +315,27 @@ public JavaType toJavaType(Optional type) { return type.map(t -> toJavaType(t)).orElse(typeUtil.OBJECT); } + public JavaClass toPolymorphicListOrSingleJavaType(RMetaAnnotatedType type, boolean isMany) { + if (isMany) { + return toPolymorphicList(toJavaReferenceType(type)); + } else + return toJavaReferenceType(type); + } + public JavaClass toPolymorphicListOrSingleJavaType(RType type, boolean isMany) { if (isMany) { return toPolymorphicList(toJavaReferenceType(type)); } else return toJavaReferenceType(type); } + + public JavaClass toListOrSingleJavaType(RMetaAnnotatedType type, boolean isMany) { + if (isMany) { + return typeUtil.wrap(typeUtil.LIST, toJavaReferenceType(type)); + } else + return toJavaReferenceType(type); + } + public JavaClass toListOrSingleJavaType(RType type, boolean isMany) { if (isMany) { return typeUtil.wrap(typeUtil.LIST, toJavaReferenceType(type)); @@ -426,4 +447,10 @@ protected JavaClass caseDateTimeType(RDateTimeType type, Void con protected JavaClass caseZonedDateTimeType(RZonedDateTimeType type, Void context) { return typeUtil.ZONED_DATE_TIME; } + + private boolean hasReferenceOrAddressMetadata(RMetaAnnotatedType rMetaAnnotatedType) { + return rMetaAnnotatedType.getMetaAttributes() + .stream() + .anyMatch(a -> a.getName().equals("reference") || a.getName().equals("address")); + } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeUtil.java index dd56afe22..336c213b4 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/JavaTypeUtil.java @@ -29,12 +29,15 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.regnosys.rosetta.rosetta.expression.RosettaExpression; import com.regnosys.rosetta.types.RosettaTypeProvider; +import com.rosetta.model.lib.GlobalKey; import com.rosetta.model.lib.RosettaModelObject; import com.rosetta.model.lib.expression.ComparisonResult; import com.rosetta.model.lib.mapper.Mapper; import com.rosetta.model.lib.mapper.MapperC; import com.rosetta.model.lib.mapper.MapperListOfLists; import com.rosetta.model.lib.mapper.MapperS; +import com.rosetta.model.lib.meta.FieldWithMeta; +import com.rosetta.model.lib.meta.ReferenceWithMeta; import com.rosetta.util.types.JavaClass; import com.rosetta.util.types.JavaGenericTypeDeclaration; import com.rosetta.util.types.JavaParameterizedType; @@ -63,11 +66,14 @@ public class JavaTypeUtil { public final JavaClass DATE = JavaClass.from(com.rosetta.model.lib.records.Date.class); public final JavaClass LOCAL_DATE_TIME = JavaClass.from(LocalDateTime.class); public final JavaClass ZONED_DATE_TIME = JavaClass.from(ZonedDateTime.class); + public final JavaClass GLOBAL_KEY = JavaClass.from(GlobalKey.class); public final JavaClass OBJECT = JavaClass.OBJECT; public final JavaClass CLONEABLE = JavaClass.CLONEABLE; public final JavaClass SERIALIZABLE = JavaClass.SERIALIZABLE; + public final JavaGenericTypeDeclaration> FIELD_WITH_META = JavaGenericTypeDeclaration.from(new TypeReference<>() {}); + public final JavaGenericTypeDeclaration> REFERENCE_WITH_META = JavaGenericTypeDeclaration.from(new TypeReference<>() {}); public final JavaGenericTypeDeclaration> LIST = JavaGenericTypeDeclaration.from(new TypeReference<>() {}); public final JavaGenericTypeDeclaration> MAPPER = JavaGenericTypeDeclaration.from(new TypeReference<>() {}); public final JavaGenericTypeDeclaration> MAPPER_S = JavaGenericTypeDeclaration.from(new TypeReference<>() {}); @@ -84,17 +90,27 @@ public JavaParameterizedType wrap(JavaGenericTypeDeclaration wrapperTy return wrap(wrapperType, JavaType.from(itemType)); } public JavaParameterizedType wrap(JavaGenericTypeDeclaration wrapperType, RosettaExpression item) { - return wrap(wrapperType, typeTranslator.toJavaReferenceType(typeProvider.getRType(item))); + return wrap(wrapperType, typeTranslator.toJavaReferenceType(typeProvider.getRMetaAnnotatedType(item))); + } + public JavaParameterizedType wrapExtendsWithoutMeta(JavaGenericTypeDeclaration wrapperType, JavaType itemType) { + if (itemType instanceof RJavaWithMetaValue) { + RJavaWithMetaValue metaItemType = (RJavaWithMetaValue) itemType; + return JavaParameterizedType.from(wrapperType, JavaWildcardTypeArgument.extendsBound(metaItemType.getValueType())); + } + return JavaParameterizedType.from(wrapperType, JavaWildcardTypeArgument.extendsBound(itemType.toReferenceType())); } - public JavaParameterizedType wrapExtends(JavaGenericTypeDeclaration wrapperType, JavaType itemType) { return JavaParameterizedType.from(wrapperType, JavaWildcardTypeArgument.extendsBound(itemType.toReferenceType())); } public JavaParameterizedType wrapExtends(JavaGenericTypeDeclaration wrapperType, Class itemType) { return wrapExtends(wrapperType, JavaType.from(itemType)); } + public JavaParameterizedType wrapExtendsWithoutMeta(JavaGenericTypeDeclaration wrapperType, RosettaExpression item) { + return wrapExtends(wrapperType, typeTranslator.toJavaReferenceType(typeProvider.getRMetaAnnotatedType(item).getRType())); + } + public JavaParameterizedType wrapExtends(JavaGenericTypeDeclaration wrapperType, RosettaExpression item) { - return wrapExtends(wrapperType, typeTranslator.toJavaReferenceType(typeProvider.getRType(item))); + return wrapExtends(wrapperType, typeTranslator.toJavaReferenceType(typeProvider.getRMetaAnnotatedType(item))); } public JavaParameterizedType wrapExtendsIfNotFinal(JavaGenericTypeDeclaration wrapperType, JavaType itemType) { @@ -114,7 +130,7 @@ public JavaParameterizedType wrapExtendsIfNotFinal(JavaGenericTypeDeclara return wrapExtendsIfNotFinal(wrapperType, JavaType.from(itemType)); } public JavaParameterizedType wrapExtendsIfNotFinal(JavaGenericTypeDeclaration wrapperType, RosettaExpression item) { - return wrapExtendsIfNotFinal(wrapperType, typeTranslator.toJavaReferenceType(typeProvider.getRType(item))); + return wrapExtendsIfNotFinal(wrapperType, typeTranslator.toJavaReferenceType(typeProvider.getRMetaAnnotatedType(item))); } public boolean hasWildcardArgument(JavaType t) { @@ -146,7 +162,6 @@ public JavaType changeItemType(JavaType t, JavaType newItemType) { } return newItemType; } - public boolean extendsNumber(JavaType t) { return t.isSubtypeOf(NUMBER); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaFieldWithMeta.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaFieldWithMeta.java new file mode 100644 index 000000000..1fad8f5ea --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaFieldWithMeta.java @@ -0,0 +1,85 @@ +package com.regnosys.rosetta.generator.java.types; + +import java.util.List; + +import com.rosetta.model.lib.RosettaModelObject; +import com.rosetta.model.lib.meta.FieldWithMeta; +import com.rosetta.util.DottedPath; +import com.rosetta.util.types.JavaClass; +import com.rosetta.util.types.JavaParameterizedType; +import com.rosetta.util.types.JavaReferenceType; +import com.rosetta.util.types.JavaType; +import com.rosetta.util.types.JavaTypeDeclaration; + +public class RJavaFieldWithMeta extends RJavaWithMetaValue { + private final DottedPath namespace; + private final JavaTypeUtil javaTypeUtil; + private final JavaParameterizedType> fieldWithMetaParameterisedType; + + + public RJavaFieldWithMeta(JavaReferenceType valueType, DottedPath namespace, JavaTypeUtil javaTypeUtil) { + super(valueType); + this.namespace = namespace; + this.javaTypeUtil = javaTypeUtil; + fieldWithMetaParameterisedType = javaTypeUtil.wrap(javaTypeUtil.FIELD_WITH_META, valueType); + } + + @Override + public boolean isSubtypeOf(JavaType other) { + if (fieldWithMetaParameterisedType.isSubtypeOf(other)) { + return true; + } + if (javaTypeUtil.ROSETTA_MODEL_OBJECT.isSubtypeOf(other)) { + return true; + } + if (javaTypeUtil.GLOBAL_KEY.isSubtypeOf(other)) { + return true; + } + return false; + } + + @Override + public String getSimpleName() { + return "FieldWithMeta" + valueType.getSimpleName(); + } + + @Override + public JavaTypeDeclaration getSuperclassDeclaration() { + return JavaClass.OBJECT; + } + + @Override + public List> getInterfaceDeclarations() { + return List.of(javaTypeUtil.ROSETTA_MODEL_OBJECT, javaTypeUtil.FIELD_WITH_META, javaTypeUtil.GLOBAL_KEY); + } + + @Override + public boolean extendsDeclaration(JavaTypeDeclaration other) { + if (other instanceof JavaClass) { + return this.isSubtypeOf((JavaClass)other); + } + return false; + } + + @Override + public boolean isFinal() { + return false; + } + + @Override + public Class loadClass(ClassLoader classLoader) throws ClassNotFoundException { + return Class.forName(getCanonicalName().toString(), true, classLoader).asSubclass(RosettaModelObject.class); + } + + @Override + public DottedPath getPackageName() { + return namespace; + } + + + @Override + public List> getInterfaces() { + return List.of(javaTypeUtil.ROSETTA_MODEL_OBJECT,fieldWithMetaParameterisedType, javaTypeUtil.GLOBAL_KEY); + } + +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaPojoInterface.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaPojoInterface.java index 8ed7902ef..0ad9fbc79 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaPojoInterface.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaPojoInterface.java @@ -47,6 +47,7 @@ public RDataType getRType() { @Override public boolean isSubtypeOf(JavaType other) { + //TODO: is this first check needed? if (other instanceof JavaPrimitiveType) { return false; } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaReferenceWithMeta.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaReferenceWithMeta.java new file mode 100644 index 000000000..dd55ac2cf --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaReferenceWithMeta.java @@ -0,0 +1,86 @@ +package com.regnosys.rosetta.generator.java.types; + +import java.util.List; + +import com.rosetta.model.lib.RosettaModelObject; +import com.rosetta.model.lib.meta.ReferenceWithMeta; +import com.rosetta.util.DottedPath; +import com.rosetta.util.types.JavaClass; +import com.rosetta.util.types.JavaParameterizedType; +import com.rosetta.util.types.JavaReferenceType; +import com.rosetta.util.types.JavaType; +import com.rosetta.util.types.JavaTypeDeclaration; + +public class RJavaReferenceWithMeta extends RJavaWithMetaValue { + private final DottedPath namespace; + private final JavaTypeUtil javaTypeUtil; + private final JavaParameterizedType> referenceWithMetaParameterisedType; + + + public RJavaReferenceWithMeta(JavaReferenceType valueType, DottedPath namespace, JavaTypeUtil javaTypeUtil) { + super(valueType); + this.namespace = namespace; + this.javaTypeUtil = javaTypeUtil; + referenceWithMetaParameterisedType = javaTypeUtil.wrap(javaTypeUtil.REFERENCE_WITH_META, valueType); + } + + @Override + public boolean isSubtypeOf(JavaType other) { + if (referenceWithMetaParameterisedType.isSubtypeOf(other)) { + return true; + } + if (javaTypeUtil.ROSETTA_MODEL_OBJECT.isSubtypeOf(other)) { + return true; + } + if (javaTypeUtil.GLOBAL_KEY.isSubtypeOf(other)) { + return true; + } + return false; + } + + @Override + public String getSimpleName() { + return "ReferenceWithMeta" + valueType.getSimpleName(); + } + + @Override + public JavaTypeDeclaration getSuperclassDeclaration() { + return JavaClass.OBJECT; + } + + @Override + public List> getInterfaceDeclarations() { + return List.of(javaTypeUtil.ROSETTA_MODEL_OBJECT, javaTypeUtil.FIELD_WITH_META); + + } + + @Override + public boolean extendsDeclaration(JavaTypeDeclaration other) { + if (other instanceof JavaClass) { + return this.isSubtypeOf((JavaClass)other); + } + return false; + } + + @Override + public boolean isFinal() { + return false; + } + + @Override + public Class loadClass(ClassLoader classLoader) throws ClassNotFoundException { + return Class.forName(getCanonicalName().toString(), true, classLoader).asSubclass(RosettaModelObject.class); + } + + @Override + public DottedPath getPackageName() { + return namespace; + } + + @Override + public List> getInterfaces() { + return List.of(javaTypeUtil.ROSETTA_MODEL_OBJECT,referenceWithMetaParameterisedType); + + } + +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaWithMetaValue.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaWithMetaValue.java new file mode 100644 index 000000000..c9005a57b --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/types/RJavaWithMetaValue.java @@ -0,0 +1,25 @@ +package com.regnosys.rosetta.generator.java.types; + +import com.rosetta.model.lib.RosettaModelObject; +import com.rosetta.util.types.JavaClass; +import com.rosetta.util.types.JavaReferenceType; + +public abstract class RJavaWithMetaValue extends JavaClass{ + protected final JavaReferenceType valueType; + + + + public RJavaWithMetaValue(JavaReferenceType valueType) { + this.valueType = valueType; + } + + public JavaReferenceType getValueType() { + return valueType; + } + + @Override + public JavaClass getSuperclass() { + return JavaClass.OBJECT; + } + +} diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/validator/ValidatorGenerator.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/validator/ValidatorGenerator.xtend index dbe9b20c5..e9de6a8d6 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/validator/ValidatorGenerator.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/java/validator/ValidatorGenerator.xtend @@ -126,7 +126,7 @@ class ValidatorGenerator { } private def StringConcatenationClient checkTypeFormat(RAttribute attr, String atrVariable) { - val t = attr.RType.stripFromTypeAliases + val t = attr.RMetaAnnotatedType.RType.stripFromTypeAliases if (t instanceof RStringType) { if (t != UNCONSTRAINED_STRING) { val min = t.interval.minBound @@ -149,7 +149,7 @@ class ValidatorGenerator { } private def StringConcatenationClient getAttributeValue(RAttribute attr) { - if (attr.metaAnnotations.empty) { + if (!attr.RMetaAnnotatedType.hasMeta) { '''o.get«attr.name?.toFirstUpper»()''' } else { val jt = attr.toMetaJavaType diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend index e240ebd68..9f9ca86e1 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend @@ -92,7 +92,7 @@ class RosettaFunctionExtensions { } dispatch def boolean needsBuilder(RAttribute rAttribute) { - needsBuilder(rAttribute.RType) + needsBuilder(rAttribute.RMetaAnnotatedType.RType) } dispatch def boolean needsBuilder(RosettaTyped ele) { @@ -100,7 +100,7 @@ class RosettaFunctionExtensions { } dispatch def boolean needsBuilder(RosettaExpression expr) { - needsBuilder(typeProvider.getRType(expr)) + needsBuilder(typeProvider.getRMetaAnnotatedType(expr).RType) } dispatch def boolean needsBuilder(AssignPathRoot root) { @@ -113,7 +113,7 @@ class RosettaFunctionExtensions { dispatch def boolean needsBuilder(RAssignedRoot root) { switch (root) { - RAttribute: root.RType.needsBuilder + RAttribute: root.RMetaAnnotatedType.RType.needsBuilder RShortcut: root.expression.needsBuilder default: false } 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 f80c9e1ff..5205d2f8d 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 @@ -110,7 +110,7 @@ public RosettaValue interpret(RosettaExpression expr, RosettaInterpreterContext @Override protected RosettaValue caseListLiteral(ListLiteral expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); List results = expr.getElements().stream() .flatMap(elem -> interpret(elem, context).getItems().stream()) .collect(Collectors.toList()); @@ -185,7 +185,7 @@ protected RosettaValue caseSymbolReference(RosettaSymbolReference expr, RosettaI @Override protected RosettaValue caseAddOperation(ArithmeticOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); if (typeSystem.isSubtypeOf(type, builtins.UNCONSTRAINED_NUMBER)) { RosettaNumber left = interpret(expr.getLeft(), context).getSingleOrThrow(RosettaNumber.class); RosettaNumber right = interpret(expr.getRight(), context).getSingleOrThrow(RosettaNumber.class); @@ -206,8 +206,8 @@ protected RosettaValue caseAddOperation(ArithmeticOperation expr, RosettaInterpr @Override protected RosettaValue caseSubtractOperation(ArithmeticOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); - RType leftType = typeProvider.getRType(expr.getLeft()); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); + RType leftType = typeProvider.getRMetaAnnotatedType(expr.getLeft()).getRType(); if (typeSystem.isSubtypeOf(leftType, builtins.UNCONSTRAINED_NUMBER)) { RosettaNumber left = interpret(expr.getLeft(), context).getSingleOrThrow(RosettaNumber.class); RosettaNumber right = interpret(expr.getRight(), context).getSingleOrThrow(RosettaNumber.class); @@ -223,7 +223,7 @@ protected RosettaValue caseSubtractOperation(ArithmeticOperation expr, RosettaIn @Override protected RosettaValue caseMultiplyOperation(ArithmeticOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); RosettaNumber left = interpret(expr.getLeft(), context).getSingleOrThrow(RosettaNumber.class); RosettaNumber right = interpret(expr.getRight(), context).getSingleOrThrow(RosettaNumber.class); RosettaNumber result = left.multiply(right); @@ -232,7 +232,7 @@ protected RosettaValue caseMultiplyOperation(ArithmeticOperation expr, RosettaIn @Override protected RosettaValue caseDivideOperation(ArithmeticOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); RosettaNumber left = interpret(expr.getLeft(), context).getSingleOrThrow(RosettaNumber.class); RosettaNumber right = interpret(expr.getRight(), context).getSingleOrThrow(RosettaNumber.class); RosettaNumber result = left.divide(right); @@ -404,7 +404,7 @@ protected RosettaValue caseAbsentOperation(RosettaAbsentExpression expr, Rosetta @Override protected RosettaValue caseCountOperation(RosettaCountOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); RosettaValue arg = interpret(expr.getArgument(), context); RosettaNumber result = RosettaNumber.valueOf(arg.size()); return valueFactory.createOfType(type, result); @@ -438,7 +438,7 @@ protected RosettaValue caseDistinctOperation(DistinctOperation expr, RosettaInte @Override protected RosettaValue caseFirstOperation(FirstOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); RosettaValue arg = interpret(expr.getArgument(), context); if (arg.size() == 0) { return RosettaValue.empty(); @@ -455,7 +455,7 @@ protected RosettaValue caseFlattenOperation(FlattenOperation expr, RosettaInterp @Override protected RosettaValue caseLastOperation(LastOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); RosettaValue arg = interpret(expr.getArgument(), context); if (arg.size() == 0) { return RosettaValue.empty(); @@ -482,7 +482,7 @@ protected RosettaValue caseOnlyElementOperation(RosettaOnlyElement expr, Rosetta @Override protected RosettaValue caseSumOperation(SumOperation expr, RosettaInterpreterContext context) { - RType type = typeProvider.getRType(expr); + RType type = typeProvider.getRMetaAnnotatedType(expr).getRType(); List arg = interpret(expr.getArgument(), context).getItems(RosettaNumber.class); RosettaNumber result = arg.stream().reduce(RosettaNumber.ZERO, RosettaNumber::add); return valueFactory.createOfType(type, result); 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 0c0121be6..34e53fc37 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 @@ -24,9 +24,8 @@ 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.expression.SwitchCaseGuard 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 @@ -34,13 +33,15 @@ 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.types.ExpectedTypeProvider +import com.regnosys.rosetta.types.RChoiceType import com.regnosys.rosetta.types.RDataType import com.regnosys.rosetta.types.REnumType +import com.regnosys.rosetta.types.RMetaAnnotatedType import com.regnosys.rosetta.types.RObjectFactory import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.RosettaTypeProvider 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 @@ -49,23 +50,22 @@ 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.expression.ExpressionPackage.Literals.* import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* -import org.eclipse.xtext.scoping.impl.ImportNormalizer -import org.eclipse.xtext.util.Strings -import com.regnosys.rosetta.types.ExpectedTypeProvider -import com.regnosys.rosetta.types.RChoiceType -import com.regnosys.rosetta.rosetta.expression.SwitchCaseGuard + +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta +import com.regnosys.rosetta.rosetta.RosettaFeature /** * This class contains custom scoping description. @@ -82,7 +82,6 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { @Inject RosettaTypeProvider typeProvider @Inject ExpectedTypeProvider expectedTypeProvider @Inject extension RosettaEcoreUtil - @Inject extension RosettaConfigExtension configs @Inject extension RosettaFunctionExtensions @Inject extension DeepFeatureCallUtil @Inject extension RObjectFactory @@ -101,32 +100,32 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { } case ROSETTA_FEATURE_CALL__FEATURE: { if (context instanceof RosettaFeatureCall) { - return createExtendedFeatureScope(context.receiver, typeProvider.getRType(context.receiver)) + return createExtendedFeatureScope(context.receiver, typeProvider.getRMetaAnnotatedType(context.receiver)) } return IScope.NULLSCOPE } case ROSETTA_DEEP_FEATURE_CALL__FEATURE: { if (context instanceof RosettaDeepFeatureCall) { - return createDeepFeatureScope(typeProvider.getRType(context.receiver)) + return createDeepFeatureScope(typeProvider.getRMetaAnnotatedType(context.receiver).RType) } return IScope.NULLSCOPE } case CHOICE_OPERATION__ATTRIBUTES: { if (context instanceof ChoiceOperation) { - return createExtendedFeatureScope(context.argument, typeProvider.getRType(context.argument)) + return createExtendedFeatureScope(context.argument, typeProvider.getRMetaAnnotatedType(context.argument).RType.withEmptyMeta) } return IScope.NULLSCOPE } case ROSETTA_ATTRIBUTE_REFERENCE__ATTRIBUTE: { if (context instanceof RosettaAttributeReference) { - return createExtendedFeatureScope(context.receiver, typeProvider.getRTypeOfAttributeReference(context.receiver)) + return createExtendedFeatureScope(context.receiver, typeProvider.getRTypeOfAttributeReference(context.receiver).withEmptyMeta) } return IScope.NULLSCOPE } case CONSTRUCTOR_KEY_VALUE_PAIR__KEY: { if (context instanceof ConstructorKeyValuePair) { val constructor = context.eContainer as RosettaConstructorExpression - return Scopes.scopeFor(typeProvider.getRType(constructor).allFeatures(context)) + return Scopes.scopeFor(typeProvider.getRMetaAnnotatedType(constructor).RType.allFeatures(context)) } return IScope.NULLSCOPE } @@ -146,14 +145,14 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { switch (context) { Operation: { val receiverType = typeProvider.getRTypeOfSymbol(context.assignRoot) - return Scopes.scopeFor(receiverType.allFeatures(context)) + return Scopes.scopeFor(receiverType.RType.allFeatures(context)) } Segment: { val prev = context.prev if (prev !== null) { if (prev.attribute.isResolved) { val receiverType = typeProvider.getRTypeOfSymbol(prev.attribute) - return Scopes.scopeFor(receiverType.allFeatures(context)) + return Scopes.scopeFor(receiverType.RType.allFeatures(context)) } } if (context.eContainer instanceof Operation) { @@ -177,7 +176,7 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { } else { var implicitFeatures = typeProvider.findFeaturesOfImplicitVariable(context) - val expectedType = expectedTypeProvider.getExpectedTypeFromContainer(context) + val expectedType = expectedTypeProvider.getExpectedTypeFromContainer(context)?.RType if (expectedType instanceof REnumType) { implicitFeatures = implicitFeatures + expectedType.allEnumValues } @@ -248,7 +247,7 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { } case SWITCH_CASE_GUARD__SYMBOL_GUARD: { if (context instanceof SwitchCaseGuard) { - val argumentType = typeProvider.getRType(context.^case.switchOperation.argument) + val argumentType = typeProvider.getRMetaAnnotatedType(context.^case.switchOperation.argument).RType if (argumentType instanceof REnumType) { return Scopes.scopeFor(argumentType.allEnumValues) } else if (argumentType instanceof RChoiceType) { @@ -371,45 +370,25 @@ class RosettaScopeProvider extends ImportedNamespaceAwareLocalScopeProvider { new FilteringScope(scope,filter) } - private def IScope createExtendedFeatureScope(EObject receiver, RType receiverType) { + private def IScope createExtendedFeatureScope(EObject receiver, RMetaAnnotatedType metaReceiverType) { + val receiverType = metaReceiverType.RType if (receiverType instanceof REnumType) { if (!(receiver instanceof RosettaSymbolReference) || !((receiver as RosettaSymbolReference).symbol instanceof RosettaEnumeration)) { return IScope.NULLSCOPE } } + val List foo = metaReceiverType.allFeatures(receiver).toList + val List allPosibilities = newArrayList allPosibilities.addAll( - receiverType.allFeatures(receiver) + foo .map[new EObjectDescription(QualifiedName.create(name), it, null)] - ) - //if an attribute has metafields then the meta names are valid in a feature call e.g. -> currency -> scheme - val feature = if (receiver instanceof RosettaFeatureCall) { - receiver.feature - } else if (receiver instanceof RosettaDeepFeatureCall) { - receiver.feature - } else if (receiver instanceof RosettaSymbolReference) { - receiver.symbol - } - if (feature instanceof Attribute) { - 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 - } - } + private def IScope createDeepFeatureScope(RType receiverType) { val t = if (receiverType instanceof RChoiceType) { 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 cad3bb579..d9cd65dc3 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 @@ -78,17 +78,18 @@ import java.util.Objects; import javax.inject.Inject; +import static com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta; @ImplementedBy(ExpectedTypeProvider.Impl.class) public interface ExpectedTypeProvider { static final int INSIGNIFICANT_INDEX = -1; - RType getExpectedTypeFromContainer(EObject owner); - default RType getExpectedType(EObject owner, EReference reference) { + RMetaAnnotatedType getExpectedTypeFromContainer(EObject owner); + default RMetaAnnotatedType getExpectedType(EObject owner, EReference reference) { return getExpectedType(owner, reference, INSIGNIFICANT_INDEX); } - RType getExpectedType(EObject owner, EReference reference, int index); + RMetaAnnotatedType getExpectedType(EObject owner, EReference reference, int index); public static class Impl implements ExpectedTypeProvider { private static Logger LOGGER = LoggerFactory.getLogger(Impl.class); @@ -109,7 +110,7 @@ public Impl(RBuiltinTypeService builtins, RosettaTypeProvider typeProvider, Type } @Override - public RType getExpectedTypeFromContainer(EObject owner) { + public RMetaAnnotatedType getExpectedTypeFromContainer(EObject owner) { EObject container = owner.eContainer(); EReference reference = owner.eContainmentFeature(); if (container == null || reference == null) { @@ -122,8 +123,8 @@ public RType getExpectedTypeFromContainer(EObject owner) { return getExpectedType(container, reference); } @Override - public RType getExpectedType(EObject owner, EReference reference, int index) { - return cache.get(new CacheKey(owner, reference, index), () -> { + public RMetaAnnotatedType getExpectedType(EObject owner, EReference reference, int index) { + return cache.get(new CacheKey(owner, reference, index), () -> { if (OPERATION__EXPRESSION.equals(reference) && owner instanceof Operation) { Operation op = (Operation)owner; if(op.getPath() == null) { @@ -141,13 +142,13 @@ public RType getExpectedType(EObject owner, EReference reference, int index) { if (operation instanceof ReduceOperation) { return getExpectedTypeFromContainer(operation); } else if (operation instanceof FilterOperation) { - return builtins.BOOLEAN; + return withEmptyMeta(builtins.BOOLEAN); } else if (operation instanceof MapOperation) { return getExpectedTypeFromContainer(operation); } else if (operation instanceof ThenOperation) { return getExpectedTypeFromContainer(operation); } else if (operation instanceof ComparingFunctionalOperation) { - return builtins.BOOLEAN; + return withEmptyMeta(builtins.BOOLEAN); } else { LOGGER.debug("Unexpected functional operation of type " + operation.getClass().getCanonicalName()); } @@ -198,8 +199,8 @@ public Context(EReference reference, int index) { this.index = index; } } - private class ExpectedTypeSwitch extends RosettaExpressionSwitch { - public RType doSwitch(RosettaExpression expr, EReference reference, int index) { + private class ExpectedTypeSwitch extends RosettaExpressionSwitch { + public RMetaAnnotatedType doSwitch(RosettaExpression expr, EReference reference, int index) { return doSwitch(expr, new Context(reference, index)); } @@ -228,12 +229,12 @@ private boolean leavesItemTypeUnchanged(RosettaExpression expr) { } @Override - protected RType caseConstructorExpression(RosettaConstructorExpression expr, Context context) { + protected RMetaAnnotatedType caseConstructorExpression(RosettaConstructorExpression expr, Context context) { return null; } @Override - protected RType caseListLiteral(ListLiteral expr, Context context) { + protected RMetaAnnotatedType caseListLiteral(ListLiteral expr, Context context) { if (LIST_LITERAL__ELEMENTS.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -241,63 +242,63 @@ protected RType caseListLiteral(ListLiteral expr, Context context) { } @Override - protected RType caseConditionalExpression(RosettaConditionalExpression expr, Context context) { + protected RMetaAnnotatedType caseConditionalExpression(RosettaConditionalExpression expr, Context context) { if (ROSETTA_CONDITIONAL_EXPRESSION__IF.equals(context.reference)) { - return builtins.BOOLEAN; + return withEmptyMeta(builtins.BOOLEAN); } else if (ROSETTA_CONDITIONAL_EXPRESSION__IFTHEN.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } else if (ROSETTA_CONDITIONAL_EXPRESSION__ELSETHEN.equals(context.reference)) { - RType expectedType = getExpectedTypeFromContainer(expr); + RMetaAnnotatedType expectedType = getExpectedTypeFromContainer(expr); if (expectedType != null) { return expectedType; } - return typeProvider.getRType(expr.getIfthen()); + return typeProvider.getRMetaAnnotatedType(expr.getIfthen()); } return null; } @Override - protected RType caseFeatureCall(RosettaFeatureCall expr, Context context) { + protected RMetaAnnotatedType caseFeatureCall(RosettaFeatureCall expr, Context context) { return null; } @Override - protected RType caseDeepFeatureCall(RosettaDeepFeatureCall expr, Context context) { + protected RMetaAnnotatedType caseDeepFeatureCall(RosettaDeepFeatureCall expr, Context context) { return null; } @Override - protected RType caseBooleanLiteral(RosettaBooleanLiteral expr, Context context) { + protected RMetaAnnotatedType caseBooleanLiteral(RosettaBooleanLiteral expr, Context context) { return null; } @Override - protected RType caseIntLiteral(RosettaIntLiteral expr, Context context) { + protected RMetaAnnotatedType caseIntLiteral(RosettaIntLiteral expr, Context context) { return null; } @Override - protected RType caseNumberLiteral(RosettaNumberLiteral expr, Context context) { + protected RMetaAnnotatedType caseNumberLiteral(RosettaNumberLiteral expr, Context context) { return null; } @Override - protected RType caseStringLiteral(RosettaStringLiteral expr, Context context) { + protected RMetaAnnotatedType caseStringLiteral(RosettaStringLiteral expr, Context context) { return null; } @Override - protected RType caseOnlyExists(RosettaOnlyExistsExpression expr, Context context) { + protected RMetaAnnotatedType caseOnlyExists(RosettaOnlyExistsExpression expr, Context context) { return null; } @Override - protected RType caseImplicitVariable(RosettaImplicitVariable expr, Context context) { + protected RMetaAnnotatedType caseImplicitVariable(RosettaImplicitVariable expr, Context context) { return null; } @Override - protected RType caseSymbolReference(RosettaSymbolReference expr, Context context) { + protected RMetaAnnotatedType caseSymbolReference(RosettaSymbolReference expr, Context context) { if (ROSETTA_SYMBOL_REFERENCE__RAW_ARGS.equals(context.reference)) { RosettaSymbol symbol = expr.getSymbol(); if (symbol instanceof Function) { @@ -305,109 +306,109 @@ protected RType caseSymbolReference(RosettaSymbolReference expr, Context context return typeProvider.getRTypeOfSymbol(fun.getInputs().get(context.index)); } else if (symbol instanceof RosettaRule) { RosettaRule rule = (RosettaRule)symbol; - return typeSystem.typeCallToRType(rule.getInput()); + return withEmptyMeta(typeSystem.typeCallToRType(rule.getInput())); } } return null; } @Override - protected RType caseAddOperation(ArithmeticOperation expr, Context context) { + protected RMetaAnnotatedType caseAddOperation(ArithmeticOperation expr, Context context) { return null; } @Override - protected RType caseSubtractOperation(ArithmeticOperation expr, Context context) { + protected RMetaAnnotatedType caseSubtractOperation(ArithmeticOperation expr, Context context) { return null; } @Override - protected RType caseMultiplyOperation(ArithmeticOperation expr, Context context) { - return builtins.UNCONSTRAINED_NUMBER; + protected RMetaAnnotatedType caseMultiplyOperation(ArithmeticOperation expr, Context context) { + return withEmptyMeta(builtins.UNCONSTRAINED_NUMBER); } @Override - protected RType caseDivideOperation(ArithmeticOperation expr, Context context) { - return builtins.UNCONSTRAINED_NUMBER; + protected RMetaAnnotatedType caseDivideOperation(ArithmeticOperation expr, Context context) { + return withEmptyMeta(builtins.UNCONSTRAINED_NUMBER); } @Override - protected RType caseJoinOperation(JoinOperation expr, Context context) { - return builtins.UNCONSTRAINED_STRING; + protected RMetaAnnotatedType caseJoinOperation(JoinOperation expr, Context context) { + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } @Override - protected RType caseAndOperation(LogicalOperation expr, Context context) { - return builtins.BOOLEAN; + protected RMetaAnnotatedType caseAndOperation(LogicalOperation expr, Context context) { + return withEmptyMeta(builtins.BOOLEAN); } @Override - protected RType caseOrOperation(LogicalOperation expr, Context context) { - return builtins.BOOLEAN; + protected RMetaAnnotatedType caseOrOperation(LogicalOperation expr, Context context) { + return withEmptyMeta(builtins.BOOLEAN); } @Override - protected RType caseLessThanOperation(ComparisonOperation expr, Context context) { + protected RMetaAnnotatedType caseLessThanOperation(ComparisonOperation expr, Context context) { return null; } @Override - protected RType caseLessThanOrEqualOperation(ComparisonOperation expr, Context context) { + protected RMetaAnnotatedType caseLessThanOrEqualOperation(ComparisonOperation expr, Context context) { return null; } @Override - protected RType caseGreaterThanOperation(ComparisonOperation expr, Context context) { + protected RMetaAnnotatedType caseGreaterThanOperation(ComparisonOperation expr, Context context) { return null; } @Override - protected RType caseGreaterThanOrEqualOperation(ComparisonOperation expr, Context context) { + protected RMetaAnnotatedType caseGreaterThanOrEqualOperation(ComparisonOperation expr, Context context) { return null; } @Override - protected RType caseEqualsOperation(EqualityOperation expr, Context context) { + protected RMetaAnnotatedType caseEqualsOperation(EqualityOperation expr, Context context) { if (ROSETTA_BINARY_OPERATION__RIGHT.equals(context.reference)) { - return typeProvider.getRType(expr.getLeft()); + return typeProvider.getRMetaAnnotatedType(expr.getLeft()); } return null; } @Override - protected RType caseNotEqualsOperation(EqualityOperation expr, Context context) { + protected RMetaAnnotatedType caseNotEqualsOperation(EqualityOperation expr, Context context) { if (ROSETTA_BINARY_OPERATION__RIGHT.equals(context.reference)) { - return typeProvider.getRType(expr.getLeft()); + return typeProvider.getRMetaAnnotatedType(expr.getLeft()); } return null; } @Override - protected RType caseContainsOperation(RosettaContainsExpression expr, Context context) { + protected RMetaAnnotatedType caseContainsOperation(RosettaContainsExpression expr, Context context) { if (ROSETTA_BINARY_OPERATION__RIGHT.equals(context.reference)) { - return typeProvider.getRType(expr.getLeft()); + return typeProvider.getRMetaAnnotatedType(expr.getLeft()); } return null; } @Override - protected RType caseDisjointOperation(RosettaDisjointExpression expr, Context context) { + protected RMetaAnnotatedType caseDisjointOperation(RosettaDisjointExpression expr, Context context) { if (ROSETTA_BINARY_OPERATION__RIGHT.equals(context.reference)) { - return typeProvider.getRType(expr.getLeft()); + return typeProvider.getRMetaAnnotatedType(expr.getLeft()); } return null; } @Override - protected RType caseDefaultOperation(DefaultOperation expr, Context context) { + protected RMetaAnnotatedType caseDefaultOperation(DefaultOperation expr, Context context) { if (ROSETTA_BINARY_OPERATION__RIGHT.equals(context.reference)) { - return typeProvider.getRType(expr.getLeft()); + return typeProvider.getRMetaAnnotatedType(expr.getLeft()); } return null; } @Override - protected RType caseAsKeyOperation(AsKeyOperation expr, Context context) { + protected RMetaAnnotatedType caseAsKeyOperation(AsKeyOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -415,32 +416,32 @@ protected RType caseAsKeyOperation(AsKeyOperation expr, Context context) { } @Override - protected RType caseChoiceOperation(ChoiceOperation expr, Context context) { + protected RMetaAnnotatedType caseChoiceOperation(ChoiceOperation expr, Context context) { return null; } @Override - protected RType caseOneOfOperation(OneOfOperation expr, Context context) { + protected RMetaAnnotatedType caseOneOfOperation(OneOfOperation expr, Context context) { return null; } @Override - protected RType caseAbsentOperation(RosettaAbsentExpression expr, Context context) { + protected RMetaAnnotatedType caseAbsentOperation(RosettaAbsentExpression expr, Context context) { return null; } @Override - protected RType caseCountOperation(RosettaCountOperation expr, Context context) { + protected RMetaAnnotatedType caseCountOperation(RosettaCountOperation expr, Context context) { return null; } @Override - protected RType caseExistsOperation(RosettaExistsExpression expr, Context context) { + protected RMetaAnnotatedType caseExistsOperation(RosettaExistsExpression expr, Context context) { return null; } @Override - protected RType caseDistinctOperation(DistinctOperation expr, Context context) { + protected RMetaAnnotatedType caseDistinctOperation(DistinctOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -448,7 +449,7 @@ protected RType caseDistinctOperation(DistinctOperation expr, Context context) { } @Override - protected RType caseFirstOperation(FirstOperation expr, Context context) { + protected RMetaAnnotatedType caseFirstOperation(FirstOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -456,7 +457,7 @@ protected RType caseFirstOperation(FirstOperation expr, Context context) { } @Override - protected RType caseFlattenOperation(FlattenOperation expr, Context context) { + protected RMetaAnnotatedType caseFlattenOperation(FlattenOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -464,7 +465,7 @@ protected RType caseFlattenOperation(FlattenOperation expr, Context context) { } @Override - protected RType caseLastOperation(LastOperation expr, Context context) { + protected RMetaAnnotatedType caseLastOperation(LastOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -472,7 +473,7 @@ protected RType caseLastOperation(LastOperation expr, Context context) { } @Override - protected RType caseReverseOperation(ReverseOperation expr, Context context) { + protected RMetaAnnotatedType caseReverseOperation(ReverseOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -480,7 +481,7 @@ protected RType caseReverseOperation(ReverseOperation expr, Context context) { } @Override - protected RType caseOnlyElementOperation(RosettaOnlyElement expr, Context context) { + protected RMetaAnnotatedType caseOnlyElementOperation(RosettaOnlyElement expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -488,73 +489,73 @@ protected RType caseOnlyElementOperation(RosettaOnlyElement expr, Context contex } @Override - protected RType caseSumOperation(SumOperation expr, Context context) { + protected RMetaAnnotatedType caseSumOperation(SumOperation expr, Context context) { return null; } @Override - protected RType caseToStringOperation(ToStringOperation expr, Context context) { + protected RMetaAnnotatedType caseToStringOperation(ToStringOperation expr, Context context) { return null; } @Override - protected RType caseToNumberOperation(ToNumberOperation expr, Context context) { + protected RMetaAnnotatedType caseToNumberOperation(ToNumberOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { - return builtins.UNCONSTRAINED_STRING; + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } return null; } @Override - protected RType caseToIntOperation(ToIntOperation expr, Context context) { + protected RMetaAnnotatedType caseToIntOperation(ToIntOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { - return builtins.UNCONSTRAINED_STRING; + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } return null; } @Override - protected RType caseToTimeOperation(ToTimeOperation expr, Context context) { + protected RMetaAnnotatedType caseToTimeOperation(ToTimeOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { - return builtins.UNCONSTRAINED_STRING; + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } return null; } @Override - protected RType caseToEnumOperation(ToEnumOperation expr, Context context) { + protected RMetaAnnotatedType caseToEnumOperation(ToEnumOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { - return builtins.UNCONSTRAINED_STRING; + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } return null; } @Override - protected RType caseToDateOperation(ToDateOperation expr, Context context) { + protected RMetaAnnotatedType caseToDateOperation(ToDateOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { - return builtins.UNCONSTRAINED_STRING; + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } return null; } @Override - protected RType caseToDateTimeOperation(ToDateTimeOperation expr, Context context) { + protected RMetaAnnotatedType caseToDateTimeOperation(ToDateTimeOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { - return builtins.UNCONSTRAINED_STRING; + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } return null; } @Override - protected RType caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Context context) { + protected RMetaAnnotatedType caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { - return builtins.UNCONSTRAINED_STRING; + return withEmptyMeta(builtins.UNCONSTRAINED_STRING); } return null; } @Override - protected RType caseFilterOperation(FilterOperation expr, Context context) { + protected RMetaAnnotatedType caseFilterOperation(FilterOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -562,7 +563,7 @@ protected RType caseFilterOperation(FilterOperation expr, Context context) { } @Override - protected RType caseMapOperation(MapOperation expr, Context context) { + protected RMetaAnnotatedType caseMapOperation(MapOperation expr, Context context) { InlineFunction f = expr.getFunction(); if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference) && f != null && leavesItemTypeUnchanged(f.getBody())) { return getExpectedTypeFromContainer(expr); @@ -571,7 +572,7 @@ protected RType caseMapOperation(MapOperation expr, Context context) { } @Override - protected RType caseMaxOperation(MaxOperation expr, Context context) { + protected RMetaAnnotatedType caseMaxOperation(MaxOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -579,7 +580,7 @@ protected RType caseMaxOperation(MaxOperation expr, Context context) { } @Override - protected RType caseMinOperation(MinOperation expr, Context context) { + protected RMetaAnnotatedType caseMinOperation(MinOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -587,12 +588,12 @@ protected RType caseMinOperation(MinOperation expr, Context context) { } @Override - protected RType caseReduceOperation(ReduceOperation expr, Context context) { + protected RMetaAnnotatedType caseReduceOperation(ReduceOperation expr, Context context) { return null; } @Override - protected RType caseSortOperation(SortOperation expr, Context context) { + protected RMetaAnnotatedType caseSortOperation(SortOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { return getExpectedTypeFromContainer(expr); } @@ -600,7 +601,7 @@ protected RType caseSortOperation(SortOperation expr, Context context) { } @Override - protected RType caseThenOperation(ThenOperation expr, Context context) { + protected RMetaAnnotatedType caseThenOperation(ThenOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference) && leavesItemTypeUnchanged(expr.getFunction().getBody())) { return getExpectedTypeFromContainer(expr); } @@ -608,7 +609,7 @@ protected RType caseThenOperation(ThenOperation expr, Context context) { } @Override - protected RType caseSwitchOperation(SwitchOperation expr, Context context) { + protected RMetaAnnotatedType caseSwitchOperation(SwitchOperation expr, Context context) { if (ROSETTA_UNARY_OPERATION__ARGUMENT.equals(context.reference)) { if (expr.getCases().stream().allMatch(c -> leavesItemTypeUnchanged(c.getExpression())) && (expr.getDefault() == null || leavesItemTypeUnchanged(expr.getDefault()))) { return getExpectedTypeFromContainer(expr); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAliasType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAliasType.java index 29ed8baf8..44a05c467 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAliasType.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAliasType.java @@ -45,11 +45,6 @@ public RTypeFunction getTypeFunction() { public RType getRefersTo() { return refersTo; } - - @Override - public boolean hasMeta() { - return refersTo.hasMeta(); - } @Override public boolean hasNaturalOrder() { diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAnnotateType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAnnotateType.java deleted file mode 100644 index b370a1add..000000000 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RAnnotateType.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 REGnosys - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.regnosys.rosetta.types; - -public abstract class RAnnotateType extends RType { - private boolean meta = false; - - public void setWithMeta(final boolean meta) { - this.meta = meta; - } - - @Override - public boolean hasMeta() { - return this.meta; - } -} 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 a12aa9d14..7333f36e8 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 @@ -28,22 +28,20 @@ public class RAttribute implements RAssignedRoot { private final String name; private final String definition; private final List docReferences; - private final RType rType; - private final List metaAnnotations; + private final RMetaAnnotatedType rMetaAnnotatedType; private final PositiveIntegerInterval cardinality; private final boolean isMeta; private final RosettaRule ruleReference; private final Attribute origin; - public RAttribute(String name, String definition, List docReferences, RType rType, List metaAnnotations, PositiveIntegerInterval cardinality, RosettaRule ruleReference, Attribute origin) { - this(name, definition, docReferences, rType, metaAnnotations, cardinality, false, ruleReference, origin); + public RAttribute(String name, String definition, List docReferences, RMetaAnnotatedType rMetaAnnotatedType, PositiveIntegerInterval cardinality, RosettaRule ruleReference, Attribute origin) { + this(name, definition, docReferences, rMetaAnnotatedType, cardinality, false, ruleReference, origin); } - public RAttribute(String name, String definition, List docReferences, RType rType, List metaAnnotations, PositiveIntegerInterval cardinality, boolean isMeta, RosettaRule ruleReference, Attribute origin) { + public RAttribute(String name, String definition, List docReferences, RMetaAnnotatedType rMetaAnnotatedType, PositiveIntegerInterval cardinality, boolean isMeta, RosettaRule ruleReference, Attribute origin) { this.name = name; this.definition = definition; this.docReferences = docReferences; - this.rType = rType; - this.metaAnnotations = metaAnnotations; + this.rMetaAnnotatedType = rMetaAnnotatedType; this.cardinality = cardinality; this.isMeta = isMeta; this.ruleReference = ruleReference; @@ -59,10 +57,10 @@ public Attribute getEObject() { return origin; } - public RType getRType() { - return rType; + public RMetaAnnotatedType getRMetaAnnotatedType() { + return rMetaAnnotatedType; } - + @Override public boolean isMulti() { return cardinality.getMax().map(m -> m > 1).orElse(true); @@ -80,15 +78,7 @@ public List getDocReferences() { return docReferences; } - public List getMetaAnnotations() { - return metaAnnotations; - } - - @Deprecated - public boolean hasReferenceOrAddressMetadata() { - return metaAnnotations.stream().anyMatch(a -> a.getName().equals("reference") || a.getName().equals("address")); - } - + public RosettaRule getRuleReference() { return ruleReference; } @@ -99,7 +89,7 @@ public boolean isMeta() { @Override public int hashCode() { - return Objects.hash(definition, cardinality, metaAnnotations, name, rType, origin); + return Objects.hash(definition, cardinality, name, rMetaAnnotatedType, origin); } @Override @@ -112,13 +102,13 @@ public boolean equals(Object obj) { return false; RAttribute other = (RAttribute) obj; return Objects.equals(definition, other.definition) && Objects.equals(cardinality, other.cardinality) - && Objects.equals(metaAnnotations, other.metaAnnotations) && Objects.equals(name, other.name) - && Objects.equals(rType, other.rType) + && Objects.equals(name, other.name) + && Objects.equals(rMetaAnnotatedType, other.rMetaAnnotatedType) && Objects.equals(origin, other.origin); } @Override public String toString() { - return String.format("RAttribute[name=%s, type=%s, cardinality=%s]", name, rType, cardinality); + return String.format("RAttribute[name=%s, type=%s, cardinality=%s]", name, rMetaAnnotatedType, cardinality); } } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceOption.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceOption.java index cf1d3d787..282f9b483 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceOption.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceOption.java @@ -7,7 +7,7 @@ public class RChoiceOption implements RObject { private final ChoiceOption option; - private RType type = null; + private RMetaAnnotatedType type = null; private final RChoiceType choiceType; @@ -25,7 +25,7 @@ public ChoiceOption getEObject() { return option; } - public RType getType() { + public RMetaAnnotatedType getType() { if (type == null) { type = typeProvider.getRTypeOfSymbol(option); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceType.java index abe83881d..4ee96fa12 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceType.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RChoiceType.java @@ -13,7 +13,7 @@ import com.regnosys.rosetta.utils.ModelIdProvider; import com.rosetta.model.lib.ModelSymbolId; -public class RChoiceType extends RAnnotateType implements RObject { +public class RChoiceType extends RType implements RObject { private final Choice choice; private ModelSymbolId symbolId = null; @@ -75,8 +75,8 @@ public List getAllOptions() { private Stream doGetAllOptions(Set visited) { if (visited.add(this)) { return getOwnOptions().stream().flatMap(o -> { - if (o.getType() instanceof RChoiceType) { - RChoiceType nested = (RChoiceType) o.getType(); + if (o.getType().getRType() instanceof RChoiceType) { + RChoiceType nested = (RChoiceType) o.getType().getRType(); return Streams.concat(Stream.of(o), nested.doGetAllOptions(visited)); } return Stream.of(o); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RDataType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RDataType.java index 7be28600a..3d4d00a6a 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RDataType.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RDataType.java @@ -31,7 +31,7 @@ import com.regnosys.rosetta.utils.ModelIdProvider; import com.rosetta.model.lib.ModelSymbolId; -public class RDataType extends RAnnotateType implements RObject { +public class RDataType extends RType implements RObject { private final Data data; private RDataType superType = null; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/REnumType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/REnumType.java index ecb6c4ff1..c9573a41c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/REnumType.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/REnumType.java @@ -28,7 +28,7 @@ import com.regnosys.rosetta.utils.ModelIdProvider; import com.rosetta.model.lib.ModelSymbolId; -public class REnumType extends RAnnotateType implements RObject { +public class REnumType extends RType implements RObject { private final RosettaEnumeration enumeration; private ModelSymbolId symbolId = null; diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAnnotatedType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAnnotatedType.java new file mode 100644 index 000000000..5899d01c3 --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAnnotatedType.java @@ -0,0 +1,63 @@ +package com.regnosys.rosetta.types; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.Validate; + +public class RMetaAnnotatedType { + private final RType rType; + private final List metaAttributes; + + public RMetaAnnotatedType(RType rType, List metaAttributes) { + this.rType = rType; + this.metaAttributes = Validate.noNullElements(metaAttributes); + } + + public static RMetaAnnotatedType withEmptyMeta(RType rType) { + return new RMetaAnnotatedType(rType, List.of()); + } + + public static RMetaAnnotatedType withMeta(RType rType, List metaAttributes) { + return new RMetaAnnotatedType(rType, metaAttributes); + } + + public RType getRType() { + return rType; + } + + public boolean hasMeta() { + return !metaAttributes.isEmpty(); + } + + public List getMetaAttributes() { + return metaAttributes; + } + + @Override + public int hashCode() { + return Objects.hash(rType, metaAttributes); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RMetaAnnotatedType other = (RMetaAnnotatedType) obj; + return Objects.equals(rType, other.rType) && Objects.equals(metaAttributes, other.metaAttributes); + } + + @Override + public String toString() { + if (metaAttributes.isEmpty()) { + return rType.toString(); + } + return rType.toString() + " with " + metaAttributes.stream().map(RMetaAttribute::getName).collect(Collectors.joining(", ")); + } + +} 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 new file mode 100644 index 000000000..e669984b9 --- /dev/null +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RMetaAttribute.java @@ -0,0 +1,56 @@ +package com.regnosys.rosetta.types; + +import java.util.Objects; + +import org.eclipse.emf.ecore.EObject; + +import com.regnosys.rosetta.rosetta.simple.Attribute; + +public class RMetaAttribute implements RObject { + private final String name; + private final RType type; + private Attribute attribute; + + public RMetaAttribute(String name, RType type, Attribute attribute) { + this.name = name; + this.type = type; + this.attribute = attribute; + } + + public String getName() { + return name; + } + + public RType getRType() { + return type; + } + + @Override + public int hashCode() { + return Objects.hash(name, type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RMetaAttribute other = (RMetaAttribute) obj; + return Objects.equals(name, other.name) && Objects.equals(type, other.type); + } + + @Override + 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 6bfe04720..657d4fe2f 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 @@ -75,11 +75,12 @@ public RFunction buildRFunction(Function function) { // TODO: should be private public RAttribute createArtificialAttribute(String name, RType type, boolean isMulti) { - return new RAttribute(name, null, Collections.emptyList(), type, List.of(), isMulti ? PositiveIntegerInterval.boundedLeft(0) : PositiveIntegerInterval.bounded(0, 1), null, null); + RMetaAnnotatedType rAnnotatedType = new RMetaAnnotatedType(type, List.of()); + return new RAttribute(name, null, Collections.emptyList(), rAnnotatedType, isMulti ? PositiveIntegerInterval.boundedLeft(0) : PositiveIntegerInterval.bounded(0, 1), null, null); } public RFunction buildRFunction(RosettaRule rule) { RType inputRType = typeSystem.typeCallToRType(rule.getInput()); - RType outputRType = typeProvider.getRType(rule.getExpression()); + RType outputRType = typeProvider.getRMetaAnnotatedType(rule.getExpression()).getRType(); boolean outputIsMulti = cardinalityProvider.isMulti(rule.getExpression()); RAttribute outputAttribute = createArtificialAttribute("output", outputRType, outputIsMulti); @@ -146,7 +147,7 @@ private List generateReportOperations(RDataType reportDataType, Map< operations.add(generateOperationForRuleReference(inputAttribute, attributeToRuleMap.get(attribute), newAssignPath)); continue; } - RType attrType = attribute.getRType() instanceof RChoiceType ? ((RChoiceType)attribute.getRType()).asRDataType() : attribute.getRType(); + RType attrType = attribute.getRMetaAnnotatedType().getRType() instanceof RChoiceType ? ((RChoiceType)attribute.getRMetaAnnotatedType().getRType()).asRDataType() : attribute.getRMetaAnnotatedType().getRType(); if (attrType instanceof RDataType) { RDataType rData = (RDataType) attrType; operations.addAll(generateReportOperations(rData, attributeToRuleMap, inputAttribute, newAssignPath)); @@ -170,21 +171,16 @@ private ROperation generateOperationForRuleReference(Attribute inputAttribute, R return new ROperation(ROperationType.SET, pathHead, pathTail, symbolRef); } - + public RAttribute buildRAttribute(Attribute attribute) { - return buildRAttribute(attribute, attribute.getTypeCall().getType() instanceof RosettaMetaType); - } - private RAttribute buildRAttribute(Attribute attribute, boolean isMeta) { - RType rType = typeProvider.getRTypeOfSymbol(attribute); - List metaAnnotations = attribute.getAnnotations().stream() - .filter(a -> a.getAnnotation().getName().equals("metadata") && a.getAttribute() != null).map(a -> buildRAttribute(a.getAttribute(), true)) - .collect(Collectors.toList()); + RMetaAnnotatedType rAnnotatedType = typeProvider.getRTypeOfSymbol(attribute); + boolean isMeta = attribute.getTypeCall().getType() instanceof RosettaMetaType; PositiveIntegerInterval card = new PositiveIntegerInterval( attribute.getCard().getInf(), attribute.getCard().isUnbounded() ? Optional.empty() : Optional.of(attribute.getCard().getSup())); RosettaRuleReference ruleRef = attribute.getRuleReference(); - return new RAttribute(attribute.getName(), attribute.getDefinition(), attribute.getReferences(), rType, metaAnnotations, + return new RAttribute(attribute.getName(), attribute.getDefinition(), attribute.getReferences(), rAnnotatedType, card, isMeta, ruleRef != null ? ruleRef.getReportingRule() : null, attribute); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RType.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RType.java index a994f3682..495aa6087 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RType.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/RType.java @@ -19,9 +19,6 @@ import com.rosetta.model.lib.ModelSymbol; public abstract class RType implements ModelSymbol { - public boolean hasMeta() { - return false; - } public boolean hasNaturalOrder() { return false; 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 99cf697c3..1cf0e47c4 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 @@ -221,6 +221,7 @@ auxiliary typeFunctionOfTypeAlias(RosettaTypeAlias typeAlias) { auxiliary typeCallToRType(TypeCall call, RosettaInterpreterContext context) { val t = call.type + switch t { Choice: t.buildRChoiceType Data: t.buildRDataType 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 cacbc4d8c..defc59a88 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 @@ -1,91 +1,95 @@ package com.regnosys.rosetta.types -import com.regnosys.rosetta.rosetta.expression.RosettaAbsentExpression -import com.regnosys.rosetta.rosetta.expression.RosettaNumberLiteral -import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation -import com.regnosys.rosetta.rosetta.expression.RosettaBooleanLiteral -import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression -import com.regnosys.rosetta.rosetta.expression.RosettaCountOperation +import com.regnosys.rosetta.RosettaEcoreUtil +import com.regnosys.rosetta.cache.IRequestScopedCache +import com.regnosys.rosetta.rosetta.RosettaAttributeReference +import com.regnosys.rosetta.rosetta.RosettaAttributeReferenceSegment +import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs +import com.regnosys.rosetta.rosetta.RosettaDataReference import com.regnosys.rosetta.rosetta.RosettaEnumValue import com.regnosys.rosetta.rosetta.RosettaEnumeration -import com.regnosys.rosetta.rosetta.expression.RosettaExistsExpression -import com.regnosys.rosetta.rosetta.expression.RosettaExpression import com.regnosys.rosetta.rosetta.RosettaExternalFunction -import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall -import com.regnosys.rosetta.rosetta.expression.RosettaIntLiteral -import com.regnosys.rosetta.rosetta.expression.RosettaOnlyExistsExpression -import com.regnosys.rosetta.rosetta.expression.RosettaStringLiteral +import com.regnosys.rosetta.rosetta.RosettaFeature +import com.regnosys.rosetta.rosetta.RosettaRule +import com.regnosys.rosetta.rosetta.RosettaSymbol import com.regnosys.rosetta.rosetta.RosettaTypedFeature -import com.regnosys.rosetta.rosetta.simple.Annotated +import com.regnosys.rosetta.rosetta.TypeParameter +import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation +import com.regnosys.rosetta.rosetta.expression.AsKeyOperation +import com.regnosys.rosetta.rosetta.expression.ChoiceOperation import com.regnosys.rosetta.rosetta.expression.ClosureParameter -import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.rosetta.simple.Function -import com.regnosys.rosetta.rosetta.expression.ListLiteral -import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration -import java.util.Map -import org.eclipse.emf.ecore.EObject -import org.eclipse.xtext.naming.IQualifiedNameProvider -import com.regnosys.rosetta.rosetta.expression.RosettaOnlyElement -import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation -import com.regnosys.rosetta.rosetta.expression.FilterOperation -import com.regnosys.rosetta.rosetta.expression.ReverseOperation -import com.regnosys.rosetta.rosetta.expression.FlattenOperation +import com.regnosys.rosetta.rosetta.expression.ComparisonOperation +import com.regnosys.rosetta.rosetta.expression.DefaultOperation import com.regnosys.rosetta.rosetta.expression.DistinctOperation +import com.regnosys.rosetta.rosetta.expression.EqualityOperation +import com.regnosys.rosetta.rosetta.expression.FilterOperation import com.regnosys.rosetta.rosetta.expression.FirstOperation +import com.regnosys.rosetta.rosetta.expression.FlattenOperation +import com.regnosys.rosetta.rosetta.expression.JoinOperation import com.regnosys.rosetta.rosetta.expression.LastOperation -import com.regnosys.rosetta.rosetta.expression.ReduceOperation +import com.regnosys.rosetta.rosetta.expression.ListLiteral +import com.regnosys.rosetta.rosetta.expression.LogicalOperation import com.regnosys.rosetta.rosetta.expression.MapOperation -import com.regnosys.rosetta.rosetta.expression.SumOperation -import com.regnosys.rosetta.utils.ImplicitVariableUtil -import com.regnosys.rosetta.rosetta.expression.RosettaSymbolReference -import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable -import com.regnosys.rosetta.rosetta.expression.AsKeyOperation +import com.regnosys.rosetta.rosetta.expression.MaxOperation +import com.regnosys.rosetta.rosetta.expression.MinOperation import com.regnosys.rosetta.rosetta.expression.OneOfOperation -import com.regnosys.rosetta.rosetta.expression.ChoiceOperation -import com.regnosys.rosetta.rosetta.expression.ThenOperation -import java.util.Optional -import com.regnosys.rosetta.types.builtin.RBuiltinTypeService -import com.regnosys.rosetta.rosetta.RosettaSymbol -import com.regnosys.rosetta.rosetta.RosettaFeature -import com.regnosys.rosetta.rosetta.RosettaAttributeReferenceSegment -import com.regnosys.rosetta.rosetta.RosettaAttributeReference -import com.regnosys.rosetta.rosetta.RosettaDataReference -import com.regnosys.rosetta.utils.RosettaExpressionSwitch -import com.regnosys.rosetta.rosetta.expression.ArithmeticOperation -import com.regnosys.rosetta.rosetta.expression.LogicalOperation +import com.regnosys.rosetta.rosetta.expression.ReduceOperation +import com.regnosys.rosetta.rosetta.expression.ReverseOperation +import com.regnosys.rosetta.rosetta.expression.RosettaAbsentExpression +import com.regnosys.rosetta.rosetta.expression.RosettaBinaryOperation +import com.regnosys.rosetta.rosetta.expression.RosettaBooleanLiteral +import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression +import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression import com.regnosys.rosetta.rosetta.expression.RosettaContainsExpression +import com.regnosys.rosetta.rosetta.expression.RosettaCountOperation +import com.regnosys.rosetta.rosetta.expression.RosettaDeepFeatureCall import com.regnosys.rosetta.rosetta.expression.RosettaDisjointExpression -import com.regnosys.rosetta.rosetta.expression.EqualityOperation -import com.regnosys.rosetta.rosetta.expression.ComparisonOperation -import com.regnosys.rosetta.rosetta.expression.JoinOperation -import com.regnosys.rosetta.rosetta.expression.MaxOperation -import com.regnosys.rosetta.rosetta.expression.MinOperation +import com.regnosys.rosetta.rosetta.expression.RosettaExistsExpression +import com.regnosys.rosetta.rosetta.expression.RosettaExpression +import com.regnosys.rosetta.rosetta.expression.RosettaFeatureCall +import com.regnosys.rosetta.rosetta.expression.RosettaFunctionalOperation +import com.regnosys.rosetta.rosetta.expression.RosettaImplicitVariable +import com.regnosys.rosetta.rosetta.expression.RosettaIntLiteral +import com.regnosys.rosetta.rosetta.expression.RosettaNumberLiteral +import com.regnosys.rosetta.rosetta.expression.RosettaOnlyElement +import com.regnosys.rosetta.rosetta.expression.RosettaOnlyExistsExpression +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.SwitchOperation +import com.regnosys.rosetta.rosetta.expression.ThenOperation +import com.regnosys.rosetta.rosetta.expression.ToDateOperation +import com.regnosys.rosetta.rosetta.expression.ToDateTimeOperation import com.regnosys.rosetta.rosetta.expression.ToEnumOperation import com.regnosys.rosetta.rosetta.expression.ToIntOperation import com.regnosys.rosetta.rosetta.expression.ToNumberOperation import com.regnosys.rosetta.rosetta.expression.ToStringOperation import com.regnosys.rosetta.rosetta.expression.ToTimeOperation -import javax.inject.Inject -import com.regnosys.rosetta.rosetta.expression.RosettaConstructorExpression -import com.regnosys.rosetta.rosetta.RosettaRule -import java.math.BigInteger -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.expression.RosettaDeepFeatureCall -import com.regnosys.rosetta.rosetta.expression.DefaultOperation -import com.regnosys.rosetta.cache.IRequestScopedCache -import com.regnosys.rosetta.rosetta.TypeParameter -import com.regnosys.rosetta.rosetta.expression.SwitchOperation +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.RosettaCallableWithArgs -import com.regnosys.rosetta.RosettaEcoreUtil +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.utils.ImplicitVariableUtil +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 org.eclipse.xtend2.lib.StringConcatenationClient +import org.eclipse.xtext.naming.IQualifiedNameProvider import com.regnosys.rosetta.rosetta.expression.SwitchCase +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withMeta -class RosettaTypeProvider extends RosettaExpressionSwitch> { +class RosettaTypeProvider extends RosettaExpressionSwitch> { public static String EXPRESSION_RTYPE_CACHE_KEY = RosettaTypeProvider.canonicalName + ".EXPRESSION_RTYPE" @@ -100,19 +104,19 @@ class RosettaTypeProvider extends RosettaExpressionSwitch findFeaturesOfImplicitVariable(EObject context) { return extensions.allFeatures(typeOfImplicitVariable(context), context) } + + def List getRMetaAttributesOfSymbol(RosettaSymbol symbol) { + if (symbol instanceof Annotated) { + return symbol.annotations.RMetaAttributes + } + #[] + } + + def List getRMetaAttributesOfFeature(RosettaFeature feature) { + if (feature instanceof Annotated) { + return feature.annotations.RMetaAttributes + } + #[] + } - private def RType safeRType(RosettaSymbol symbol, EObject context,Map cycleTracker) { + def List 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)] + .toList + } + + + private def RMetaAnnotatedType safeRType(RosettaSymbol symbol, EObject context,Map cycleTracker) { if (!extensions.isResolved(symbol)) { - return NOTHING + return NOTHING.withEmptyMeta } switch symbol { RosettaFeature: { @@ -144,27 +170,27 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + private def RMetaAnnotatedType safeRType(RosettaFeature feature, EObject context, Map cycleTracker) { if (!extensions.isResolved(feature)) { - return NOTHING + return NOTHING.withEmptyMeta } switch (feature) { RosettaTypedFeature: { val featureType = if (feature.typeCall === null) { - NOTHING + NOTHING.withEmptyMeta } else { - feature.typeCall.typeCallToRType + feature.typeCall.typeCallToRType.withMeta(feature.RMetaAttributesOfFeature) } - if (feature instanceof Annotated) { - if (featureType instanceof RAnnotateType) { - featureType.withMeta = extensions.hasMetaDataAnnotations(feature) - } - } featureType } RosettaEnumValue: { if (context instanceof RosettaFeatureCall) { context.receiver.safeRType(cycleTracker) } else { - context.expectedTypeFromContainer + context.expectedTypeFromContainer ?: NOTHING.withEmptyMeta } } default: - new RErrorType("Cannot infer type of feature.") + new RErrorType("Cannot infer type of feature.").withEmptyMeta } } - private def RType safeRType(RosettaExpression expression, Map cycleTracker) { + + private def RMetaAnnotatedType safeRType(RosettaExpression expression, Map cycleTracker) { getRTypeFromCache(EXPRESSION_RTYPE_CACHE_KEY, expression, [ if (cycleTracker.containsKey(expression)) { val computed = cycleTracker.get(expression) if (computed === null) { - return new RErrorType('''Can't infer type due to a cyclic reference of «qNames.getFullyQualifiedName(expression)»''') + return new RErrorType('''Can't infer type due to a cyclic reference of «qNames.getFullyQualifiedName(expression)»''').withEmptyMeta } else { return computed } } if (!extensions.isResolved(expression)) { - return NOTHING + return NOTHING.withEmptyMeta } doSwitch(expression, cycleTracker) ]) } - private def RType getRTypeFromCache(String cacheKey, EObject object, Provider typeProvider) { + + private def RMetaAnnotatedType getRTypeFromCache(String cacheKey, EObject object, Provider typeProvider) { if (object === null) { return typeProvider.get() } @@ -233,112 +256,112 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + private def RMetaAnnotatedType safeTypeOfImplicitVariable(EObject context, Map cycleTracker) { val definingContainer = context.findContainerDefiningImplicitVariable definingContainer.map [ if (it instanceof Data) { - buildRDataType + buildRDataType.withEmptyMeta } else if (it instanceof RosettaFunctionalOperation) { safeRType(argument, cycleTracker) } else if (it instanceof RosettaRule) { - input?.typeCallToRType ?: MISSING + input?.typeCallToRType.withEmptyMeta ?: MISSING.withEmptyMeta } else if (it instanceof SwitchCase) { guard.choiceOptionGuard.RTypeOfSymbol } - ].orElse(MISSING) + ].orElse(MISSING.withEmptyMeta) } - private def caseBinaryOperation(RosettaBinaryOperation expr, Map cycleTracker) { + private def caseBinaryOperation(RosettaBinaryOperation expr, Map cycleTracker) { val left = expr.left var leftType = left.safeRType(cycleTracker) - if (leftType instanceof RErrorType) { - return NOTHING + if (leftType.RType instanceof RErrorType) { + return NOTHING.withEmptyMeta } val right = expr.right var rightType = right.safeRType(cycleTracker) - if (rightType instanceof RErrorType) { - return NOTHING + if (rightType.RType instanceof RErrorType) { + return NOTHING.withEmptyMeta } - expr.operator.resultType(leftType, rightType) + expr.operator.resultType(leftType.RType, rightType.RType).withEmptyMeta } - override protected caseAbsentOperation(RosettaAbsentExpression expr, Map cycleTracker) { - BOOLEAN + override protected caseAbsentOperation(RosettaAbsentExpression expr, Map cycleTracker) { + BOOLEAN.withEmptyMeta } - override protected caseAddOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseAddOperation(ArithmeticOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseAndOperation(LogicalOperation expr, Map cycleTracker) { + override protected caseAndOperation(LogicalOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseAsKeyOperation(AsKeyOperation expr, Map cycleTracker) { + override protected caseAsKeyOperation(AsKeyOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseBooleanLiteral(RosettaBooleanLiteral expr, Map cycleTracker) { - BOOLEAN + override protected caseBooleanLiteral(RosettaBooleanLiteral expr, Map cycleTracker) { + BOOLEAN.withEmptyMeta } - override protected caseChoiceOperation(ChoiceOperation expr, Map cycleTracker) { - BOOLEAN + override protected caseChoiceOperation(ChoiceOperation expr, Map cycleTracker) { + BOOLEAN.withEmptyMeta } - override protected caseConditionalExpression(RosettaConditionalExpression expr, Map cycleTracker) { + override protected caseConditionalExpression(RosettaConditionalExpression expr, Map cycleTracker) { val ifT = expr.ifthen.safeRType(cycleTracker) - if (ifT instanceof RErrorType) { - return NOTHING + if (ifT.RType instanceof RErrorType) { + return NOTHING.withEmptyMeta } val elseT = expr.elsethen.safeRType(cycleTracker) - if (elseT instanceof RErrorType) { - return NOTHING + if (elseT.RType instanceof RErrorType) { + return NOTHING.withEmptyMeta } - val joined = join(ifT, elseT) + val joined = joinMetaAnnotatedTypes(ifT, elseT) if (joined == ANY) { - new RErrorType('''Types `«ifT»` and `«elseT»` do not have a common supertype.''') + new RErrorType('''Types `«ifT»` and `«elseT»` do not have a common supertype.''').withEmptyMeta } else { joined } } - override protected caseContainsOperation(RosettaContainsExpression expr, Map cycleTracker) { + override protected caseContainsOperation(RosettaContainsExpression expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseDefaultOperation(DefaultOperation expr, Map cycleTracker) { + override protected caseDefaultOperation(DefaultOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseCountOperation(RosettaCountOperation expr, Map cycleTracker) { - constrainedInt(Optional.empty(), Optional.of(BigInteger.ZERO), Optional.empty()) + override protected caseCountOperation(RosettaCountOperation expr, Map cycleTracker) { + constrainedInt(Optional.empty(), Optional.of(BigInteger.ZERO), Optional.empty()).withEmptyMeta } - override protected caseDisjointOperation(RosettaDisjointExpression expr, Map cycleTracker) { + override protected caseDisjointOperation(RosettaDisjointExpression expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseDistinctOperation(DistinctOperation expr, Map cycleTracker) { + override protected caseDistinctOperation(DistinctOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseDivideOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseDivideOperation(ArithmeticOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseEqualsOperation(EqualityOperation expr, Map cycleTracker) { + override protected caseEqualsOperation(EqualityOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseExistsOperation(RosettaExistsExpression expr, Map cycleTracker) { - BOOLEAN + override protected caseExistsOperation(RosettaExistsExpression expr, Map cycleTracker) { + BOOLEAN.withEmptyMeta } - override protected caseFeatureCall(RosettaFeatureCall expr, Map cycleTracker) { + override protected caseFeatureCall(RosettaFeatureCall expr, Map cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { - return NOTHING + return NOTHING.withEmptyMeta } if (feature instanceof RosettaEnumValue) { expr.receiver.safeRType(cycleTracker) @@ -347,138 +370,138 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseDeepFeatureCall(RosettaDeepFeatureCall expr, Map cycleTracker) { val feature = expr.feature if (!extensions.isResolved(feature)) { - return NOTHING + return NOTHING.withEmptyMeta } (feature as RosettaFeature).safeRType(expr, cycleTracker) } - override protected caseFilterOperation(FilterOperation expr, Map cycleTracker) { + override protected caseFilterOperation(FilterOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseFirstOperation(FirstOperation expr, Map cycleTracker) { + override protected caseFirstOperation(FirstOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseFlattenOperation(FlattenOperation expr, Map cycleTracker) { + override protected caseFlattenOperation(FlattenOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseGreaterThanOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseGreaterThanOperation(ComparisonOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseGreaterThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseGreaterThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseImplicitVariable(RosettaImplicitVariable expr, Map cycleTracker) { + override protected caseImplicitVariable(RosettaImplicitVariable expr, Map cycleTracker) { safeTypeOfImplicitVariable(expr, cycleTracker) } - override protected caseIntLiteral(RosettaIntLiteral expr, Map cycleTracker) { - constrainedInt(if (expr.value.signum >= 0) expr.value.toString.length else expr.value.toString.length - 1, expr.value, expr.value) + override protected caseIntLiteral(RosettaIntLiteral expr, Map cycleTracker) { + constrainedInt(if (expr.value.signum >= 0) expr.value.toString.length else expr.value.toString.length - 1, expr.value, expr.value).withEmptyMeta } - override protected caseJoinOperation(JoinOperation expr, Map cycleTracker) { - UNCONSTRAINED_STRING + override protected caseJoinOperation(JoinOperation expr, Map cycleTracker) { + UNCONSTRAINED_STRING.withEmptyMeta } - override protected caseLastOperation(LastOperation expr, Map cycleTracker) { + override protected caseLastOperation(LastOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseLessThanOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseLessThanOperation(ComparisonOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseLessThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { + override protected caseLessThanOrEqualOperation(ComparisonOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseListLiteral(ListLiteral expr, Map cycleTracker) { - val types = expr.elements.map[RType].filter[it !== null] - val joined = types.join + override protected caseListLiteral(ListLiteral expr, Map cycleTracker) { + val types = expr.elements.map[RMetaAnnotatedType].filter[it !== null] + val joined = types.joinMetaAnnotatedTypes val unique = newLinkedHashSet(types) val StringConcatenationClient failedList = '''«FOR t: unique.take(unique.size-1) SEPARATOR ", "»`«t»`«ENDFOR» and `«unique.last»`''' if (joined == ANY) { - new RErrorType('''Types «failedList» do not have a common supertype.''') + new RErrorType('''Types «failedList» do not have a common supertype.''').withEmptyMeta } else { joined } } - override protected caseMapOperation(MapOperation expr, Map cycleTracker) { + override protected caseMapOperation(MapOperation expr, Map cycleTracker) { expr.function?.body?.safeRType(cycleTracker) } - override protected caseMaxOperation(MaxOperation expr, Map cycleTracker) { + override protected caseMaxOperation(MaxOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseMinOperation(MinOperation expr, Map cycleTracker) { + override protected caseMinOperation(MinOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseMultiplyOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseMultiplyOperation(ArithmeticOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseNotEqualsOperation(EqualityOperation expr, Map cycleTracker) { + override protected caseNotEqualsOperation(EqualityOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseNumberLiteral(RosettaNumberLiteral expr, Map cycleTracker) { + override protected caseNumberLiteral(RosettaNumberLiteral expr, Map cycleTracker) { if (expr.value === null) { // In case of a parse error - return NOTHING + return NOTHING.withEmptyMeta } - constrainedNumber(expr.value.toPlainString.replaceAll("\\.|\\-", "").length, Math.max(0, expr.value.scale), expr.value, expr.value) + constrainedNumber(expr.value.toPlainString.replaceAll("\\.|\\-", "").length, Math.max(0, expr.value.scale), expr.value, expr.value).withEmptyMeta } - override protected caseOneOfOperation(OneOfOperation expr, Map cycleTracker) { - BOOLEAN + override protected caseOneOfOperation(OneOfOperation expr, Map cycleTracker) { + BOOLEAN.withEmptyMeta } - override protected caseOnlyElementOperation(RosettaOnlyElement expr, Map cycleTracker) { + override protected caseOnlyElementOperation(RosettaOnlyElement expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseOnlyExists(RosettaOnlyExistsExpression expr, Map cycleTracker) { - BOOLEAN + override protected caseOnlyExists(RosettaOnlyExistsExpression expr, Map cycleTracker) { + BOOLEAN.withEmptyMeta } - override protected caseOrOperation(LogicalOperation expr, Map cycleTracker) { + override protected caseOrOperation(LogicalOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseReduceOperation(ReduceOperation expr, Map cycleTracker) { + override protected caseReduceOperation(ReduceOperation expr, Map cycleTracker) { expr.function?.body?.safeRType(cycleTracker) } - override protected caseReverseOperation(ReverseOperation expr, Map cycleTracker) { + override protected caseReverseOperation(ReverseOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseSortOperation(SortOperation expr, Map cycleTracker) { + override protected caseSortOperation(SortOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseStringLiteral(RosettaStringLiteral expr, Map cycleTracker) { - constrainedString(expr.value.length, expr.value.length) + override protected caseStringLiteral(RosettaStringLiteral expr, Map cycleTracker) { + constrainedString(expr.value.length, expr.value.length).withEmptyMeta } - override protected caseSubtractOperation(ArithmeticOperation expr, Map cycleTracker) { + override protected caseSubtractOperation(ArithmeticOperation expr, Map cycleTracker) { caseBinaryOperation(expr, cycleTracker) } - override protected caseSumOperation(SumOperation expr, Map cycleTracker) { + override protected caseSumOperation(SumOperation expr, Map cycleTracker) { expr.argument.safeRType(cycleTracker) } - override protected caseSymbolReference(RosettaSymbolReference expr, Map cycleTracker) { + override protected caseSymbolReference(RosettaSymbolReference expr, Map cycleTracker) { if (expr.symbol instanceof RosettaExternalFunction) { val fun = expr.symbol as RosettaExternalFunction val returnType = fun.safeRType(expr, cycleTracker) @@ -486,7 +509,7 @@ class RosettaTypeProvider extends RosettaExpressionSwitch cycleTracker) { + override protected caseThenOperation(ThenOperation expr, Map cycleTracker) { expr.function?.body?.safeRType(cycleTracker) } - override protected caseToEnumOperation(ToEnumOperation expr, Map cycleTracker) { - expr.enumeration.buildREnumType + override protected caseToEnumOperation(ToEnumOperation expr, Map cycleTracker) { + expr.enumeration.buildREnumType.withEmptyMeta } - override protected caseToIntOperation(ToIntOperation expr, Map cycleTracker) { - UNCONSTRAINED_INT + override protected caseToIntOperation(ToIntOperation expr, Map cycleTracker) { + UNCONSTRAINED_INT.withEmptyMeta } - override protected caseToNumberOperation(ToNumberOperation expr, Map cycleTracker) { - UNCONSTRAINED_NUMBER + override protected caseToNumberOperation(ToNumberOperation expr, Map cycleTracker) { + UNCONSTRAINED_NUMBER.withEmptyMeta } - override protected caseToStringOperation(ToStringOperation expr, Map cycleTracker) { - UNCONSTRAINED_STRING + override protected caseToStringOperation(ToStringOperation expr, Map cycleTracker) { + UNCONSTRAINED_STRING.withEmptyMeta } - override protected caseToTimeOperation(ToTimeOperation expr, Map cycleTracker) { - TIME + override protected caseToTimeOperation(ToTimeOperation expr, Map cycleTracker) { + TIME.withEmptyMeta } - override protected caseConstructorExpression(RosettaConstructorExpression expr, Map cycleTracker) { - expr.typeCall.typeCallToRType + override protected caseConstructorExpression(RosettaConstructorExpression expr, Map cycleTracker) { + expr.typeCall.typeCallToRType.withEmptyMeta } - override protected caseToDateOperation(ToDateOperation expr, Map cycleTracker) { - DATE + override protected caseToDateOperation(ToDateOperation expr, Map cycleTracker) { + DATE.withEmptyMeta } - override protected caseToDateTimeOperation(ToDateTimeOperation expr, Map cycleTracker) { - DATE_TIME + override protected caseToDateTimeOperation(ToDateTimeOperation expr, Map cycleTracker) { + DATE_TIME.withEmptyMeta } - override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Map cycleTracker) { - ZONED_DATE_TIME + override protected caseToZonedDateTimeOperation(ToZonedDateTimeOperation expr, Map cycleTracker) { + ZONED_DATE_TIME.withEmptyMeta } - override protected caseSwitchOperation(SwitchOperation expr, Map context) { + override protected caseSwitchOperation(SwitchOperation expr, Map context) { expr.cases - .map[it.expression.RType] - .join + .map[it.expression.RMetaAnnotatedType] + .joinMetaAnnotatedTypes } } + \ No newline at end of file diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java index 2ffa9b362..1889b2def 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/SubtypeRelation.java @@ -1,6 +1,7 @@ package com.regnosys.rosetta.types; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Optional; @@ -8,6 +9,7 @@ import javax.inject.Inject; +import com.google.common.collect.Sets; import com.regnosys.rosetta.interpreter.RosettaValue; import com.regnosys.rosetta.types.builtin.RBuiltinTypeService; import com.regnosys.rosetta.types.builtin.RNumberType; @@ -33,7 +35,17 @@ public class SubtypeRelation { @Inject private RBuiltinTypeService builtins; + public boolean isSubtypeOf(RMetaAnnotatedType t1, RMetaAnnotatedType t2, boolean treatChoiceTypesAsDataTypes) { + if (t1.equals(t2)) { + return true; + } + return isSubtypeOf(t1.getRType(), t2.getRType(), treatChoiceTypesAsDataTypes); + } + public boolean isSubtypeOf(RType t1, RType t2, boolean treatChoiceTypesAsDataTypes) { + if (t1.equals(t2)) { + return true; + } return isSubtypeOf(t1, t2, treatChoiceTypesAsDataTypes, null); } public boolean isSubtypeOf(RType t1, RType t2, boolean treatChoiceTypesAsDataTypes, Stack visited) { @@ -57,11 +69,11 @@ public boolean isSubtypeOf(RType t1, RType t2, boolean treatChoiceTypesAsDataTyp } else if (t1 instanceof RChoiceType) { RType t1_ = t1; RType t2_ = t2; - return ((RChoiceType)t1).getOwnOptions().stream().allMatch(t -> safeIsSubtypeOf(t.getType(), t2_, false, t1_, visited)); + return ((RChoiceType)t1).getOwnOptions().stream().allMatch(t -> safeIsSubtypeOf(t.getType().getRType(), t2_, false, t1_, visited)); } else if (t2 instanceof RChoiceType) { RType t1_ = t1; RType t2_ = t2; - return ((RChoiceType)t2).getOwnOptions().stream().anyMatch(t -> safeIsSubtypeOf(t1_, t.getType(), false, t2_, visited)); + return ((RChoiceType)t2).getOwnOptions().stream().anyMatch(t -> safeIsSubtypeOf(t1_, t.getType().getRType(), false, t2_, visited)); } else if (t1 instanceof RDataType) { RType st = ((RDataType)t1).getSuperType(); if (st == null) { @@ -89,6 +101,15 @@ private boolean safeIsSubtypeOf(RType t1, RType t2, boolean treatChoiceTypesAsDa return result; } + public RMetaAnnotatedType join(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { + RType t1RType = t1.getRType(); + RType t2RType = t2.getRType(); + if (t1RType.equals(t2RType)) { + return new RMetaAnnotatedType(t1RType, intersectMeta(t1, t2)); + } + return new RMetaAnnotatedType(join(t1RType, t2RType), List.of()); + } + public RType join(RType t1, RType t2) { if (t1 instanceof RChoiceType) { t1 = ((RChoiceType) t1).asRDataType(); @@ -189,4 +210,12 @@ private RType joinByTraversingAncestorsAndAliases(RType t1, RType t2) { } return join(curr1, curr2); } + + private List intersectMeta(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { + return Sets + .intersection(new HashSet<>(t1.getMetaAttributes()), new HashSet<>(t2.getMetaAttributes())) + .immutableCopy() + .asList(); + } + } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java index c3665f27e..2bbbd4cf3 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/TypeSystem.java @@ -17,6 +17,7 @@ package com.regnosys.rosetta.types; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -78,7 +79,7 @@ private RType getRulesInputType(RDataType data, Optional source, Provider typeProvider) { return cache.get(new Pair<>(RULE_INPUT_TYPE_CACHE_KEY, new Pair<>(data, source)), typeProvider); } + + public RMetaAnnotatedType joinMetaAnnotatedTypes(RMetaAnnotatedType t1, RMetaAnnotatedType t2) { + Objects.requireNonNull(t1); + Objects.requireNonNull(t2); + + return subtypeRelation.join(t1, t2); + } + + public RMetaAnnotatedType joinMetaAnnotatedTypes(Iterable types) { + Objects.requireNonNull(types); + Validate.noNullElements(types); + + RMetaAnnotatedType any = new RMetaAnnotatedType(builtins.ANY, List.of()); + RMetaAnnotatedType acc = new RMetaAnnotatedType(builtins.NOTHING, List.of()); + for (RMetaAnnotatedType t: types) { + acc = subtypeRelation.join(acc, t); + if (acc.equals(any)) { + return acc; + } + } + return acc; + } public RType join(RType t1, RType t2) { Objects.requireNonNull(t1); @@ -147,6 +170,16 @@ public RType meet(Iterable types) { return acc; } + public boolean isSubtypeOf(RMetaAnnotatedType sub, RMetaAnnotatedType sup) { + return isSubtypeOf(sub, sup, true); + } + public boolean isSubtypeOf(RMetaAnnotatedType sub, RMetaAnnotatedType sup, boolean treatChoiceTypeAsData) { + Objects.requireNonNull(sub); + Objects.requireNonNull(sup); + + return subtypeRelation.isSubtypeOf(sub, sup, treatChoiceTypeAsData); + } + public boolean isSubtypeOf(RType sub, RType sup) { return isSubtypeOf(sub, sup, true); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java index 0473e886c..da03a7828 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/types/builtin/RBuiltinTypeService.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -28,6 +29,7 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import com.regnosys.rosetta.types.RAliasType; +import com.regnosys.rosetta.types.RMetaAnnotatedType; import com.regnosys.rosetta.types.RType; import com.regnosys.rosetta.types.RTypeFunction; import com.rosetta.model.lib.RosettaNumber; @@ -80,12 +82,18 @@ public Optional> reverse(RType type) { public final RBasicType NOTHING = registerConstantType(new RBasicType("nothing", true)); public final RBasicType ANY = registerConstantType(new RBasicType("any", false)); public final RAliasType UNCONSTRAINED_INT = new RAliasType(INT_FUNCTION, new LinkedHashMap<>(Map.of(RNumberType.DIGITS_PARAM_NAME, RosettaValue.empty(), RNumberType.MIN_PARAM_NAME, RosettaValue.empty(), RNumberType.MAX_PARAM_NAME, RosettaValue.empty())), new RNumberType(Optional.empty(), Optional.of(0), Optional.empty(), Optional.empty(), Optional.empty())); + public final RMetaAnnotatedType UNCONSTRAINED_INT_WITH_NO_META = new RMetaAnnotatedType(UNCONSTRAINED_INT, List.of()); public final RNumberType UNCONSTRAINED_NUMBER = new RNumberType(Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()); + public final RMetaAnnotatedType UNCONSTRAINED_NUMBER_WITH_NO_META = new RMetaAnnotatedType(UNCONSTRAINED_NUMBER, List.of()); public final RStringType UNCONSTRAINED_STRING = new RStringType(Optional.empty(), Optional.empty(), Optional.empty()); + public final RMetaAnnotatedType UNCONSTRAINED_STRING_WITH_NO_META = new RMetaAnnotatedType(UNCONSTRAINED_STRING, List.of()); public final RDateType DATE = registerConstantType(new RDateType()); + public final RMetaAnnotatedType DATE_WITH_NO_META = new RMetaAnnotatedType(DATE, List.of()); public final RDateTimeType DATE_TIME = registerConstantType(new RDateTimeType()); + public final RMetaAnnotatedType DATE_TIME_WITH_NO_META = new RMetaAnnotatedType(DATE_TIME, List.of()); public final RZonedDateTimeType ZONED_DATE_TIME = registerConstantType(new RZonedDateTimeType()); + public final RMetaAnnotatedType ZONED_DATE_TIME_NO_META = new RMetaAnnotatedType(ZONED_DATE_TIME, List.of()); public RBuiltinTypeService() { register("number", (m) -> RNumberType.from(m)); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/DeepFeatureCallUtil.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/DeepFeatureCallUtil.java index cf5b19b73..ad5f4214c 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/DeepFeatureCallUtil.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/DeepFeatureCallUtil.java @@ -31,7 +31,8 @@ public Map findDeepFeatureMap(RDataType type) { result.put(attr.getName(), attr); } for (RAttribute attr : allNonOverridenAttributes) { - RType attrType = attr.getRType(); + + RType attrType = attr.getRMetaAnnotatedType().getRType(); if (attrType instanceof RChoiceType) { attrType = ((RChoiceType) attrType).asRDataType(); } @@ -80,7 +81,7 @@ private void intersectButRetainAttribute(Map featuresMapToMo String name = e.getKey(); RAttribute currFeature = e.getValue(); RAttribute otherFeature = otherFeatureMap.get(name); - if (otherFeature != null && !currFeature.getMetaAnnotations().isEmpty() && otherFeature.getMetaAnnotations().isEmpty()) { + if (otherFeature != null && currFeature.getRMetaAnnotatedType().hasMeta() && !otherFeature.getRMetaAnnotatedType().hasMeta()) { e.setValue(otherFeature); } } @@ -91,7 +92,7 @@ private void merge(Map featuresMapToModify, Map featuresMapToModify, Map attrRules = getAllRuleReferencesForType(ruleSource, dataType); dataType.getAllNonOverridenAttributes().forEach(attr -> { - RType attrType = attr.getRType(); + RType attrType = attr.getRMetaAnnotatedType().getRType(); if (attrType instanceof RChoiceType) { attrType = ((RChoiceType) attrType).asRDataType(); } diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java index d46291947..6a4d5c030 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/utils/RosettaTypeSwitch.java @@ -17,7 +17,6 @@ package com.regnosys.rosetta.utils; import com.regnosys.rosetta.types.RAliasType; -import com.regnosys.rosetta.types.RAnnotateType; import com.regnosys.rosetta.types.RChoiceType; import com.regnosys.rosetta.types.RDataType; import com.regnosys.rosetta.types.REnumType; @@ -46,27 +45,22 @@ private UnsupportedOperationException errorMissedCase(RType type) { } protected Return doSwitch(RType type, Context context) { - if (type instanceof RAnnotateType) { - return doSwitch((RAnnotateType)type, context); - } else if (type instanceof RErrorType) { - return caseErrorType((RErrorType)type, context); - } else if (type instanceof RParametrizedType) { - return doSwitch((RParametrizedType)type, context); - } else if (type instanceof RRecordType) { - return doSwitch((RRecordType)type, context); - } - throw errorMissedCase(type); - } - protected Return doSwitch(RAnnotateType type, Context context) { if (type instanceof RDataType) { return caseDataType((RDataType)type, context); } else if (type instanceof RChoiceType) { return caseChoiceType((RChoiceType)type, context); } else if (type instanceof REnumType) { return caseEnumType((REnumType)type, context); + } else if (type instanceof RErrorType) { + return caseErrorType((RErrorType)type, context); + } else if (type instanceof RParametrizedType) { + return doSwitch((RParametrizedType)type, context); + } else if (type instanceof RRecordType) { + return doSwitch((RRecordType)type, context); } throw errorMissedCase(type); } + protected Return doSwitch(RParametrizedType type, Context context) { if (type instanceof RAliasType) { return caseAliasType((RAliasType)type, context); diff --git a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ChoiceValidator.java b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ChoiceValidator.java index d3b6e5836..54ecfd214 100644 --- a/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ChoiceValidator.java +++ b/rosetta-lang/src/main/java/com/regnosys/rosetta/validation/ChoiceValidator.java @@ -16,6 +16,7 @@ import com.regnosys.rosetta.rosetta.simple.ChoiceOption; import com.regnosys.rosetta.types.RChoiceOption; import com.regnosys.rosetta.types.RChoiceType; +import com.regnosys.rosetta.types.RMetaAnnotatedType; import com.regnosys.rosetta.types.RObjectFactory; import com.regnosys.rosetta.types.RType; @@ -64,10 +65,10 @@ private boolean hasCyclicOption(Choice current, List path, Set v @Check public void checkChoiceOptionsDoNotOverlap(Choice choice) { RChoiceType t = rObjectFactory.buildRChoiceType(choice); - Map includedOptions = new HashMap<>(); + Map includedOptions = new HashMap<>(); for (RChoiceOption opt: t.getOwnOptions()) { - if (opt.getType() instanceof RChoiceType) { - ((RChoiceType) opt.getType()).getAllOptions().forEach(o -> includedOptions.put(o.getType(), opt)); + if (opt.getType().getRType() instanceof RChoiceType) { + ((RChoiceType) opt.getType().getRType()).getAllOptions().forEach(o -> includedOptions.put(o.getType(), opt)); } } for (RChoiceOption opt: t.getOwnOptions()) { 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 c1875fc29..f60a47cc5 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 @@ -117,6 +117,7 @@ import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import static com.regnosys.rosetta.validation.RosettaIssueCodes.* import static extension org.eclipse.emf.ecore.util.EcoreUtil.* +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.* import org.eclipse.emf.ecore.impl.EClassImpl import com.regnosys.rosetta.rosetta.expression.RosettaConditionalExpression import com.regnosys.rosetta.rosetta.RosettaExternalFunction @@ -125,6 +126,8 @@ import com.regnosys.rosetta.interpreter.RosettaInterpreter import com.google.common.collect.Lists import java.util.Collection import org.eclipse.xtext.resource.IResourceDescriptions +import com.regnosys.rosetta.types.RMetaAnnotatedType +import com.regnosys.rosetta.rosetta.RosettaMetaType // TODO: split expression validator // TODO: type check type call arguments @@ -148,6 +151,26 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Inject extension RObjectFactory objectFactory @Inject extension RosettaInterpreter + @Check + def void checkOnlyExistsNotUsedOnMeta(RosettaOnlyExistsExpression op) { + val message = "Invalid use of `only exists` on meta feature" + + op.args + .filter(RosettaFeatureCall) + .filter[it.feature instanceof RosettaMetaType] + .forEach[ + error('''«message» «it.feature.name»''', it, ROSETTA_FEATURE_CALL__FEATURE) + ] + + op.args + .filter(RosettaSymbolReference) + .filter[it.symbol instanceof RosettaMetaType] + .forEach[ + error('''«message» «it.symbol.name»''', it, ROSETTA_SYMBOL_REFERENCE__SYMBOL) + ] + + } + @Check def void deprecatedWarning(EObject object) { val crossRefs = (object.eClass.EAllStructuralFeatures as EClassImpl.FeatureSubsetSupplier).crossReferences as List @@ -183,8 +206,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { if (op.argument.multi) { error("Input to switch must be single cardinality", op.argument, null) } - - val argumentType = op.argument.RType.stripFromTypeAliases + val argumentType = op.argument.RMetaAnnotatedType.RType.stripFromTypeAliases if (argumentType instanceof REnumType) { checkEnumSwitch(argumentType, op) } else if (argumentType instanceof RBasicType) { @@ -195,6 +217,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { error('''Type `«argumentType»` is not a valid switch argument type. Supported argument types are basic types, enumerations, and choice types.''', op, ROSETTA_UNARY_OPERATION__ARGUMENT) } } + private def void checkEnumSwitch(REnumType argumentType, SwitchOperation op) { // When the argument is an enum: // - all guards should be enum guards, @@ -210,6 +233,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } } } + if (op.^default === null) { val missingEnumValues = argumentType.allEnumValues.filter[!seenValues.contains(it)] if (!missingEnumValues.empty) { @@ -230,7 +254,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { if (!seenValues.add(caseStatement.guard.literalGuard.interpret)) { error('''Duplicate case''', caseStatement, SWITCH_CASE__GUARD) } - val conditionType = caseStatement.guard.literalGuard.RType + val conditionType = caseStatement.guard.literalGuard.RMetaAnnotatedType.RType if (!conditionType.isComparable(argumentType)) { error('''Invalid case: «argumentType.notComparableMessage(conditionType)»''', caseStatement, SWITCH_CASE__GUARD) } @@ -242,7 +266,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { // - all guards should be choice option guards, // - all cases should be reachable, // - all choice options should be covered. - val Map includedOptions = newHashMap + val Map includedOptions = newHashMap for (caseStatement : op.cases) { if (caseStatement.guard.choiceOptionGuard === null) { error('''Case should match a choice option of type «argumentType»''', caseStatement, SWITCH_CASE__GUARD) @@ -254,8 +278,9 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } else { val guardType = guard.RTypeOfSymbol includedOptions.put(guard, guardType) - if (guardType instanceof RChoiceType) { - guardType.allOptions.forEach[includedOptions.put(it.EObject, guardType)] + val valueType = guardType.RType + if (valueType instanceof RChoiceType) { + valueType.allOptions.forEach[includedOptions.put(it.EObject, guardType)] } } } @@ -265,20 +290,21 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { for (guard : includedOptions.values.toSet) { for (var i=0; i attrFromClazzes, Iterable attrFromSuperClasses, String name) { attrFromClazzes.forEach [ childAttr | - val childAttrType = childAttr.RType + val childAttrType = childAttr.RMetaAnnotatedType.RType attrFromSuperClasses.forEach [ parentAttr | - val parentAttrType = parentAttr.RType + val parentAttrType = parentAttr.RMetaAnnotatedType.RType if (childAttrType != parentAttrType) { error('''Overriding attribute '«name»' with type «childAttrType» must match the type of the attribute it overrides («parentAttrType»)''', childAttr.EObject, ROSETTA_NAMED__NAME, DUPLICATE_ATTRIBUTE) @@ -813,7 +841,8 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { val callerArg = indexed.value val callerIdx = indexed.key val param = callable.inputs.get(callerIdx) - checkType(param.typeCall.typeCallToRType, callerArg, element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, callerIdx) + + checkType(param.getRTypeOfSymbol, callerArg, element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, callerIdx) if(!param.card.isMany && cardinality.isMulti(callerArg)) { error('''Expecting single cardinality for parameter '«param.name»'.''', element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, callerIdx) @@ -821,7 +850,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { ] } else if (callable instanceof RosettaRule) { if (callable.input !== null) { - checkType(callable.input.typeCallToRType, element.args.head, element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0) + checkType(callable.input.typeCallToRType.withEmptyMeta, element.args.head, element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0) if (cardinality.isMulti(element.args.head)) { error('''Expecting single cardinality for input to rule.''', element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, 0) @@ -832,7 +861,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { val callerArg = indexed.value val callerIdx = indexed.key val param = callable.parameters.get(callerIdx) - checkType(param.typeCall.typeCallToRType, callerArg, element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, callerIdx) + checkType(param.typeCall.typeCallToRType.withEmptyMeta, callerArg, element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, callerIdx) if(cardinality.isMulti(callerArg)) { error('''Expecting single cardinality for parameter '«param.name»'.''', element, ROSETTA_SYMBOL_REFERENCE__RAW_ARGS, callerIdx) @@ -892,7 +921,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def void checkPatternAndFormat(RosettaExternalRegularAttribute attribute) { - if (!isDateTime(attribute.attributeRef.getRTypeOfFeature(attribute))){ + if (!isDateTime(attribute.attributeRef.getRTypeOfFeature(attribute).RType)){ for(s:attribute.externalSynonyms) { checkFormatNull(s.body) checkPatternValid(s.body) @@ -907,7 +936,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def void checkPatternAndFormat(Attribute attribute) { - if (!isDateTime(attribute.RTypeOfSymbol)){ + if (!isDateTime(attribute.RTypeOfSymbol.RType)){ for(s:attribute.synonyms) { checkFormatNull(s.body) checkPatternValid(s.body) @@ -980,7 +1009,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { val attrExt = attr.buildRAttribute val attrSingle = !attrExt.isMulti - val attrType = attrExt.RType + val attrType = attrExt.RMetaAnnotatedType.RType // check cardinality val ruleSingle = !rule.expression.isMulti @@ -995,7 +1024,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } // check type - val ruleType = rule.expression.RType + val ruleType = rule.expression.RMetaAnnotatedType.RType if (ruleType !== null && ruleType != MISSING && attrType !== null && attrType != MISSING && !ruleType.isSubtypeOf(attrType)) { val typeError = '''Type mismatch - report field «attr.name» has type «attrType.name» ''' + '''whereas the reporting rule «rule.name» has type «ruleType».''' @@ -1021,7 +1050,8 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def checkBinaryParamsRightTypes(RosettaBinaryOperation binOp) { - val resultType = binOp.RType + val resultMetaType = binOp.RMetaAnnotatedType + val resultType = resultMetaType.RType if (resultType instanceof RErrorType) { error(resultType.message, binOp, ROSETTA_OPERATION__OPERATOR) } @@ -1049,7 +1079,8 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def checkAttribute(Attribute ele) { - var eleType = ele.RTypeOfSymbol + var eleType = ele.RTypeOfSymbol.RType + if (eleType instanceof RChoiceType) { eleType = eleType.asRDataType } @@ -1130,7 +1161,8 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def checkConstructorExpression(RosettaConstructorExpression ele) { - val rType = ele.RType + + val rType = ele.RMetaAnnotatedType?.RType if (rType !== null) { var baseRType = rType.stripFromTypeAliases if (baseRType instanceof RChoiceType) { @@ -1175,7 +1207,8 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def checkListLiteral(ListLiteral ele) { - val type = ele.RType + val metaType = ele.RMetaAnnotatedType + val type = metaType.RType if (type instanceof RErrorType) { error('''All collection elements must have the same super type but types were «type.message»''', ele, null) } @@ -1216,7 +1249,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { if (cardinality.isMulti(arg)) { error('''The argument of «ele.operator» should be of singular cardinality.''', ele, ROSETTA_UNARY_OPERATION__ARGUMENT) } - if (!arg.RType.isSubtypeOf(UNCONSTRAINED_STRING)) { + if (!arg.RMetaAnnotatedType.isSubtypeOf(UNCONSTRAINED_STRING_WITH_NO_META)) { error('''The argument of «ele.operator» should be a string.''', ele, ROSETTA_UNARY_OPERATION__ARGUMENT) } } @@ -1229,7 +1262,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { if (cardinality.isMulti(arg)) { error('''The argument of «ele.operator» should be of singular cardinality.''', ele, ROSETTA_UNARY_OPERATION__ARGUMENT) } - val type = arg.RType.stripFromTypeAliases + val type = arg.RMetaAnnotatedType.RType.stripFromTypeAliases if (!(type instanceof RBasicType || type instanceof RRecordType || type instanceof REnumType)) { error('''The argument of «ele.operator» should be of a builtin type or an enum.''', ele, ROSETTA_UNARY_OPERATION__ARGUMENT) } @@ -1300,8 +1333,8 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { if (attrRef.attribute.isResolved) { checkForLocation(attrRef.attribute, it) val targetType = attrRef.attribute.typeCall.typeCallToRType - val thisType = ele.RTypeOfSymbol - if (!targetType.isSubtypeOf(thisType)) + val thisType = ele.RTypeOfSymbol.RType + if (!thisType.isSubtypeOf(targetType)) error('''Expected address target type of '«thisType.name»' but was '«targetType?.name ?: 'null'»'«»''', it, ANNOTATION_QUALIFIER__QUAL_PATH, TYPE_ERROR) } } @@ -1344,11 +1377,11 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { val func = ele as Function - var annotationType = annotations.head.attribute.RTypeOfSymbol + var annotationType = annotations.head.attribute.RTypeOfSymbol.RType if (annotationType instanceof RChoiceType) { annotationType = annotationType.asRDataType } - var funcOutputType = func.RTypeOfSymbol + var funcOutputType = func.RTypeOfSymbol.RType if (funcOutputType instanceof RChoiceType) { funcOutputType = funcOutputType.asRDataType } @@ -1492,15 +1525,15 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { @Check def checkReduceOperation(ReduceOperation o) { checkNumberOfMandatoryNamedParameters(o.function, 2) - if (o.argument.RType != o.function.body.RType) { - error('''List reduce expression must evaluate to the same type as the input. Found types «o.argument.RType» and «o.function.body.RType».''', o, ROSETTA_FUNCTIONAL_OPERATION__FUNCTION) + if (!o.argument.RMetaAnnotatedType.isSubtypeOf(o.function.body.RMetaAnnotatedType)) { + error('''List reduce expression must evaluate to the same type as the input. Found types «o.argument.RMetaAnnotatedType.RType» and «o.function.body.RMetaAnnotatedType.RType».''', o, ROSETTA_FUNCTIONAL_OPERATION__FUNCTION) } checkBodyIsSingleCardinality(o.function) } @Check def checkNumberReducerOperation(SumOperation o) { - checkInputType(o, UNCONSTRAINED_NUMBER) + checkInputType(o, UNCONSTRAINED_NUMBER_WITH_NO_META) } @Check @@ -1586,16 +1619,16 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } } - private def void checkInputType(RosettaUnaryOperation o, RType type) { - if (!o.argument.getRType.isSubtypeOf(type)) { + private def void checkInputType(RosettaUnaryOperation o, RMetaAnnotatedType type) { + if (!o.argument.getRMetaAnnotatedType.isSubtypeOf(type)) { error('''Input type must be a «type».''', o, ROSETTA_OPERATION__OPERATOR) } } private def void checkInputIsComparable(RosettaUnaryOperation o) { - val inputRType = o.argument.getRType - if (!inputRType.hasNaturalOrder) { - error('''Operation «o.operator» only supports comparable types (string, int, string, date). Found type «inputRType.name».''', o, ROSETTA_OPERATION__OPERATOR) + val inputRType = o.argument.getRMetaAnnotatedType + if (!inputRType.RType.hasNaturalOrder) { + error('''Operation «o.operator» only supports comparable types (string, int, number, boolean, date). Found type «inputRType.RType.name».''', o, ROSETTA_OPERATION__OPERATOR) } } @@ -1606,7 +1639,7 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { } private def void checkBodyType(InlineFunction ref, RType type) { - val bodyType = ref?.body?.getRType + val bodyType = ref?.body?.getRMetaAnnotatedType?.RType if (ref !== null && bodyType !== null && bodyType != MISSING && bodyType != type) { error('''Expression must evaluate to a «type.name».''', ref, null) } @@ -1615,9 +1648,9 @@ class RosettaSimpleValidator extends AbstractDeclarativeRosettaValidator { private def void checkBodyIsComparable(RosettaFunctionalOperation op) { val ref = op.function if (ref !== null) { - val bodyRType = ref.body.getRType - if (!bodyRType.hasNaturalOrder) { - error('''Operation «op.operator» only supports comparable types (string, int, string, date). Found type «bodyRType.name».''', ref, null) + val bodyRType = ref.body.getRMetaAnnotatedType + if (!bodyRType.RType.hasNaturalOrder) { + error('''Operation «op.operator» only supports comparable types (string, int, number, boolean, date). Found type «bodyRType.RType.name».''', ref, null) } } } 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 d702d32e3..5bd867483 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 @@ -216,7 +216,7 @@ public void checkExternalRuleSource(RosettaExternalRuleSource source) { current = newCurrent; } } else { - RType attrType = ts.stripFromTypeAliases(attr.getRType()); + RType attrType = ts.stripFromTypeAliases(attr.getRMetaAnnotatedType().getRType()); if (attrType instanceof RChoiceType) { attrType = ((RChoiceType) attrType).asRDataType(); } 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 dcee7b47d..ee1d774dd 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 @@ -97,6 +97,11 @@ class CodeGeneratorTestHelper { val code = generateCode(model) code.inMemoryCompileToClasses(this.class.classLoader); } + + def createEnumInstance(Map> classes, String className, String enumValue) { + val myEnumClass = classes.get(rootPackage + '.' + className) as Class + Enum.valueOf(myEnumClass, enumValue) as Enum + } def createInstanceUsingBuilder(Map> classes, String className, Map itemsToSet) { classes.createInstanceUsingBuilder(rootPackage, className, itemsToSet) diff --git a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionParser.xtend b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionParser.xtend index e47d9530e..8e98addb1 100644 --- a/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionParser.xtend +++ b/rosetta-testing/src/main/java/com/regnosys/rosetta/tests/util/ExpressionParser.xtend @@ -49,7 +49,7 @@ class ExpressionParser { } def RosettaExpression parseExpression(CharSequence expr, List context, Collection attrs) { - val attributes = attrs.map[createAttribute(context)].toList + val attributes = attrs.map[parseAttribute(context)].toList return parseExpression(expr, context, attributes) } @@ -66,11 +66,11 @@ class ExpressionParser { return expression } - def Attribute createAttribute(CharSequence attr) { - return createAttribute(attr, defaultContext) + def Attribute parseAttribute(CharSequence attr) { + return parseAttribute(attr, defaultContext) } - def Attribute createAttribute(CharSequence attr, List context) { + def Attribute parseAttribute(CharSequence attr, List context) { val IParseResult result = parser.parse(grammar.attributeRule, new StringReader(attr.toString())) assertFalse(result.hasSyntaxErrors) val attribute = result.rootASTElement as Attribute diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend index efda4016e..2439e46fc 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/condition/ConditionGeneratorTest.xtend @@ -10,6 +10,7 @@ import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import static com.google.common.collect.ImmutableMap.* import static org.junit.jupiter.api.Assertions.* +import com.rosetta.model.lib.records.Date @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) @@ -17,6 +18,31 @@ class ConditionGeneratorTest { @Inject extension CodeGeneratorTestHelper @Inject extension ConditionTestHelper + @Test + def void dateComparisonConditionOnTypeWithMetaTest() { + val code = ''' + type Foo: + [metadata key] + a date (1..1) + b date (1..1) + + condition DateCondition: + a <= b + '''.generateCode + + val classes = code.compileToClasses + + val largerDate = Date.of(2024, 6, 30) + val smallerDate = Date.of(2024, 1, 1) + + val validFoo = classes.createInstanceUsingBuilder('Foo', #{ + "a" -> smallerDate, + "b" -> largerDate + }) + + assertTrue(classes.runCondition(validFoo, "FooDateCondition").isSuccess) + } + @Test def void omittedParameterInConditionTest() { val code = ''' diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend index 735312933..2a1d82c32 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/expression/TypeCoercionTest.xtend @@ -21,13 +21,15 @@ import java.util.Arrays import org.eclipse.xtend2.lib.StringConcatenationClient import com.rosetta.util.types.JavaPrimitiveType import com.regnosys.rosetta.generator.java.types.JavaTypeUtil +import com.regnosys.rosetta.generator.java.types.RJavaFieldWithMeta +import com.regnosys.rosetta.generator.java.types.RJavaReferenceWithMeta @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) class TypeCoercionTest { @Inject TypeCoercionService coercionService @Inject extension ImportManagerExtension - @Inject extension JavaTypeUtil + @Inject extension JavaTypeUtil typeUtil private def void assertCoercion(String expectedCode, StringConcatenationClient expr, Class actual, JavaType expected) { assertCoercion(expectedCode, expr, JavaType.from(actual), expected) @@ -46,6 +48,149 @@ class TypeCoercionTest { assertEquals(expectedCode, buildClass(pkg, '''«coercedExpr.completeAsReturn»''', scope).replace("package test.ns;", "").trim + System.lineSeparator) } + + + @Test + def void testConvertBigDecimalToFieldWithMetaInteger() { + val expected = ''' + import java.math.BigDecimal; + import test.FieldWithMetaInteger; + + + { + final BigDecimal bigDecimal = BigDecimal.valueOf(10); + return bigDecimal == null ? null : FieldWithMetaInteger.builder().setValue(bigDecimal.intValueExact()).build(); + } + ''' + + val expectedType = new RJavaFieldWithMeta(INTEGER, DottedPath.of("test"), typeUtil) + + assertCoercion(expected, '''BigDecimal.valueOf(10)''', BigDecimal, expectedType) + + } + + + @Test + def void testConvertFieldWithMetaIntegerToBigDecimal() { + val expected = ''' + import java.math.BigDecimal; + import test.FieldWithMetaInteger; + + + { + final FieldWithMetaInteger fieldWithMetaInteger = FieldWithMetaInteger.builder().setValue(10).build(); + if (fieldWithMetaInteger == null) { + return null; + } + final Integer integer = fieldWithMetaInteger.getValue(); + return integer == null ? null : BigDecimal.valueOf(integer); + } + ''' + + val actualType = new RJavaFieldWithMeta(INTEGER, DottedPath.of("test"), typeUtil) + + assertCoercion(expected, '''FieldWithMetaInteger.builder().setValue(10).build()''', actualType, BigDecimal) + } + + @Test + def void testConvertMetaReferenceToMetaField() { + val expected = ''' + import test.FieldWithMetaString; + import test.ReferenceWithMetaString; + + + { + final ReferenceWithMetaString referenceWithMetaString = ReferenceWithMetaString.builder().setValue("foo").build(); + if (referenceWithMetaString == null) { + return null; + } + final String string = referenceWithMetaString.getValue(); + return string == null ? null : FieldWithMetaString.builder().setValue(string).build(); + } + ''' + + val actualType = new RJavaReferenceWithMeta(STRING, DottedPath.of("test"), typeUtil) + val expectedType = new RJavaFieldWithMeta(STRING, DottedPath.of("test"), typeUtil) + + assertCoercion(expected, '''ReferenceWithMetaString.builder().setValue("foo").build()''', actualType, expectedType) + } + + @Test + def void testConvertMetaFieldToMetaReference() { + val expected = ''' + import test.FieldWithMetaString; + import test.ReferenceWithMetaString; + + + { + final FieldWithMetaString fieldWithMetaString = FieldWithMetaString.builder().setValue("foo").build(); + if (fieldWithMetaString == null) { + return null; + } + final String string = fieldWithMetaString.getValue(); + return string == null ? null : ReferenceWithMetaString.builder().setValue(string).build(); + } + ''' + + val actualType = new RJavaFieldWithMeta(STRING, DottedPath.of("test"), typeUtil) + val expectedType = new RJavaReferenceWithMeta(STRING, DottedPath.of("test"), typeUtil) + + assertCoercion(expected, '''FieldWithMetaString.builder().setValue("foo").build()''', actualType, expectedType) + } + + + @Test + def void testConvertStringToMeta() { + val expected = ''' + import test.FieldWithMetaString; + + + { + final String string = "foo"; + return string == null ? null : FieldWithMetaString.builder().setValue(string).build(); + } + ''' + assertCoercion(expected, '''"foo"''', String, new RJavaFieldWithMeta(STRING, DottedPath.of("test"), typeUtil)) + + val expected2 = ''' + import test.ReferenceWithMetaString; + + + { + final String string = "foo"; + return string == null ? null : ReferenceWithMetaString.builder().setValue(string).build(); + } + ''' + assertCoercion(expected2, '''"foo"''', String, new RJavaReferenceWithMeta(STRING, DottedPath.of("test"), typeUtil)) + } + + @Test + def void testConvertMetaToString() { + val String expected = ''' + import test.FieldWithMetaString; + + + { + final FieldWithMetaString fieldWithMetaString = FieldWithMetaString.builder().setValue("foo").build(); + return fieldWithMetaString == null ? null : fieldWithMetaString.getValue(); + } + ''' + + assertCoercion(expected, '''FieldWithMetaString.builder().setValue("foo").build()''', new RJavaFieldWithMeta(STRING, DottedPath.of("test"), typeUtil), String) + + val expected2 = ''' + import test.ReferenceWithMetaString; + + + { + final ReferenceWithMetaString referenceWithMetaString = ReferenceWithMetaString.builder().setValue("foo").build();; + return referenceWithMetaString == null ? null : referenceWithMetaString.getValue(); + } + ''' + + assertCoercion(expected2, '''ReferenceWithMetaString.builder().setValue("foo").build();''', new RJavaReferenceWithMeta(STRING, DottedPath.of("test"), typeUtil), String) + } + @Test def void testItemToItemConversion() { var String expected diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend index 96e488b7d..143fa96c0 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend @@ -32,6 +32,7 @@ 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 @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) @@ -42,6 +43,742 @@ class FunctionGeneratorTest { @Inject extension ModelHelper @Inject extension ValidationTestHelper + @Test + def void testCanPassMetaFromOutputOfFunctionCall() { + val code = ''' + func A: + inputs: + myInput string (1..1) + [metadata scheme] + output: + result string (1..1) + [metadata scheme] + set result: myInput + + func B: + inputs: + myInput string (1..1) + [metadata scheme] + output: + result string (1..1) + set result: + A(myInput) -> scheme + '''.generateCode + + code.compileToClasses + val classes = code.compileToClasses + + val funcB = classes.createFunc("B") + + val myInput = classes.createFieldWithMetaString("myValue", "myScheme") + + val result = funcB.invokeFunc(String, myInput) + + assertEquals("myScheme", result) + } + + @Test + def void testIngoreMetaOnSwitchInputs() { + val code = ''' + func Test: + inputs: + myInput string (1..1) + [metadata scheme] + output: + result int (1..1) + + set result: myInput switch + "a" then 1, + "b" then 2, + default 3 + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createFieldWithMetaString("b", "myScheme") + + assertEquals(2, test.invokeFunc(Integer, myInput)) + } + + @Test + def void testIngoreMetaOnSwitchBasicTypeInputs() { + val code = ''' + func Test: + inputs: + myInput string (1..1) + [metadata scheme] + output: + result int (1..1) + + set result: myInput switch + "a" then 1, + "b" then 2, + default 3 + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createFieldWithMetaString("b", "myScheme") + + assertEquals(2, test.invokeFunc(Integer, myInput)) + } + + + @Test + def void testIgnoreMetaOnChoiceTypes() { + val code = ''' + type Foo: + a string (1..1) + b int (0..1) + c number (0..1) + + func Test: + inputs: + foo Foo (1..1) + [metadata scheme] + output: + result boolean (1..1) + set result: + foo required choice b, c + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "FieldWithMetaFoo", #{ + "value" -> classes.createInstanceUsingBuilder("Foo", #{ + "a" -> "aValue", + "c" -> BigDecimal.valueOf(20) + }), + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "myScheme" + }) + }) + + val result = test.invokeFunc(Boolean, myInput) + assertTrue(result) + } + + @Test + def void testSortFunctionsOnMetaItemsInInput() { + val code = ''' + func Test: + inputs: + myInputs string (1..*) + [metadata scheme] + output: + result string (1..*) + + set result: myInputs sort + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInputs = #[ + classes.createFieldWithMetaString("DDDD", "myScheme"), + classes.createFieldWithMetaString("AAAA", "myScheme"), + classes.createFieldWithMetaString("HHHH", "myScheme") + ] + + val result = test.invokeFunc(List, myInputs) + + assertEquals(#["AAAA", "DDDD", "HHHH"], result) + } + + @Test + def void testSumOnMetaIntegers() { + val code = ''' + func Test: + inputs: + n int (0..*) + [metadata scheme] + output: + result int (1..1) + set result: + n sum + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInputs = #[ + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaInteger", #{ + "value" -> 6, + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "myScheme" + }) + }), + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaInteger", #{ + "value" -> 5, + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "myScheme" + }) + }) + ] + + val result = test.invokeFunc(Integer, myInputs) + + assertEquals(11, result) + } + + @Test + def void testMaxFunctionsOnMetaItemsInInput() { + val code = ''' + func Test: + inputs: + myInputs string (1..*) + [metadata scheme] + output: + result string (1..1) + + set result: myInputs max + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInputs = #[ + classes.createFieldWithMetaString("AAAA", "myScheme"), + classes.createFieldWithMetaString("BBBB", "myScheme") + ] + + val result = test.invokeFunc(String, myInputs) + + assertEquals("BBBB", result) + } + + @Test + def void testMinFunctionsOnMetaItemsInInput() { + val code = ''' + func Test: + inputs: + myInputs string (1..*) + [metadata scheme] + output: + result string (1..1) + + set result: myInputs min + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInputs = #[ + classes.createFieldWithMetaString("AAAA", "myScheme"), + classes.createFieldWithMetaString("BBBB", "myScheme") + ] + + val result = test.invokeFunc(String, myInputs) + + assertEquals("AAAA", result) + } + + @Test + def void testToStringOnEnumWithMeta() { + val code = ''' + enum MyEnum: + A + B + C + + type Foo: + myEnum MyEnum (1..1) + [metadata scheme] + + func Test: + inputs: + myInput Foo (1..1) + + output: + result string (1..1) + + set result: myInput -> myEnum to-string + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createInstanceUsingBuilder("Foo", #{ + "myEnum" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "FieldWithMetaMyEnum", #{ + "value" -> classes.createEnumInstance("MyEnum", "B"), + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "myScheme" + }) + }) + }) + + val result = test.invokeFunc(String, myInput) + + assertEquals("B", result) + } + + @Test + def void testSettingMetaOnOutputWithReferenceAsKey() { + val code = ''' + type Foo: + [metadata key] + + type Bar: + b Foo (1..1) + [metadata reference] + + func Test: + inputs: + myInput Foo (1..1) + [metadata reference] + output: + result Bar (1..1) + + set result -> b: myInput as-key + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "value" -> classes.createInstanceUsingBuilder("Foo", #{ + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "externalKey" -> "myExternalKey", + "globalKey" -> "myGlobalKey" + }) + }) + }) + + val expected = classes.createInstanceUsingBuilder("Bar", #{ + "b" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "externalReference" -> "myExternalKey", + "globalReference" -> "myGlobalKey" + }) + }) + + val result = test.invokeFunc(RosettaModelObject, myInput) + + assertEquals(expected, result) + } + + + @Test + def void testSettingMetaOnOutput() { + val code = ''' + type Bar: + b string (1..1) + [metadata scheme] + + func Test: + inputs: + myInput string (1..1) + [metadata scheme] + output: + result Bar (1..1) + + set result -> b: myInput + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "someInput", + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "scheme" -> "myScheme" + }) + }) + + val expected = classes.createInstanceUsingBuilder("Bar", #{ + "b" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "someInput" + }) + }) + + val result = test.invokeFunc(RosettaModelObject, myInput) + + assertEquals(expected, result) + } + + @Test + def void testTransitivilyPassingMetaReference() { + val code = ''' + type FooContainer: + foo Foo (1..1) + [metadata reference] + + type Foo: + [metadata key] + + type Bar: + b Foo (1..1) + [metadata reference] + + func Test: + inputs: + myInput FooContainer (1..1) + + output: + result Bar (1..1) + + set result: Bar { + b: myInput -> foo as-key + } + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createInstanceUsingBuilder("FooContainer" , #{ + "foo" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "value" -> classes.createInstanceUsingBuilder("Foo", #{ + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "externalKey" -> "myExternalKey", + "globalKey" -> "myGlobalKey" + }) + }) + }) + }) + + val expected = classes.createInstanceUsingBuilder("Bar", #{ + "b" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "externalReference" -> "myExternalKey", + "globalReference" -> "myGlobalKey" + }) + }) + + val result = test.invokeFunc(RosettaModelObject, myInput) + + assertEquals(expected, result) + } + + @Test + def void testPassingMetaItemToConstructorWithReferenceAsKey() { + val code = ''' + type Foo: + [metadata key] + + type Bar: + b Foo (1..1) + [metadata reference] + + func Test: + inputs: + myInput Foo (1..1) + [metadata reference] + output: + result Bar (1..1) + + set result: Bar { + b: myInput as-key + } + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "value" -> classes.createInstanceUsingBuilder("Foo", #{ + "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ + "externalKey" -> "myExternalKey", + "globalKey" -> "myGlobalKey" + }) + }) + }) + + val expected = classes.createInstanceUsingBuilder("Bar", #{ + "b" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "externalReference" -> "myExternalKey", + "globalReference" -> "myGlobalKey" + }) + }) + + val result = test.invokeFunc(RosettaModelObject, myInput) + + assertEquals(expected, result) + } + + @Test + def void testPassingMetaItemToConstructor() { + val code = ''' + type Foo: + a string (1..1) + [metadata scheme] + + func Test: + inputs: + myInput string (1..1) + [metadata scheme] + output: + result Foo (1..1) + + set result: Foo { + a: myInput + } + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val myInput = classes.createFieldWithMetaString("someInput", "myScheme") + + val expected = classes.createInstanceUsingBuilder("Foo", #{ + "a" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "value" -> "someInput" + }) + }) + + val result = test.invokeFunc(RosettaModelObject, myInput) + + assertEquals(expected, result) + } + + @Test + def void testCompareItemWithMetaToItemWithMeta() { + val code = ''' + func Test: + inputs: + a string (1..1) + [metadata scheme] + b string (1..1) + [metadata scheme] + output: + result boolean (1..1) + set result: a = b + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val a = classes.createFieldWithMetaString("foo", "myScheme") + + val b = classes.createFieldWithMetaString("foo", "myScheme") + + assertTrue(test.invokeFunc(Boolean, a, b)) + } + + @Test + def void testCompareItemToItemWithMeta() { + val code = ''' + func Test: + inputs: + a string (1..1) + b string (1..1) + [metadata scheme] + output: + result boolean (1..1) + set result: a = b + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val a = "foo" + val b = classes.createFieldWithMetaString("foo", "myScheme") + + assertTrue(test.invokeFunc(Boolean, a, b)) + } + + @Test + def void testCompareItemWithMetaToItemWithReference() { + val code = ''' + func Test: + inputs: + a string (1..1) + [metadata scheme] + b string (1..1) + [metadata reference] + output: + result boolean (1..1) + set result: a = b + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val a = classes.createFieldWithMetaString("foo", "myScheme") + val b = classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaString", #{ + "value" -> "foo", + "reference" -> (Reference.builder.reference = "myRef").build + }) + + assertTrue(test.invokeFunc(Boolean, a, b)) + } + + @Test + def void testDeepFeatureCallWithMeta() { + val code = ''' + choice Foo: + A + B + + type A: + attr int (1..1) + + type B: + attr int (1..1) + + func Test: + inputs: + fooWithReference Foo (1..1) + [metadata reference] + output: + result int (1..1) + set result: + fooWithReference ->> attr + '''.generateCode + + val classes = code.compileToClasses + + val test = classes.createFunc("Test") + + val fooWithReference = classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.test.model.metafields"), "ReferenceWithMetaFoo", #{ + "value" -> classes.createInstanceUsingBuilder("Foo", #{ + "a" -> classes.createInstanceUsingBuilder("A", #{ + "attr" -> 99 + }) + }), + "reference" -> (Reference.builder.reference = "myRef").build + }) + + assertEquals(99, test.invokeFunc(Integer, fooWithReference)) + } + + @Test + def void canHandleMetaCoecrion() { + val code = ''' + metaType reference string + + func SomeFunc: + inputs: + myInput int (0..*) + [metadata reference] + + output: + myResult number (0..*) + [metadata scheme] + + set myResult: myInput + '''.generateCode + + val classes = code.compileToClasses + val someFunc = classes.createFunc("SomeFunc") + + val myInput = #[ + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaInteger", #{ + "value" -> 5, + "reference" -> (Reference.builder.reference = "myRef").build + }), + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaInteger", #{ + "value" -> 10, + "reference" -> (Reference.builder.reference = "myRef2").build + }), + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "ReferenceWithMetaInteger", #{ + "value" -> 15, + "reference" -> (Reference.builder.reference = "myRef3").build + }) + ] + + val expected = #[ + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaBigDecimal", #{ + "value" -> BigDecimal.valueOf(5) + }), + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaBigDecimal", #{ + "value" -> BigDecimal.valueOf(10) + }), + classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaBigDecimal", #{ + "value" -> BigDecimal.valueOf(15) + }) + ] + + val result = someFunc.invokeFunc(List, myInput) + + assertEquals(expected, result) + } + + @Test + def void canPassMetadataToFunctionAndUseInExpression() { + val code = ''' + func SomeFunc: + inputs: + myInput string (1..1) + [metadata scheme] + output: + myResult string (1..1) + + set myResult: myInput + myInput -> scheme + '''.generateCode + + val classes = code.compileToClasses + val someFunc = classes.createFunc("SomeFunc") + + + val myInput = classes.createFieldWithMetaString("myInputValue", "myScheme") + + val result = someFunc.invokeFunc(String, myInput) + assertEquals("myInputValuemyScheme", result) + } + + @Test + def void canSetFunctionWithMetaOutput() { + val code = ''' + func SomeFunc: + inputs: + myInput string (1..1) + [metadata scheme] + output: + myResult string (1..1) + [metadata scheme] + + set myResult: myInput + '''.generateCode + + val classes = code.compileToClasses + val someFunc = classes.createFunc("SomeFunc") + + val myInput = classes.createFieldWithMetaString("myInputValue", "myScheme") + + val result = someFunc.invokeFunc(RosettaModelObject, myInput) + assertEquals(myInput, result) + } + + @Test + def void canPassMetadataToFunctions() { + val code = ''' + func SomeFunc: + inputs: + myInput string (1..1) + [metadata scheme] + output: + myResult string (1..1) + + set myResult: myInput -> scheme + '''.generateCode + + val classes = code.compileToClasses + val someFunc = classes.createFunc("SomeFunc") + + + val myInput = classes.createFieldWithMetaString("myInputValue", "myScheme") + + val result = someFunc.invokeFunc(String, myInput) + assertEquals("myScheme", result) + } + @Test def void switchOnChoiceType() { val code = ''' @@ -166,7 +903,6 @@ class FunctionGeneratorTest { } @Test - @Disabled // Enable as part of https://github.com/finos/rune-dsl/pull/845 def void switchOnChoiceOptionWithMetadata() { val code = ''' typeAlias MyString: string(maxLength: 42) @@ -194,7 +930,7 @@ class FunctionGeneratorTest { "number" -> BigDecimal.valueOf(42) }) val fooStrWithScheme = classes.createInstanceUsingBuilder("Foo", #{ - "string" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ + "MyString" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "FieldWithMetaString", #{ "meta" -> classes.createInstanceUsingBuilder(new RootPackage("com.rosetta.model.metafields"), "MetaFields", #{ "scheme" -> "myScheme" }), diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend index 4f10e339d..b044a7329 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/tests/RosettaExpressionsTest.xtend @@ -159,7 +159,7 @@ class RosettaExpressionsTest { code.compileToClasses } - + @Test def void shoudCodeGenerateAndCompileAccessingMetaSimple() { val code = ''' @@ -176,7 +176,7 @@ class RosettaExpressionsTest { '''.generateCode code.compileToClasses } - + @Test def void shoudCodeGenerateAndCompileAccessingMeta() { val code = ''' 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 7a49dd9be..865d6d3d6 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,21 @@ class RosettaParsingTest { @Inject extension ValidationTestHelper @Inject extension ExpressionParser + @Test + def void canPassMetadataToFunctions() { + ''' + func MyFunc: + inputs: + myInput string (1..1) + [metadata scheme] + output: + myResult string (1..1) + + set myResult: myInput -> scheme + '''.parseRosettaWithNoIssues + } + + @Test def void testPropagationForScopingForImplicitEnumType() { val model = ''' diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend index 14003df20..4df92fee6 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/RosettaTypeProviderTest.xtend @@ -70,10 +70,10 @@ class RosettaTypeProviderTest { '''.parseRosettaWithNoErrors.elements.filter(Function) val allNumber = funcs.filter[name == "Qualify_AllNumber"].head - assertEquals('number', (allNumber.operations.head.expression as RosettaContainsExpression).left.RType.name) + assertEquals('number', (allNumber.operations.head.expression as RosettaContainsExpression).left.RMetaAnnotatedType.RType.name) val mixed = funcs.filter[name == "Qualify_MixedNumber"].head - assertEquals('number', (mixed.operations.head.expression as RosettaContainsExpression).left.RType.name) + assertEquals('number', (mixed.operations.head.expression as RosettaContainsExpression).left.RMetaAnnotatedType.RType.name) val intOnly = funcs.filter[name == "Qualify_IntOnly"].head - assertEquals('int', (intOnly.operations.head.expression as RosettaBinaryOperation).left.RType.name) + assertEquals('int', (intOnly.operations.head.expression as RosettaBinaryOperation).left.RMetaAnnotatedType.RType.name) } } diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend index 4262bfeba..fdc5b2bbf 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/types/SubtypeRelationTest.xtend @@ -13,6 +13,10 @@ import org.junit.jupiter.api.Test import com.regnosys.rosetta.rosetta.RosettaModel import com.regnosys.rosetta.rosetta.RosettaNamed import com.regnosys.rosetta.rosetta.RosettaEnumeration +import com.regnosys.rosetta.tests.util.ExpressionParser +import com.regnosys.rosetta.types.builtin.RBuiltinTypeService +import com.regnosys.rosetta.types.builtin.RStringType +import static extension com.regnosys.rosetta.types.RMetaAnnotatedType.withEmptyMeta @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) @@ -20,6 +24,10 @@ class SubtypeRelationTest { @Inject extension SubtypeRelation @Inject extension ModelHelper @Inject extension RObjectFactory + @Inject extension ExpressionParser + @Inject extension RosettaTypeProvider + @Inject extension RBuiltinTypeService + private def Data getData(RosettaModel model, String name) { return model.elements.filter(RosettaNamed).findFirst[it.name == name] as Data @@ -28,6 +36,84 @@ class SubtypeRelationTest { return model.elements.filter(RosettaNamed).findFirst[it.name == name] as RosettaEnumeration } + @Test + def testJoinOnMetaSubTypesReturnsParent() { + val model = ''' + type A: + + type B extends A: + + type C extends A: + + '''.parseRosettaWithNoIssues + + val fieldBType = ''' + fieldB B (1..1) + [metadata reference] + '''.parseAttribute(#[model]).RTypeOfSymbol + + val fieldCType = ''' + fieldC C (1..1) + [metadata reference] + '''.parseAttribute(#[model]).RTypeOfSymbol + + + val fieldA = model.getData("A").buildRDataType + + val joined = fieldBType.join(fieldCType) + + assertEquals(fieldA.withEmptyMeta, joined) + } + + @Test + def testJoinOnSameBaseTypeWithMetaIsCorrect() { + val fieldA = ''' + fieldA string (1..1) + [metadata scheme] + [metadata reference] + '''.parseAttribute.RTypeOfSymbol + + val fieldB = ''' + fieldB string (1..1) + [metadata scheme] + [metadata address] + '''.parseAttribute.RTypeOfSymbol + + val result = fieldA.join(fieldB) + assertTrue(result.RType instanceof RStringType) + val resultMetaAttribute = result.getMetaAttributes.get(0) + assertEquals("scheme", resultMetaAttribute.name) + assertEquals(UNCONSTRAINED_STRING, resultMetaAttribute.RType) + } + + @Test + def testStringWithSchemeAndReferenceIsSubtypeOfRelationWithStringWithAddressAndLocation() { + val fieldAType = ''' + fieldA string (1..1) + [metadata scheme] + [metadata reference] + '''.parseAttribute.RTypeOfSymbol + + val fieldBType = ''' + fieldB string (1..1) + [metadata address] + [metadata location] + '''.parseAttribute.RTypeOfSymbol + + assertTrue(fieldAType.isSubtypeOf(fieldBType, true)) + assertTrue(fieldBType.isSubtypeOf(fieldAType, true)) + } + + @Test + def testStringWithSchemeIsSubtypeOfStringWithScheme() { + val fieldAType = ''' + fieldA string (1..1) + [metadata scheme] + '''.parseAttribute.RTypeOfSymbol + + assertTrue(fieldAType.isSubtypeOf(fieldAType, true)) + } + @Test def testExtendedTypeIsSubtype() { val model = ''' diff --git a/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java b/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java index e3aafad95..61e79f902 100644 --- a/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java +++ b/rosetta-testing/src/test/java/com/regnosys/rosetta/utils/RosettaSimpleSystemSolverTest.java @@ -86,8 +86,8 @@ private void assertSolution(Map expected, SolutionS @Test public void testSimpleSystem() { - Attribute x = parser.createAttribute("x int (1..1)"); - Attribute y = parser.createAttribute("y int (1..1)"); + Attribute x = parser.parseAttribute("x int (1..1)"); + Attribute y = parser.parseAttribute("y int (1..1)"); List system = parse("x", "42", "-1", "y", "10", "10", x, y); SolutionSet set = solve(system, x, y).orElseThrow(); assertSolution(Map.of(x, helper.toValue(42), y, helper.toValue(-1)), set); @@ -95,9 +95,9 @@ public void testSimpleSystem() { @Test public void testSimpleSystemWithVariable() { - Attribute x = parser.createAttribute("x int (1..1)"); - Attribute y = parser.createAttribute("y int (1..1)"); - Attribute a = parser.createAttribute("a int (1..1)"); + Attribute x = parser.parseAttribute("x int (1..1)"); + Attribute y = parser.parseAttribute("y int (1..1)"); + Attribute a = parser.parseAttribute("a int (1..1)"); List system = parse("x", "a", "a", "y", "x", "29", x, y, a); SolutionSet set = solve(system, x, y).orElseThrow(); assertSolution(Map.of(x, helper.toValue(29), y, helper.toValue(29)), set, Map.of(a, helper.toValue(29))); @@ -105,15 +105,15 @@ public void testSimpleSystemWithVariable() { @Test public void testNotSimpleSystem() { - Attribute x = parser.createAttribute("x int (1..1)"); + Attribute x = parser.parseAttribute("x int (1..1)"); Equation system = parse("x", "42 + 1", x); assertTrue(solve(system, x).isEmpty()); } @Test public void testUnsolvableSystem() { - Attribute x = parser.createAttribute("x int (1..1)"); - Attribute a = parser.createAttribute("a int (1..1)"); + Attribute x = parser.parseAttribute("x int (1..1)"); + Attribute a = parser.parseAttribute("a int (1..1)"); List system = parse("x", "a", "a", "0", x, a); SolutionSet set = solve(system, x).orElseThrow(); assertTrue(set.getSolution(RosettaInterpreterContext.ofSymbolMap(Map.of(a, helper.toValue(1)))).isEmpty()); @@ -121,8 +121,8 @@ public void testUnsolvableSystem() { @Test public void testUnderdeterminedSystem() { - Attribute x = parser.createAttribute("x int (1..1)"); - Attribute y = parser.createAttribute("y int (1..1)"); + Attribute x = parser.parseAttribute("x int (1..1)"); + Attribute y = parser.parseAttribute("y int (1..1)"); List system = parse("x", "42", "10", "10", x, y); assertTrue(solve(system, x, y).isEmpty()); } 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 ad0bec812..5297f87c7 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 @@ -31,6 +31,58 @@ class RosettaValidatorTest implements RosettaIssueCodes { @Inject extension ModelHelper @Inject extension ExpressionParser + @Test + def void testOnlyExistsOnMetaIsNotValidOnSymbolReferences() { + val model = ''' + type Foo: + a int (0..1) + b int (0..1) + c int (0..1) + + type Bar: + b int (1..1) + + func MyFunc: + inputs: + foosWithMeta Foo (1..*) + [metadata scheme] + output: + result Foo (1..*) + add result: + foosWithMeta filter (b, scheme) only exists + '''.parseRosetta + + model.assertError(ROSETTA_SYMBOL_REFERENCE, null, + "Invalid use of `only exists` on meta feature scheme" + ) + } + + @Test + def void testOnlyExistsOnMetaIsNotValidOnFeatureCalls() { + val model = ''' + type Foo: + a int (0..1) + b int (0..1) + c int (0..1) + + type Bar: + b int (1..1) + + func MyFunc: + inputs: + foo Foo (1..1) + [metadata scheme] + output: + result boolean (1..1) + set result: + (foo -> b, foo -> scheme) only exists + '''.parseRosetta + + model.assertError(ROSETTA_FEATURE_CALL, null, + "Invalid use of `only exists` on meta feature scheme" + ) + } + @Test def void testSwitchOnChoiceCannotHaveLiteralGuard() { val model = ''' @@ -3217,7 +3269,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { add sortedFoos: foos sort // sort based on Foo '''.parseRosetta - model.assertError(SORT_OPERATION, null, "Operation sort only supports comparable types (string, int, string, date). Found type Foo.") + model.assertError(SORT_OPERATION, null, "Operation sort only supports comparable types (string, int, number, boolean, date). Found type Foo.") } @Test @@ -3239,7 +3291,7 @@ class RosettaValidatorTest implements RosettaIssueCodes { bars sort x [ x -> foo ] // sort based on Foo '''.parseRosetta - model.assertError(INLINE_FUNCTION, null, "Operation sort only supports comparable types (string, int, string, date). Found type Foo.") + model.assertError(INLINE_FUNCTION, null, "Operation sort only supports comparable types (string, int, number, boolean, date). Found type Foo.") } @Test