From 52154e048c6d7ecc462aeabdfe50b78bb6e4c56c Mon Sep 17 00:00:00 2001 From: Dennis Huebner Date: Mon, 23 Sep 2019 12:55:33 +0200 Subject: [PATCH] Consolidate Code Generators for Functions (#62) * Allow alias in assignment #43 * Move calculation impl to the doEvaluate method #43 * Additiona FuncGenerator fixes. Adjusted test expectation #43 * Added type check for assign-output * Added Reference type support #43 * Allow usage of output in assign expression #43 * Added cardinality and name validation. Better type inference for if-else * Allow only-element for callable call * assignment of an alias #53 * Validate list element access #65 --- com.regnosys.rosetta.lib/.classpath | 6 + .../model/lib/math/BigDecimalExtensions.java | 80 + .../model/lib/validation/ValidatorHelper.java | 14 + .../CalculationGeneratorHelper.xtend | 8 +- .../RosettaCalcExpressionTest.xtend | 1 + .../RosettaCalculationGenerationTest.xtend | 1571 ++++------------- .../calculation/RosettaCalculationTest.xtend | 48 +- .../RosettaFunctionGenerationTest.xtend | 96 +- .../java/function/FunctionGeneratorTest.xtend | 728 -------- .../object/RosettaEnumGeneratorTest.xtend | 46 +- .../tests/util/CodeGeneratorTestHelper.xtend | 6 +- com.regnosys.rosetta/META-INF/MANIFEST.MF | 1 - com.regnosys.rosetta/model/Rosetta.xcore | 7 +- .../model/RosettaSimple.xcore | 35 +- .../src/com/regnosys/rosetta/Rosetta.xtext | 4 +- .../regnosys/rosetta/RosettaExtensions.xtend | 7 +- .../rosetta/generator/RosettaGenerator.xtend | 16 +- .../calculation/CalculationGenerator.xtend | 728 -------- ...taExternalFunctionDependencyProvider.xtend | 129 -- .../generator/java/enums/EnumGenerator.xtend | 74 +- .../generator/java/enums/EnumHelper.xtend | 71 + .../generator/java/expression/Expression.java | 129 -- .../ExpressionGeneratorWithBuilder.xtend | 232 +++ .../RosettaExpressionJavaGenerator.xtend | 4 +- ...aExpressionJavaGeneratorForFunctions.xtend | 159 +- .../RosettaToJavaExtensions.xtend | 12 +- .../java/function/CardinalityProvider.xtend | 2 +- .../java/function/FuncGenerator.xtend | 300 ++++ .../java/function/FunctionGenerator.xtend | 340 ---- .../function/JavaQualifiedTypeProvider.xtend | 2 +- .../RosettaFunctionDependencyProvider.xtend | 2 +- .../object/ModelObjectBuilderGenerator.xtend | 78 +- .../java/rule/DataRuleGenerator.xtend | 8 +- .../generator/java/util/JavaNames.xtend | 43 +- .../util/RosettaFunctionExtensions.xtend | 61 +- .../scoping/RosettaScopeProvider.xtend | 9 +- .../types/RosettaExpectedTypeProvider.xtend | 7 + .../rosetta/types/RosettaOperators.xtend | 2 +- .../rosetta/types/RosettaTypeProvider.xtend | 22 +- .../rosetta/utils/ExpressionHelper.xtend | 42 + .../rosetta/validation/RosettaValidator.xtend | 59 +- 41 files changed, 1584 insertions(+), 3605 deletions(-) delete mode 100644 com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend delete mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/CalculationGenerator.xtend delete mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaExternalFunctionDependencyProvider.xtend create mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumHelper.xtend delete mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/Expression.java create mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorWithBuilder.xtend rename com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/{function => expression}/RosettaExpressionJavaGeneratorForFunctions.xtend (76%) rename com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/{calculation => expression}/RosettaToJavaExtensions.xtend (95%) create mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FuncGenerator.xtend delete mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend rename com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/{calculation => function}/RosettaFunctionDependencyProvider.xtend (97%) create mode 100644 com.regnosys.rosetta/src/com/regnosys/rosetta/utils/ExpressionHelper.xtend diff --git a/com.regnosys.rosetta.lib/.classpath b/com.regnosys.rosetta.lib/.classpath index 199d589c7..7b51bb309 100644 --- a/com.regnosys.rosetta.lib/.classpath +++ b/com.regnosys.rosetta.lib/.classpath @@ -17,6 +17,12 @@ + + + + + + diff --git a/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/math/BigDecimalExtensions.java b/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/math/BigDecimalExtensions.java index 2770cfa73..18e29a57f 100644 --- a/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/math/BigDecimalExtensions.java +++ b/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/math/BigDecimalExtensions.java @@ -3,6 +3,9 @@ import java.math.BigDecimal; import java.math.MathContext; +import com.rosetta.model.lib.functions.Mapper; +import com.rosetta.model.lib.functions.MapperS; + public class BigDecimalExtensions { /** @@ -39,4 +42,81 @@ public static BigDecimal divide(BigDecimal a, BigDecimal b) { public static boolean closeTo(BigDecimal a, BigDecimal b, BigDecimal error) { return a.subtract(b).abs().compareTo(error) < 0; } + /** + * + * @see BigDecimal.valueOf(double); + */ + public static BigDecimal valueOf(double a) { + return BigDecimal.valueOf(a); + } + /** + * + * @see BigDecimal.valueOf(long); + */ + public static BigDecimal valueOf(long a) { + return BigDecimal.valueOf(a); + } + /** + * + * @see BigDecimal.valueOf(long, int); + */ + public static BigDecimal valueOf(long a, int scale) { + return BigDecimal.valueOf(a,scale); + } + + /// for Mappers + /** + * Add a and b + */ + public static Mapper add(Mapper a, Mapper b) { + return MapperS.of(add(a.get(),b.get())); + } + + /** + * Subtract b from a + */ + public static Mapper subtract(Mapper a, Mapper b) { + return MapperS.of(subtract(a.get(),b.get())); + } + + /** + * Multiply a and b + */ + public static Mapper multiply(Mapper a, Mapper b) { + return MapperS.of(multiply(a.get(),b.get())); + } + + /** + * Divide a by b + */ + public static Mapper divide(Mapper a, Mapper b) { + return MapperS.of(divide(a.get(),b.get())); + } + + /** + * Is a close to b, with given error + */ + public static boolean closeTo(Mapper a, Mapper b, Mapper error) { + return closeTo(a.get(), b.get(), error.get()); + } + + /** + * + * @see BigDecimal.valueOf(long); + */ + public static Mapper valueOf(Mapper a) { + Number number = a.get(); + if (number instanceof BigDecimal) + return MapperS.of(valueOf(((BigDecimal) number).doubleValue())); + return MapperS.of(valueOf(number.longValue())); + } + + /** + * + * @see BigDecimal.valueOf(long, int); + */ + public static Mapper valueOf(Mapper a, int scale) { + return MapperS.of(valueOf(a.get(), scale)); + } + } diff --git a/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/validation/ValidatorHelper.java b/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/validation/ValidatorHelper.java index 92503285d..16ef1354e 100644 --- a/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/validation/ValidatorHelper.java +++ b/com.regnosys.rosetta.lib/src/main/java/com/rosetta/model/lib/validation/ValidatorHelper.java @@ -87,17 +87,31 @@ public static Mapper doIf(Mapper test, Mapper ifthen, Mapper< if (testResult) return ifthen; else return elsethen; } + public static Mapper doIf(Mapper test, Mapper ifthen) { + boolean testResult = test.getMulti().stream().allMatch(b->b.booleanValue()); + if (testResult) return ifthen; + else return null; + } public static ComparisonResult doIf(ComparisonResult test, ComparisonResult ifthen, ComparisonResult elsethen) { if (test.get()) return ifthen; else return elsethen; } + public static ComparisonResult doIf(ComparisonResult test, ComparisonResult ifthen) { + if (test.get()) return ifthen; + else return ComparisonResult.success(); + } public static Mapper doIf(ComparisonResult test, Mapper ifthen, Mapper elsethen) { if (test.get()) return ifthen; else return elsethen; } + public static Mapper doIf(ComparisonResult test, Mapper ifthen) { + if (test.get()) return ifthen; + else return null; + } + public static ComparisonResult doWhenPresent(Mapper whenPresent, ComparisonResult compare) { if(exists(whenPresent, false).get()) return compare; diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/CalculationGeneratorHelper.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/CalculationGeneratorHelper.xtend index 8a2669588..735a65eef 100644 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/CalculationGeneratorHelper.xtend +++ b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/CalculationGeneratorHelper.xtend @@ -1,8 +1,10 @@ package com.regnosys.rosetta.generator.java.calculation import com.google.inject.Inject +import com.regnosys.rosetta.generator.java.function.FuncGenerator import com.regnosys.rosetta.generator.java.util.JavaNames import com.regnosys.rosetta.rosetta.RosettaModel +import com.regnosys.rosetta.rosetta.simple.Function import com.regnosys.rosetta.tests.util.ModelHelper import java.util.function.Consumer import org.eclipse.xtext.xbase.testing.RegisteringFileSystemAccess @@ -11,20 +13,20 @@ import static org.junit.jupiter.api.Assertions.* class CalculationGeneratorHelper { - @Inject CalculationGenerator generator + @Inject FuncGenerator generator @Inject extension ModelHelper @Inject RegisteringFileSystemAccess fsa @Inject JavaNames.Factory factory def void assertToGeneratedFunction(CharSequence actualModel, CharSequence expected) throws AssertionError { actualModel.assertToGenerated(expected, [ - generator.generateFunctions(fsa, it.elements, factory.create(it), "test") + generator.generate(factory.create(it), fsa, it.elements.filter(Function).filter[operations.nullOrEmpty].head, "test") ]) } def void assertToGeneratedCalculation(CharSequence actualModel, CharSequence expected) throws AssertionError { actualModel.assertToGenerated(expected, [ - generator.generateCalculation(fsa, it.elements, factory.create(it), "test") + generator.generate(factory.create(it), fsa, it.elements.filter(Function).filter[!operations.nullOrEmpty].head, "test") ]) } diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalcExpressionTest.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalcExpressionTest.xtend index 2270d9790..7a3158033 100644 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalcExpressionTest.xtend +++ b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalcExpressionTest.xtend @@ -1,6 +1,7 @@ package com.regnosys.rosetta.generator.java.calculation import com.google.inject.Inject +import com.regnosys.rosetta.generator.java.expression.RosettaToJavaExtensions import com.regnosys.rosetta.generator.java.util.ImportingStringConcatination import com.regnosys.rosetta.generator.java.util.JavaNames import com.regnosys.rosetta.rosetta.RosettaArgumentFeature diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationGenerationTest.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationGenerationTest.xtend index 63015a2cc..8f76f5259 100644 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationGenerationTest.xtend +++ b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationGenerationTest.xtend @@ -1,7 +1,6 @@ package com.regnosys.rosetta.generator.java.calculation import com.google.inject.Inject -import com.regnosys.rosetta.generator.java.blueprints.RosettaBlueprintTest import com.regnosys.rosetta.rosetta.RosettaPackage import com.regnosys.rosetta.tests.RosettaInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper @@ -23,10 +22,10 @@ class RosettaCalculationGenerationTest { @Inject extension CodeGeneratorTestHelper @Inject extension ModelHelper @Inject extension ValidationTestHelper - + @Test def void testSimpleTransDep() { - ''' + val genereated = ''' class Period { frequency int (1..1); periodEnum PeriodEnum (1..1); @@ -36,141 +35,84 @@ class RosettaCalculationGenerationTest { enum PeriodEnum { MONTH } - - calculation DayFraction { - res defined by: p / 360 - - where - p: is Period -> periodEnum - } - calculation PeriodEnum.MONTH { - defined by: i * 30.0 + func DayFraction : + inputs: in2 Period( 1..1 ) + output: res number (1..1) + alias p: PeriodEnumFunc(in2 -> periodEnum, in2) + assign-output res: p / 360 - where - i: is Period -> frequency - } - '''.assertToGeneratedCalculation( - ''' - package com.rosetta.test.model.calculation; - - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; - import com.rosetta.model.lib.math.BigDecimalExtensions; - import com.rosetta.test.model.Period; - import java.math.BigDecimal; - import java.util.ArrayList; - import java.util.Arrays; - import java.util.List; + func PeriodEnumFunc : + inputs: + in1 PeriodEnum( 1..1 ) + in2 Period( 1..1 ) + output: out number( 1..1 ) - public class DayFraction { - - public CalculationResult calculate(Period paramPeriod) { - CalculationInput input = new CalculationInput().create(paramPeriod); - CalculationResult result = new CalculationResult(input); - result.res = BigDecimalExtensions.divide(input.p, BigDecimal.valueOf(360)); - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private final List calculationResults = new ArrayList<>(); - private BigDecimal p; - - public CalculationInput create(Period inputParam) { - PeriodEnum.CalculationResult periodEnumCalculationResult = new PeriodEnum().calculate(inputParam, inputParam.getPeriodEnum()); - this.calculationResults.add(periodEnumCalculationResult); - this.p = periodEnumCalculationResult.getValue(); - return this; - } - - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("DayFraction", "res defined by: p / 360", this)); - } - - @Override - public List getCalculationResults() { - return calculationResults; - } - - public BigDecimal getP() { - return p; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("p", BigDecimal.class, (IResult res) -> ((CalculationInput) res).getP()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - } - - public static class CalculationResult implements ICalculationResult { - - private CalculationInput calculationInput; - - private BigDecimal res; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public BigDecimal getRes() { - return this.res; - } - - public CalculationResult setRes(BigDecimal res) { - this.res = res; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("res", BigDecimal.class, (IResult res) -> ((CalculationResult) res).getRes()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; + func PeriodEnumFunc(in1: PeriodEnum -> MONTH ): + alias i: in2 -> frequency + assign-output out: i * 30.0 + '''.generateCode.get("com.rosetta.test.model.functions.PeriodEnumFunc") + + assertEquals( + ''' + package com.rosetta.test.model.functions; + + import com.google.inject.Inject; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperS; + import com.rosetta.model.lib.functions.RosettaFunction; + import com.rosetta.model.lib.math.BigDecimalExtensions; + import com.rosetta.test.model.Period; + import com.rosetta.test.model.PeriodEnum; + import java.lang.Integer; + import java.math.BigDecimal; + + + /** + * @version test + */ + public class PeriodEnumFunc { + + @Inject protected PeriodEnumFunc.MONTH MONTH; + + public BigDecimal evaluate(PeriodEnum in1, Period in2) { + switch (in1) { + case MONTH: + return MONTH.evaluate(in1, in2); + default: + throw new IllegalArgumentException("Enum value not implemented: " + in1); + } } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (res != null ? !res.equals(_that.res) : _that.res != null) return false; - return true; - } - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (res != null ? res.hashCode() : 0); - return _result; - } + public static class MONTH implements RosettaFunction { - @Override - public String toString() { - return "CalculationResult {" + - "res=" + this.res + - '}'; + /** + * @param in1 + * @param in2 + * @return out + */ + public BigDecimal evaluate(PeriodEnum in1, Period in2) { + + BigDecimal out = doEvaluate(in1, in2); + + return out; + } + + protected BigDecimal doEvaluate(PeriodEnum in1, Period in2) { + Mapper outHolder = null; + outHolder = MapperS.of(BigDecimalExtensions.multiply(BigDecimalExtensions.valueOf(i(in1, in2).get()), BigDecimalExtensions.valueOf(30.0))); + return outHolder.get(); + } + + + protected Mapper i(PeriodEnum in1, Period in2) { + return MapperS.of(in2).map("getFrequency", Period::getFrequency); + } } } - } - ''' + '''.toString, + genereated ) } @@ -179,252 +121,101 @@ class RosettaCalculationGenerationTest { @Test def void testOnePlusOneGeneration() { ''' - calculation Calc { - defined by: one + one - - where - one int : is 1 - } + func Calc: + inputs: + one int (1..1) + output: out int (1..1) + alias oneA : 1 + assign-output out: oneA + oneA '''.assertToGeneratedCalculation( ''' - package com.rosetta.test.model.calculation; + package com.rosetta.test.model.functions; - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperS; + import com.rosetta.model.lib.functions.RosettaFunction; import java.lang.Integer; - import java.util.Arrays; - import java.util.List; - public class Calc { - - public CalculationResult calculate() { - CalculationInput input = new CalculationInput().create(); - CalculationResult result = new CalculationResult(input); - result.value = (input.one + input.one); - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private Integer one; - - public CalculationInput create() { - this.one = 1; - return this; - } - - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("Calc", "defined by: one + one", this)); - } + + public class Calc implements RosettaFunction { + + /** + * @param one + * @return out + */ + public Integer evaluate(Integer one) { - public Integer getOne() { - return one; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("one", Integer.class, (IResult res) -> ((CalculationInput) res).getOne()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } + Integer out = doEvaluate(one); + return out; } - public static class CalculationResult implements ICalculationResult { - - private CalculationInput calculationInput; + protected Integer doEvaluate(Integer one) { + Mapper outHolder = null; + outHolder = MapperS.of((oneA(one).get() + oneA(one).get())); + return outHolder.get(); + } - private Integer value; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public Integer getValue() { - return this.value; - } - - public CalculationResult setValue(Integer value) { - this.value = value; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("value", Integer.class, (IResult res) -> ((CalculationResult) res).getValue()) - ); - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (value != null ? !value.equals(_that.value) : _that.value != null) return false; - return true; - } - - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (value != null ? value.hashCode() : 0); - return _result; - } - - @Override - public String toString() { - return "CalculationResult {" + - "value=" + this.value + - '}'; - } + protected Mapper oneA(Integer one) { + return MapperS.of(Integer.valueOf(1)); } } ''' ) - } @Test def void testSimpleCalculationGeneration() { ''' - calculation Calc { - res defined by: arg1 + arg2 * 215 - - where - arg1 int : is Min(1,2) - arg2 int : is Max(1,2) - } + func Calc: + inputs: + arg1 int (1..1) + arg2 int (1..1) + output: res int (1..1) + alias a1 : Min(1,2) + alias a2 : Max(1,2) + + assign-output res: a1 + a2 * 215 '''.assertToGeneratedCalculation( ''' - package com.rosetta.test.model.calculation; + package com.rosetta.test.model.functions; - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperS; import com.rosetta.model.lib.functions.Max; import com.rosetta.model.lib.functions.Min; + import com.rosetta.model.lib.functions.RosettaFunction; import java.lang.Integer; - import java.util.Arrays; - import java.util.List; - public class Calc { - - public CalculationResult calculate() { - CalculationInput input = new CalculationInput().create(); - CalculationResult result = new CalculationResult(input); - result.res = (input.arg1 + (input.arg2 * 215)); - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private Integer arg1; - private Integer arg2; - - public CalculationInput create() { - this.arg1 = new Min().execute(1,2); - this.arg2 = new Max().execute(1,2); - return this; - } - - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("Calc", "res defined by: arg1 + arg2 * 215", this)); - } + + public class Calc implements RosettaFunction { + + /** + * @param arg1 + * @param arg2 + * @return res + */ + public Integer evaluate(Integer arg1, Integer arg2) { - public Integer getArg1() { - return arg1; - } - - public Integer getArg2() { - return arg2; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("arg1", Integer.class, (IResult res) -> ((CalculationInput) res).getArg1()), - new Attribute<>("arg2", Integer.class, (IResult res) -> ((CalculationInput) res).getArg2()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } + Integer res = doEvaluate(arg1, arg2); + return res; } - public static class CalculationResult implements ICalculationResult { + protected Integer doEvaluate(Integer arg1, Integer arg2) { + Mapper resHolder = null; + resHolder = MapperS.of((a1(arg1, arg2).get() + (a2(arg1, arg2).get() * 215))); + return resHolder.get(); + } - private CalculationInput calculationInput; - private Integer res; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public Integer getRes() { - return this.res; - } - - public CalculationResult setRes(Integer res) { - this.res = res; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("res", Integer.class, (IResult res) -> ((CalculationResult) res).getRes()) - ); + protected Mapper a1(Integer arg1, Integer arg2) { + return MapperS.of(new Min().execute(MapperS.of(Integer.valueOf(1)).get(), MapperS.of(Integer.valueOf(2)).get())); + } - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (res != null ? !res.equals(_that.res) : _that.res != null) return false; - return true; - } - - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (res != null ? res.hashCode() : 0); - return _result; - } - - @Override - public String toString() { - return "CalculationResult {" + - "res=" + this.res + - '}'; - } + protected Mapper a2(Integer arg1, Integer arg2) { + return MapperS.of(new Max().execute(MapperS.of(Integer.valueOf(1)).get(), MapperS.of(Integer.valueOf(2)).get())); } } ''' @@ -434,156 +225,75 @@ class RosettaCalculationGenerationTest { @Test def void testDateTimeAdd() { val calculation = ''' - calculation Calc { - res defined by: arg1 + arg2 - string res2 defined by: arg1 + arg2 - - where - arg1 date : is FuncIn->val1 - arg2 time : is FuncIn->val2 - } - class FuncIn { val1 date (1..1); val2 time (1..1); } + data FoncOut: + res1 string (1..1) + res2 string (1..1) + + func Calc: + inputs: + funIn FuncIn(1..1) + + output: + res FoncOut(1..1) + alias arg1: funIn-> val1 + alias arg2: funIn-> val2 + assign-output res -> res1: arg1 + arg2 + assign-output res -> res2: arg1 + arg2 '''.generateCode - val calcJava = calculation.get("com.rosetta.test.model.calculation.Calc") + val calcJava = calculation.get("com.rosetta.test.model.functions.Calc") //RosettaBlueprintTest.writeOutClasses(calculation, "testDateTimeAdd") calculation.compileToClasses val expected = ''' - package com.rosetta.test.model.calculation; + package com.rosetta.test.model.functions; - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperMaths; + import com.rosetta.model.lib.functions.MapperS; + import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.model.lib.records.Date; + import com.rosetta.test.model.FoncOut; import com.rosetta.test.model.FuncIn; - import java.lang.String; - import java.time.LocalDateTime; + import java.lang.SuppressWarnings; import java.time.LocalTime; - import java.util.Arrays; - import java.util.List; - public class Calc { - - public CalculationResult calculate(FuncIn paramFuncIn) { - CalculationInput input = new CalculationInput().create(paramFuncIn); - CalculationResult result = new CalculationResult(input); - result.res = Date.of(input.arg1, input.arg2); - result.res2 = Date.of(input.arg1, input.arg2).toString(); - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private Date arg1; - private LocalTime arg2; - - public CalculationInput create(FuncIn inputParam) { - this.arg1 = inputParam.getVal1(); - this.arg2 = inputParam.getVal2(); - return this; - } - - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("Calc", "res defined by: arg1 + arg2", this), - new Formula("Calc", "string res2 defined by: arg1 + arg2", this)); - } + + public class Calc implements RosettaFunction { + + /** + * @param funIn + * @return res + */ + public FoncOut evaluate(FuncIn funIn) { - public Date getArg1() { - return arg1; - } - - public LocalTime getArg2() { - return arg2; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("arg1", Date.class, (IResult res) -> ((CalculationInput) res).getArg1()), - new Attribute<>("arg2", LocalTime.class, (IResult res) -> ((CalculationInput) res).getArg2()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } + FoncOut res = doEvaluate(funIn).build(); + return res; } - public static class CalculationResult implements ICalculationResult { + protected FoncOut.FoncOutBuilder doEvaluate(FuncIn funIn) { + FoncOut.FoncOutBuilder resHolder = FoncOut.builder(); + @SuppressWarnings("unused") FoncOut res = resHolder.build(); + resHolder + .setRes1(MapperMaths.add(MapperS.of(arg1(funIn).get()), MapperS.of(arg2(funIn).get())).get()); + ; + res = resHolder.build(); + resHolder + .setRes2(MapperMaths.add(MapperS.of(arg1(funIn).get()), MapperS.of(arg2(funIn).get())).get()); + ; + return resHolder; + } - private CalculationInput calculationInput; - private LocalDateTime res; - private String res2; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public LocalDateTime getRes() { - return this.res; - } - - public CalculationResult setRes(LocalDateTime res) { - this.res = res; - return this; - } - - public String getRes2() { - return this.res2; - } - - public CalculationResult setRes2(String res2) { - this.res2 = res2; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("res", LocalDateTime.class, (IResult res) -> ((CalculationResult) res).getRes()), - new Attribute<>("res2", String.class, (IResult res) -> ((CalculationResult) res).getRes2()) - ); + protected Mapper arg1(FuncIn funIn) { + return MapperS.of(funIn).map("getVal1", FuncIn::getVal1); + } - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (res != null ? !res.equals(_that.res) : _that.res != null) return false; - if (res2 != null ? !res2.equals(_that.res2) : _that.res2 != null) return false; - return true; - } - - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (res != null ? res.hashCode() : 0); - _result = 31 * _result + (res2 != null ? res2.hashCode() : 0); - return _result; - } - - @Override - public String toString() { - return "CalculationResult {" + - "res=" + this.res + ", " + - "res2=" + this.res2 + - '}'; - } + protected Mapper arg2(FuncIn funIn) { + return MapperS.of(funIn).map("getVal2", FuncIn::getVal2); } } ''' @@ -593,163 +303,82 @@ class RosettaCalculationGenerationTest { @Test def void testWierdness() { val calculation = ''' - calculation RTS_22_Fields { - string transactionReferenceNumber defined by: "SPH"+linkId - string tradingDateTime defined by: tradeDate + tradeTime - - where - linkId string : is FuncIn->valS - tradeDate date : is FuncIn->val1 - tradeTime time : is FuncIn->val2 - } class FuncIn { valS string (1..1); val1 date (1..1); val2 time (1..1); } + class FuncOut { + transactionReferenceNumber string (1..1); + tradingDateTime string (1..1); + } + + func RTS_22_Fields : + inputs: funcIn FuncIn (1..1) + + output: out FuncOut (1..1) + alias linkId: funcIn -> valS + alias tradeDate: funcIn -> val1 + alias tradeTime: funcIn -> val2 + assign-output out -> transactionReferenceNumber: "SPH"+linkId + assign-output out -> tradingDateTime: + tradeDate + tradeTime '''.generateCode - val calcJava = calculation.get("com.rosetta.test.model.calculation.RTS_22_Fields") + val calcJava = calculation.get("com.rosetta.test.model.functions.RTS_22_Fields") //RosettaBlueprintTest.writeOutClasses(calculation, "testWierdness") calculation.compileToClasses val expected = ''' - package com.rosetta.test.model.calculation; + package com.rosetta.test.model.functions; - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperMaths; + import com.rosetta.model.lib.functions.MapperS; + import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.model.lib.records.Date; import com.rosetta.test.model.FuncIn; + import com.rosetta.test.model.FuncOut; import java.lang.String; + import java.lang.SuppressWarnings; import java.time.LocalTime; - import java.util.Arrays; - import java.util.List; - public class RTS_22_Fields { - - public CalculationResult calculate(FuncIn paramFuncIn) { - CalculationInput input = new CalculationInput().create(paramFuncIn); - CalculationResult result = new CalculationResult(input); - result.transactionReferenceNumber = ("SPH" + input.linkId); - result.tradingDateTime = Date.of(input.tradeDate, input.tradeTime).toString(); - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private String linkId; - private Date tradeDate; - private LocalTime tradeTime; + + public class RTS_22_Fields implements RosettaFunction { + + /** + * @param funcIn + * @return out + */ + public FuncOut evaluate(FuncIn funcIn) { - public CalculationInput create(FuncIn inputParam) { - this.linkId = inputParam.getValS(); - this.tradeDate = inputParam.getVal1(); - this.tradeTime = inputParam.getVal2(); - return this; - } - - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("RTS_22_Fields", "string transactionReferenceNumber defined by: 'SPH'+linkId", this), - new Formula("RTS_22_Fields", "string tradingDateTime defined by: tradeDate + tradeTime", this)); - } + FuncOut out = doEvaluate(funcIn).build(); - public String getLinkId() { - return linkId; - } - - public Date getTradeDate() { - return tradeDate; - } - - public LocalTime getTradeTime() { - return tradeTime; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("linkId", String.class, (IResult res) -> ((CalculationInput) res).getLinkId()), - new Attribute<>("tradeDate", Date.class, (IResult res) -> ((CalculationInput) res).getTradeDate()), - new Attribute<>("tradeTime", LocalTime.class, (IResult res) -> ((CalculationInput) res).getTradeTime()) - ); + return out; + } - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - + protected FuncOut.FuncOutBuilder doEvaluate(FuncIn funcIn) { + FuncOut.FuncOutBuilder outHolder = FuncOut.builder(); + @SuppressWarnings("unused") FuncOut out = outHolder.build(); + outHolder + .setTransactionReferenceNumber(MapperMaths.add(MapperS.of("SPH"), MapperS.of(linkId(funcIn).get())).get()); + ; + out = outHolder.build(); + outHolder + .setTradingDateTime(MapperMaths.add(MapperS.of(tradeDate(funcIn).get()), MapperS.of(tradeTime(funcIn).get())).get()); + ; + return outHolder; } - public static class CalculationResult implements ICalculationResult { - private CalculationInput calculationInput; + protected Mapper linkId(FuncIn funcIn) { + return MapperS.of(funcIn).map("getValS", FuncIn::getValS); + } - private String transactionReferenceNumber; - private String tradingDateTime; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public String getTransactionReferenceNumber() { - return this.transactionReferenceNumber; - } - - public CalculationResult setTransactionReferenceNumber(String transactionReferenceNumber) { - this.transactionReferenceNumber = transactionReferenceNumber; - return this; - } - - public String getTradingDateTime() { - return this.tradingDateTime; - } - - public CalculationResult setTradingDateTime(String tradingDateTime) { - this.tradingDateTime = tradingDateTime; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("transactionReferenceNumber", String.class, (IResult res) -> ((CalculationResult) res).getTransactionReferenceNumber()), - new Attribute<>("tradingDateTime", String.class, (IResult res) -> ((CalculationResult) res).getTradingDateTime()) - ); + protected Mapper tradeDate(FuncIn funcIn) { + return MapperS.of(funcIn).map("getVal1", FuncIn::getVal1); + } - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (transactionReferenceNumber != null ? !transactionReferenceNumber.equals(_that.transactionReferenceNumber) : _that.transactionReferenceNumber != null) return false; - if (tradingDateTime != null ? !tradingDateTime.equals(_that.tradingDateTime) : _that.tradingDateTime != null) return false; - return true; - } - - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (transactionReferenceNumber != null ? transactionReferenceNumber.hashCode() : 0); - _result = 31 * _result + (tradingDateTime != null ? tradingDateTime.hashCode() : 0); - return _result; - } - - @Override - public String toString() { - return "CalculationResult {" + - "transactionReferenceNumber=" + this.transactionReferenceNumber + ", " + - "tradingDateTime=" + this.tradingDateTime + - '}'; - } + protected Mapper tradeTime(FuncIn funcIn) { + return MapperS.of(funcIn).map("getVal2", FuncIn::getVal2); } } ''' @@ -880,683 +509,237 @@ class RosettaCalculationGenerationTest { @Test def void shouldResolveFunctionDependencies() { - ''' - calculation Adder { - res defined by: arg1 - - where - arg1 int : is AddOne( 1 ) -> out - } - - function AddOne( arg int ) { - out int; - } + ''' + func Adder: + output: res int (1..1) + alias arg1 : AddOne( 1 ) + assign-output res : arg1 + + func AddOne: + inputs: arg int (1..1) + output: out int(1..1) '''.assertToGeneratedCalculation( ''' - package com.rosetta.test.model.calculation; + package com.rosetta.test.model.functions; - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; + import com.google.inject.Inject; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperS; + import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.test.model.functions.AddOne; import java.lang.Integer; - import java.util.Arrays; - import java.util.List; - public class Adder { - - private final AddOne addOne; - - public Adder(AddOne addOne) { - this.addOne = addOne; - } - - public CalculationResult calculate() { - CalculationInput input = new CalculationInput().create(addOne); - CalculationResult result = new CalculationResult(input); - result.res = input.arg1; - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private Integer arg1; - - public CalculationInput create(AddOne addOne) { - this.arg1 = addOne.execute(1).getOut(); - return this; - } - - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("Adder", "res defined by: arg1", this)); - } - - public Integer getArg1() { - return arg1; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("arg1", Integer.class, (IResult res) -> ((CalculationInput) res).getArg1()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - } - - public static class CalculationResult implements ICalculationResult { - - private CalculationInput calculationInput; - - private Integer res; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public Integer getRes() { - return this.res; - } - - public CalculationResult setRes(Integer res) { - this.res = res; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("res", Integer.class, (IResult res) -> ((CalculationResult) res).getRes()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (res != null ? !res.equals(_that.res) : _that.res != null) return false; - return true; - } - - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (res != null ? res.hashCode() : 0); - return _result; - } - - @Override - public String toString() { - return "CalculationResult {" + - "res=" + this.res + - '}'; - } - } - } - ''' - ) - } - - @Test - def void shouldResolveTransitiveFunctionDependencies() { - ''' - class MathInput - { - mathInput string (1..1); - math Math (1..1); - } - - calculation AddOrSubtract { - res defined by: arg1 - - where - arg1 string : is MathInput -> math - arg2 string : is AddThree( '3' ) -> out - } - - function AddOne( arg string ) { - out string; - } - - function SubOne( arg string ) { - out string; - } - function AddThree( arg string ) { - out string; - } - - enum Math - { - INCR, - DECR - } - - calculation Math.INCR { - defined by: arg1 - - where - arg1 string : is AddOne(MathInput -> mathInput) -> out - } - - calculation Math.DECR { - defined by: arg1 - - where - arg1 string : is SubOne(MathInput -> mathInput) -> out - } - '''.assertToGeneratedCalculation( - ''' - package com.rosetta.test.model.calculation; - - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; - import com.rosetta.test.model.MathInput; - import com.rosetta.test.model.functions.AddOne; - import com.rosetta.test.model.functions.AddThree; - import com.rosetta.test.model.functions.SubOne; - import java.lang.String; - import java.util.ArrayList; - import java.util.Arrays; - import java.util.List; - - public class AddOrSubtract { - - private final AddOne addOne; - private final SubOne subOne; - private final AddThree addThree; - - public AddOrSubtract(AddOne addOne, SubOne subOne, AddThree addThree) { - this.addOne = addOne; - this.subOne = subOne; - this.addThree = addThree; - } - - public CalculationResult calculate(MathInput paramMathInput) { - CalculationInput input = new CalculationInput().create(paramMathInput, addOne, subOne, addThree); - CalculationResult result = new CalculationResult(input); - result.res = input.arg1; - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private final List calculationResults = new ArrayList<>(); - private String arg1; - private String arg2; - - public CalculationInput create(MathInput inputParam, AddOne addOne, SubOne subOne, AddThree addThree) { - Math.CalculationResult mathCalculationResult = new Math(addOne, subOne).calculate(inputParam, inputParam.getMath()); - this.calculationResults.add(mathCalculationResult); - this.arg1 = mathCalculationResult.getValue(); - this.arg2 = addThree.execute("3").getOut(); - return this; - } + public class Adder implements RosettaFunction { - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("AddOrSubtract", "res defined by: arg1", this)); - } - - @Override - public List getCalculationResults() { - return calculationResults; - } + // RosettaFunction dependencies + // + @Inject protected AddOne addOne; + + /** + * @return res + */ + public Integer evaluate() { - public String getArg1() { - return arg1; - } - - public String getArg2() { - return arg2; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("arg1", String.class, (IResult res) -> ((CalculationInput) res).getArg1()), - new Attribute<>("arg2", String.class, (IResult res) -> ((CalculationInput) res).getArg2()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } + Integer res = doEvaluate(); + return res; } - public static class CalculationResult implements ICalculationResult { - - private CalculationInput calculationInput; + protected Integer doEvaluate() { + Mapper resHolder = null; + resHolder = MapperS.of(arg1().get()); + return resHolder.get(); + } - private String res; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public String getRes() { - return this.res; - } - - public CalculationResult setRes(String res) { - this.res = res; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("res", String.class, (IResult res) -> ((CalculationResult) res).getRes()) - ); - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (res != null ? !res.equals(_that.res) : _that.res != null) return false; - return true; - } - - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (res != null ? res.hashCode() : 0); - return _result; - } - - @Override - public String toString() { - return "CalculationResult {" + - "res=" + this.res + - '}'; - } + protected Mapper arg1() { + return MapperS.of(addOne.evaluate(MapperS.of(Integer.valueOf(1)).get())); } } ''' ) } + @Test def void shouldResolveExternalFunctionDependenciesWhenEnumCalculation() { - ''' + val generated = ''' class MathInput { - mathInput string (1..1); - math Math (1..1); + mathInput string (1..1); + math Math (1..1); } - function AddOne( arg string ) { - out string; - } + func AddOne: + inputs: arg string (1..1) + output: out string (1..1) + + func SubOne: + inputs: arg string (1..1) + output: out string (1..1) - function SubOne( arg string ) { - out string; - } enum Math { - INCR, - DECR + INCR, + DECR } - calculation Math.INCR { - defined by: arg1 - - where - arg1 string : is AddOne(MathInput -> mathInput) -> out - } + func MathFunc: + inputs: + in1 Math (1..1) + in2 MathInput (1..1) + output: arg1 string (1..1) - calculation Math.DECR { - defined by: arg1 - - where - arg1 string : is SubOne(MathInput -> mathInput) -> out - } - '''.assertToGeneratedCalculation( + func MathFunc (in1 : Math -> INCR ): + assign-output arg1: AddOne(in2 -> mathInput) + + func MathFunc (in1 : Math -> DECR ): + assign-output arg1: SubOne(in2 -> mathInput) + '''.generateCode + .get("com.rosetta.test.model.functions.MathFunc") + assertEquals( ''' - package com.rosetta.test.model.calculation; + package com.rosetta.test.model.functions; - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; + import com.google.inject.Inject; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperS; + import com.rosetta.model.lib.functions.RosettaFunction; + import com.rosetta.test.model.Math; import com.rosetta.test.model.MathInput; import com.rosetta.test.model.functions.AddOne; import com.rosetta.test.model.functions.SubOne; import java.lang.String; - import java.util.Arrays; - import java.util.List; + /** * @version test */ - public class Math { + public class MathFunc { - private final AddOne addOne; - private final SubOne subOne; + @Inject protected MathFunc.INCR INCR; + @Inject protected MathFunc.DECR DECR; - public Math(AddOne addOne, SubOne subOne) { - this.addOne = addOne; - this.subOne = subOne; - } - - public CalculationResult calculate(MathInput mathInput, com.rosetta.test.model.Math enumValue) { - switch (enumValue) { + public String evaluate(Math in1, MathInput in2) { + switch (in1) { case INCR: - return new INCR(addOne).calculate(mathInput); + return INCR.evaluate(in1, in2); case DECR: - return new DECR(subOne).calculate(mathInput); + return DECR.evaluate(in1, in2); default: - throw new IllegalArgumentException("Enum value not implemented: " + enumValue); + throw new IllegalArgumentException("Enum value not implemented: " + in1); } } - public static class INCR { - - private final AddOne addOne; - - public INCR(AddOne addOne) { - this.addOne = addOne; - } - - public CalculationResult calculate(MathInput paramMathInput) { - CalculationInput input = new CalculationInput().create(paramMathInput, addOne); - CalculationResult result = new CalculationResult(input); - result.value = input.arg1; - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private String arg1; - - public CalculationInput create(MathInput inputParam, AddOne addOne) { - this.arg1 = addOne.execute(inputParam.getMathInput()).getOut(); - return this; - } + + public static class INCR implements RosettaFunction { - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("INCR", "defined by: arg1", this)); - } + // RosettaFunction dependencies + // + @Inject protected AddOne addOne; + + /** + * @param in1 + * @param in2 + * @return arg1 + */ + public String evaluate(Math in1, MathInput in2) { - public String getArg1() { - return arg1; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("arg1", String.class, (IResult res) -> ((CalculationInput) res).getArg1()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } + String arg1 = doEvaluate(in1, in2); - } - } - public static class DECR { - - private final SubOne subOne; - - public DECR(SubOne subOne) { - this.subOne = subOne; + return arg1; } - public CalculationResult calculate(MathInput paramMathInput) { - CalculationInput input = new CalculationInput().create(paramMathInput, subOne); - CalculationResult result = new CalculationResult(input); - result.value = input.arg1; - return result; + protected String doEvaluate(Math in1, MathInput in2) { + Mapper arg1Holder = null; + arg1Holder = MapperS.of(addOne.evaluate(in2.getMathInput())); + return arg1Holder.get(); } - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private String arg1; - - public CalculationInput create(MathInput inputParam, SubOne subOne) { - this.arg1 = subOne.execute(inputParam.getMathInput()).getOut(); - return this; - } - - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("DECR", "defined by: arg1", this)); - } - - public String getArg1() { - return arg1; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("arg1", String.class, (IResult res) -> ((CalculationInput) res).getArg1()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - } } - public static class CalculationResult implements ICalculationResult { - - private ICalculationInput calculationInput; - private String value; - - public CalculationResult(ICalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public String getValue() { - return this.value; - } + public static class DECR implements RosettaFunction { - public CalculationResult setValue(String value) { - this.value = value; - return this; - } - - @Override - public ICalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("value", String.class, (IResult res) -> ((CalculationResult) res).getValue()) - ); + // RosettaFunction dependencies + // + @Inject protected SubOne subOne; - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (value != null ? !value.equals(_that.value) : _that.value != null) return false; - return true; + /** + * @param in1 + * @param in2 + * @return arg1 + */ + public String evaluate(Math in1, MathInput in2) { + + String arg1 = doEvaluate(in1, in2); + + return arg1; } - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (value != null ? value.hashCode() : 0); - return _result; + protected String doEvaluate(Math in1, MathInput in2) { + Mapper arg1Holder = null; + arg1Holder = MapperS.of(subOne.evaluate(in2.getMathInput())); + return arg1Holder.get(); } - @Override - public String toString() { - return "CalculationResult {" + - "value=" + this.value + - '}'; - } } } - ''') + '''.toString, generated) } @Test def void shouldResolveFunctionDependenciesWhenReferencedInAlias() { ''' - calculation Adder { - res defined by: arg1 + func Adder : + inputs: arg1 int (1..1) + output: res int (1..1) - where - alias addedOne AddOne( 1 ) - arg1 int : is addedOne -> out - } + alias addedOne: AddOne( 1 ) + assign-output res: addedOne - function AddOne( arg int ) { - out int; - } + func AddOne: + inputs: arg int (1..1) + output: out int (1..1) '''.assertToGeneratedCalculation( ''' - package com.rosetta.test.model.calculation; + package com.rosetta.test.model.functions; - import com.rosetta.model.lib.functions.Formula; - import com.rosetta.model.lib.functions.ICalculationInput; - import com.rosetta.model.lib.functions.ICalculationResult; - import com.rosetta.model.lib.functions.IResult; + import com.google.inject.Inject; + import com.rosetta.model.lib.functions.Mapper; + import com.rosetta.model.lib.functions.MapperS; + import com.rosetta.model.lib.functions.RosettaFunction; import com.rosetta.test.model.functions.AddOne; import java.lang.Integer; - import java.util.Arrays; - import java.util.List; - public class Adder { - - private final AddOne addOne; - - public Adder(AddOne addOne) { - this.addOne = addOne; - } - - public CalculationResult calculate() { - CalculationInput input = new CalculationInput().create(addOne); - CalculationResult result = new CalculationResult(input); - result.res = input.arg1; - return result; - } - - public static class CalculationInput implements ICalculationInput { - private CalculationInput input = this; // For when arguments need to reference other arguments - private Integer arg1; - - public CalculationInput create(AddOne addOne) { - AddOne.CalculationResult addedOneAlias = addOne.execute(1); - this.arg1 = addedOneAlias.getOut(); - return this; - } + + public class Adder implements RosettaFunction { - @Override - public List getFormulas() { - return Arrays.asList( - new Formula("Adder", "res defined by: arg1", this)); - } + // RosettaFunction dependencies + // + @Inject protected AddOne addOne; + + /** + * @param arg1 + * @return res + */ + public Integer evaluate(Integer arg1) { - public Integer getArg1() { - return arg1; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("arg1", Integer.class, (IResult res) -> ((CalculationInput) res).getArg1()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } + Integer res = doEvaluate(arg1); + return res; } - public static class CalculationResult implements ICalculationResult { - - private CalculationInput calculationInput; + protected Integer doEvaluate(Integer arg1) { + Mapper resHolder = null; + resHolder = MapperS.of(addedOne(arg1).get()); + return resHolder.get(); + } - private Integer res; - - public CalculationResult(CalculationInput calculationInput) { - this.calculationInput = calculationInput; - } - public Integer getRes() { - return this.res; - } - - public CalculationResult setRes(Integer res) { - this.res = res; - return this; - } - - @Override - public CalculationInput getCalculationInput() { - return calculationInput; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("res", Integer.class, (IResult res) -> ((CalculationResult) res).getRes()) - ); - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (res != null ? !res.equals(_that.res) : _that.res != null) return false; - return true; - } - - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (res != null ? res.hashCode() : 0); - return _result; - } - - @Override - public String toString() { - return "CalculationResult {" + - "res=" + this.res + - '}'; - } + protected Mapper addedOne(Integer arg1) { + return MapperS.of(addOne.evaluate(MapperS.of(Integer.valueOf(1)).get())); } } ''' diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationTest.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationTest.xtend index 3a4c91cb4..a7b68d9ca 100644 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationTest.xtend +++ b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaCalculationTest.xtend @@ -153,14 +153,15 @@ class RosettaCalculationTest implements RosettaIssueCodes { a number (1..1); b number (1..1); } - - calculation FooNumberCalc { - number: 1 + aArg + bArg + + func FooNumberCalc: + inputs: f Foo(1..1) + output: + out number (1..1) + alias aArg: f -> a + alias bArg: f -> b - where - aArg: is Foo -> a - bArg: is Foo -> b - } + assign-output out : 1 + aArg + bArg '''.generateCode val classes = code.compileToClasses @@ -180,13 +181,14 @@ class RosettaCalculationTest implements RosettaIssueCodes { b int (1..1); } - calculation FooIntCalc { - defined by: if aArg > 0 then aArg * 5 else bArg + 5 + func FooIntCalc: + inputs: f Foo(1..1) + output: + out int (1..1) + alias aArg: f -> a + alias bArg: f -> b - where - aArg: is Foo -> a - bArg: is Foo -> b - } + assign-output out : if aArg > 0 then aArg * 5 else bArg + 5 '''.generateCode val classes = code.compileToClasses @@ -212,13 +214,14 @@ class RosettaCalculationTest implements RosettaIssueCodes { b number (1..1); } - calculation FooNumberCalc { - number: if aArg >= 0.0 then aArg * 5.0 else bArg / 5 + func FooNumberCalc: + inputs: f Foo(1..1) + output: + out number (1..1) + alias aArg: f -> a + alias bArg: f -> b - where - aArg: is Foo -> a - bArg: is Foo -> b - } + assign-output out : if aArg >= 0.0 then aArg * 5.0 else bArg / 5 '''.generateCode val classes = code.compileToClasses @@ -237,10 +240,7 @@ class RosettaCalculationTest implements RosettaIssueCodes { } def private calculate(Object calc, Object input) { - val result = IResult.cast(calc.class.getMethod('calculate', input.class).invoke(calc, input)) - - assertThat('Unexpected number of result attributes', result.attributes.size, is(1)) - - result.attributes.get(0).get(result) + val result = calc.class.getMethod('evaluate', input.class).invoke(calc, input) + result } } diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaFunctionGenerationTest.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaFunctionGenerationTest.xtend index ab3080f30..e49016277 100644 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaFunctionGenerationTest.xtend +++ b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/calculation/RosettaFunctionGenerationTest.xtend @@ -16,93 +16,37 @@ class RosettaFunctionGenerationTest { @Test def void testSimpleFunctionGeneration() { ''' - function FuncFoo (name string, name2 string) { - result string; - result2 string; - } + func FuncFoo: + inputs: + name string (0..1) + name2 string (0..1) + output: + result string (0..1) '''.assertToGeneratedFunction( ''' package com.rosetta.test.model.functions; import com.google.inject.ImplementedBy; - import com.rosetta.model.lib.functions.IFunctionResult; - import com.rosetta.model.lib.functions.IResult; + import com.rosetta.model.lib.functions.RosettaFunction; import java.lang.String; - import java.util.Arrays; - import java.util.List; - /** - * @version test - */ + @ImplementedBy(FuncFooImpl.class) - public interface FuncFoo { - - CalculationResult execute(String name, String name2); - - class CalculationResult implements IFunctionResult { - - - private String result; - private String result2; - - public CalculationResult() { - } - public String getResult() { - return this.result; - } - - public CalculationResult setResult(String result) { - this.result = result; - return this; - } - - public String getResult2() { - return this.result2; - } - - public CalculationResult setResult2(String result2) { - this.result2 = result2; - return this; - } - - private static final List> ATTRIBUTES = Arrays.asList( - new Attribute<>("result", String.class, (IResult res) -> ((CalculationResult) res).getResult()), - new Attribute<>("result2", String.class, (IResult res) -> ((CalculationResult) res).getResult2()) - ); - - @Override - public List> getAttributes() { - return ATTRIBUTES; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - CalculationResult _that = (CalculationResult) o; - - if (result != null ? !result.equals(_that.result) : _that.result != null) return false; - if (result2 != null ? !result2.equals(_that.result2) : _that.result2 != null) return false; - return true; - } + public abstract class FuncFoo implements RosettaFunction { + + /** + * @param name + * @param name2 + * @return result + */ + public String evaluate(String name, String name2) { - @Override - public int hashCode() { - int _result = 0; - _result = 31 * _result + (result != null ? result.hashCode() : 0); - _result = 31 * _result + (result2 != null ? result2.hashCode() : 0); - return _result; - } + String result = doEvaluate(name, name2); - @Override - public String toString() { - return "CalculationResult {" + - "result=" + this.result + ", " + - "result2=" + this.result2 + - '}'; - } + return result; } + + protected abstract String doEvaluate(String name, String name2); } ''' ) diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend deleted file mode 100644 index f25fbb004..000000000 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/function/FunctionGeneratorTest.xtend +++ /dev/null @@ -1,728 +0,0 @@ -package com.regnosys.rosetta.generator.java.function - -import com.google.inject.Inject -import com.regnosys.rosetta.generator.java.util.ImportingStringConcatination -import com.regnosys.rosetta.rosetta.RosettaFunction -import com.regnosys.rosetta.tests.RosettaInjectorProvider -import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper -import com.regnosys.rosetta.tests.util.ModelHelper -import com.rosetta.model.lib.records.Date -import org.eclipse.xtext.testing.InjectWith -import org.eclipse.xtext.testing.extensions.InjectionExtension -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.^extension.ExtendWith - -import static org.hamcrest.CoreMatchers.* -import static org.hamcrest.MatcherAssert.* -import static org.junit.jupiter.api.Assertions.* - -@ExtendWith(InjectionExtension) -@InjectWith(RosettaInjectorProvider) -class FunctionGeneratorTest { - - @Inject extension ModelHelper - @Inject extension CodeGeneratorTestHelper - @Inject extension FunctionGenerator generator - - @Inject JavaQualifiedTypeProvider.Factory factory - - var ImportingStringConcatination concatenator; - - @BeforeEach - def void setup() { - concatenator = new ImportingStringConcatination - } - - @Test - def void shouldGenerateEnrichMethod() { - val javaNames = factory.create(javaPackages) - - val function = ''' - class Bar {} - - spec Foo: - output: - result Bar (1..1) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributeEnrichMethod(javaNames) - concatenator.append(result) - - assertEquals('protected abstract Bar doEvaluate();', concatenator.toString.trim) - assertThat(concatenator.imports, hasItems(endsWith('.Bar'))) - } - - @Test - def void shouldGenerateEnrichMethodWhenBasicTypeOutput() { - val javaNames = factory.create(javaPackages) - - val function = ''' - class Bar {} - - spec Foo: - output: - result number (1..1) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributeEnrichMethod(javaNames) - concatenator.append(result) - - assertEquals('protected abstract BigDecimal doEvaluate();', concatenator.toString.trim) - assertThat(concatenator.imports, hasItems(endsWith('.BigDecimal'))) - } - - @Test - def void shouldGenerateEnrichMethodWhenListOutput() { - val javaNames = factory.create(javaPackages) - - val function = ''' - class Bar {} - - spec Foo: - output: - result Bar (1..*) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributeEnrichMethod(javaNames) - concatenator.append(result) - - assertEquals('protected abstract List doEvaluate();', concatenator.toString.trim ) - assertThat(concatenator.imports, hasItems(endsWith('.Bar'), endsWith('.List'))) - } - - @Test - def void shouldGenerateEnrichMethodArgumentsWhenInputsExist() { - val javaNames = factory.create(javaPackages) - - val function = ''' - class Bar {} - class Foo {} - - spec FooFunc: - inputs: - input1 Foo (1..1) - input2 string (1..1) - - output: - result Bar (1..1) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributeEnrichMethod(javaNames) - concatenator.append(result) - - assertEquals('protected abstract Bar doEvaluate(Foo input1, String input2);', concatenator.toString.trim) - assertThat(concatenator.imports, hasItems(endsWith('.Foo'), endsWith('.Bar'))) - } - - @Test - def void shouldGenerateEnrichMethodArgumentsWhenInputsIsMultiple() { - val javaNames = factory.create(javaPackages) - - val function = ''' - class Bar {} - class Foo {} - - spec FooFunc: - inputs: - input1 Foo (1..1) - input2 string (1..*) - - output: - result Bar (1..1) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributeEnrichMethod(javaNames) - concatenator.append(result) - - assertThat(concatenator.toString.trim, is('protected abstract Bar doEvaluate(Foo input1, List input2);')) - assertThat(concatenator.imports, hasItems(endsWith('.Foo'), endsWith('.Bar'))) - } - - @Test - def void shouldGenerateEvaluateMethodWhenRosettaClassReturnType() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - class Bar {} - class Foo {} - - spec FooFunc: - inputs: - input1 Foo (1..1) - input2 string (1..1) - - output: - result Bar (1..1) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val fooFunc = functions.filter[name == 'FooFunc'].head - val deps = functions.filter[name != 'FooFunc'] - - val result = fooFunc.contributeEvaluateMethod(javaNames, deps) - concatenator.append(result) - - val expected = ''' - /** - * @param input1 - * @param input2 - * @return result - */ - public Bar evaluate(Foo input1, String input2) { - - // Delegate to implementation - // - Bar result = doEvaluate(input1, input2); - - return result; - } - ''' - assertEquals(expected.trim, concatenator.toString.trim) - assertThat(concatenator.imports, hasItems(endsWith('.Bar'), endsWith('.Foo'))) - } - - @Test - def void shouldGenerateEvaluateMethodWhenBasicReturnType() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - class Bar {} - class Foo {} - - spec FooFunc: - inputs: - input1 Foo (1..1) - input2 string (1..1) - - output: - result number (1..1) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val fooFunc = functions.filter[name == 'FooFunc'].head - val deps = functions.filter[name != 'FooFunc'] - - val result = fooFunc.contributeEvaluateMethod(javaNames, deps) - concatenator.append(result) - - val expected = ''' - - /** - * @param input1 - * @param input2 - * @return result - */ - public BigDecimal evaluate(Foo input1, String input2) { - - // Delegate to implementation - // - BigDecimal result = doEvaluate(input1, input2); - - return result; - } - ''' - assertEquals(expected, concatenator.toString) - assertThat(concatenator.imports, hasItems(endsWith('.Foo'))) - } - - @Test - def void shouldGenerateEvaluateMethodWhenMultipleReturn() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - class Bar {} - class Foo {} - - spec FooFunc: - inputs: - input1 Foo (1..1) - input2 string (1..1) - - output: - results Bar (1..*) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val result = functions.head.contributeEvaluateMethod(javaNames, functions.tail) - concatenator.append(result) - - val expected = ''' - /** - * @param input1 - * @param input2 - * @return results - */ - public List evaluate(Foo input1, String input2) { - - // Delegate to implementation - // - List results = doEvaluate(input1, input2); - - return results; - } - ''' - assertEquals(expected.trim, concatenator.toString.trim) - assertThat(concatenator.imports, hasItems(endsWith('.Foo'))) - } - - @Test - def void shouldGeneratePreConditions() { - val javaNames = factory.create(javaPackages) - - val function = ''' - spec FooFunc: - inputs: - input1 number (1..1) - - output: - result number (1..1) - - pre-condition <"pre condition 1 should pass">: - input1 > 42; - input1 < 84; - - pre-condition <"pre condition 2 should pass">: - input1 < 100; - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributePreConditions(javaNames) - concatenator.append(result) - - val expected = ''' - // pre-conditions - // - assert - greaterThan(MapperS.of(input1), MapperS.of(Integer.valueOf(42))).get() && - lessThan(MapperS.of(input1), MapperS.of(Integer.valueOf(84))).get() - : "pre condition 1 should pass"; - assert - lessThan(MapperS.of(input1), MapperS.of(Integer.valueOf(100))).get() - : "pre condition 2 should pass"; - ''' - - assertEquals(expected.trim, concatenator.toString.trim) - } - - - @Test - def void shouldCompileWhenPreConditions() { - ''' - spec FooFunc: - inputs: - input1 number (1..1) - - output: - result number (1..1) - - pre-condition <"pre condition 1 should pass">: - input1 > 42; - input1 < 84; - - pre-condition <"pre condition 2 should pass">: - input1 < 100; - '''.compileJava8 - } - - @Test - def void shouldGeneratePostConditions() { - val javaNames = factory.create(javaPackages) - - val function = ''' - spec FooFunc: - inputs: - input1 number (1..1) - - output: - result number (1..1) - - post-condition <"post condition 1 should pass">: - input1 > 42; - input1 < 84; - - post-condition <"post condition 2 should pass">: - input1 < 100; - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributePostConditions(javaNames) - concatenator.append(result) - - val expected = ''' - // post-conditions - // - assert - greaterThan(MapperS.of(input1), MapperS.of(Integer.valueOf(42))).get() && - lessThan(MapperS.of(input1), MapperS.of(Integer.valueOf(84))).get() - : "post condition 1 should pass"; - assert - lessThan(MapperS.of(input1), MapperS.of(Integer.valueOf(100))).get() - : "post condition 2 should pass"; - ''' - - assertEquals(expected.trim, concatenator.toString.trim) - } - - @Test - def void shouldGenerateJavadoc() { - val function = ''' - spec FooFunc <"an example function level comment">: - inputs: - input1 number (1..1) <"is the one and only input"> - - output: - result number (1..1) <"is the number that is returned"> - - post-condition <"post condition 1 should pass">: - input1 > 42; - input1 < 84; - - post-condition <"post condition 2 should pass">: - input1 < 100; - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributeJavaDoc - concatenator.append(result) - - val expected = ''' - /** - * an example function level comment - */ - ''' - - assertEquals(expected, concatenator.toString) - } - - @Test - def void shouldGenerateConstructor() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - spec FooFunc <"an example function level comment">: - inputs: - input1 number (1..1) <"is the one and only input"> - - output: - result number (1..1) <"is the number that is returned"> - - post-condition <"post condition 1 should pass">: - input1 > 42; - input1 < 84; - - post-condition <"post condition 2 should pass">: - input1 < BarFunc(); - - spec BarFunc: - output: - result number (1..1) - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val result = functions.head.contributeConstructor(javaNames) - concatenator.append(result) - - val expected = ''' - protected FooFunc(ClassToInstanceMap classRegistry) { - - // On concrete instantiation, register implementation with function to implementation container - // - classRegistry.putInstance(FooFunc.class, this); - this.classRegistry = classRegistry; - } - ''' - - assertEquals(expected.trim, concatenator.toString.trim) - assertThat(concatenator.imports, hasItems(endsWith('.ClassToInstanceMap'), endsWith('.RosettaFunction'))) - } - - @Test - def void shouldGenerateConstructorWhenNoDependencies() { - val javaNames = factory.create(javaPackages) - - val function = ''' - spec FooFunc <"an example function level comment">: - inputs: - input1 number (1..1) <"is the one and only input"> - - output: - result number (1..1) <"is the number that is returned"> - - post-condition <"post condition 1 should pass">: - input1 > 42; - input1 < 84; - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction).head - - val result = function.contributeConstructor(javaNames) - concatenator.append(result) - - val expected = ''' - - protected FooFunc(ClassToInstanceMap classRegistry) { - - // On concrete instantiation, register implementation with function to implementation container - // - classRegistry.putInstance(FooFunc.class, this); - this.classRegistry = classRegistry; - } - ''' - - assertEquals(expected, concatenator.toString) - } - - @Test - def void shouldGenerateFields() { - val javaNames = factory.create(javaPackages) - - val result = contributeFields(javaNames) - concatenator.append(result) - - val expected = '''protected final ClassToInstanceMap classRegistry;''' - - assertEquals(expected.trim, concatenator.toString.trim) - assertThat(concatenator.imports, hasItems(endsWith('.ClassToInstanceMap'), endsWith('.RosettaFunction'))) - } - - @Test - def void shouldGenerateConditionWhenFunctionCall() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - spec FooFunc <"an example function level comment">: - inputs: - object1 Object1 (1..1) <"is the one and only input"> - object2 Object2 (1..1) - - output: - result number (1..1) <"is the number that is returned"> - - pre-condition <"post condition 1 should pass">: - if object1 exists then - object1 -> object2 -> result = BuzzFunc( object2 -> result ) and - object1 -> object2 -> result = BarFunc( object2 -> result ) - else - object1 -> object2 is absent and - object2 -> result count = 1; - - spec BuzzFunc: - inputs: - object2 Object2 (1..1) - output: - result number (1..1) - - spec BarFunc: - inputs: - object2 Object2 (1..1) - output: - result number (1..1) - - class Object1 { - object2 Object2 (1..1); - } - - class Object2 { - result number (1..1); - } - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val fooFunc = functions.filter[name == 'FooFunc'].head - - val result = fooFunc.contributePreConditions(javaNames) - concatenator.append(result) - - val expected = ''' - // pre-conditions - // - assert - doIf(exists(MapperS.of(object1), false),areEqual(MapperS.of(object1).map("getObject2", Object1::getObject2).map("getResult", Object2::getResult), MapperS.of(buzzFunc.evaluate(MapperS.of(object2).map("getResult", Object2::getResult).get()))).and(areEqual(MapperS.of(object1).map("getObject2", Object1::getObject2).map("getResult", Object2::getResult), MapperS.of(barFunc.evaluate(MapperS.of(object2).map("getResult", Object2::getResult).get())))),notExists(MapperS.of(object1).map("getObject2", Object1::getObject2)).and(areEqual(MapperS.of(MapperS.of(object2).map("getResult", Object2::getResult).resultCount()), MapperS.of(Integer.valueOf(1))))).get() - : "post condition 1 should pass"; - ''' - - assertEquals(expected.trim, concatenator.toString.trim) - } - - @Test - def void shouldGenerateConditionWhenFunctionCalledInsideBUiltInFunction() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - spec FooFunc <"an example function level comment">: - inputs: - object1 Object1 (1..1) <"is the one and only input"> - input2 Object1 (0..1) - output: - result number (1..1) <"is the number that is returned"> - - pre-condition <"post condition 1 should pass">: - BuzzFunc(object1 -> object2, input2) > 23; - - spec BuzzFunc: - inputs: - input1 Object2 (1..1) - input2 number (1..*) - output: - result number (1..1) - - class Object1 { - object2 Object2 (1..1); - object3 Object3 (1..*); - } - - class Object2 { - result number (1..1); - } - - class Object3 { - result number (1..1); - } - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val fooFunc = functions.filter[name == 'FooFunc'].head - - val result = fooFunc.contributePreConditions(javaNames) - concatenator.append(result) - - val expected = ''' - // pre-conditions - // - assert - greaterThan(MapperS.of(buzzFunc.evaluate(MapperS.of(object1).map("getObject2", Object1::getObject2).get(), MapperS.of(input2).get())), MapperS.of(Integer.valueOf(23))).get() - : "post condition 1 should pass"; - ''' - - assertEquals(expected.trim, concatenator.toString.trim) - } - - @Disabled @Test - def void shouldImportLocalDateWhenUsedInExpression() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - spec FooFunc <"an example function level comment">: - inputs: - input1 Object1 (1..1) <"is the one and only input"> - output: - result number (1..1) <"is the number that is returned"> - - pre-condition <"post condition 1 should pass">: - input1 -> object2 -> result exists; - - class Object1 { - object2 Object2 (1..1); - } - - class Object2 { - result date (1..1); - } - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val fooFunc = functions.filter[name == 'FooFunc'].head - - val result = fooFunc.contributePreConditions(javaNames) - concatenator.append(result) - - assertThat(concatenator.imports, hasItems(endsWith('java.time.LocalDate'))) - } - - @Test - def void shouldUseDate() { - val javaNames = factory.create(javaPackages) - - val functions = ''' - spec FooFunc <"an example function level comment">: - inputs: - input1 date (1..1) <"is the one and only input"> - output: - result date (1..1) <"is the number that is returned"> - '''.parseRosettaWithNoErrors.elements.filter(RosettaFunction) - - val fooFunc = functions.filter[name == 'FooFunc'].head - - val result = fooFunc.contributeEnrichMethod(javaNames) - concatenator.append(result) - -// println(concatenator.imports) -// println(concatenator.toString) - - assertThat(concatenator.imports, hasItems(endsWith(Date.name))) - } - - @Test - def void shouldGenerateCode() { - val functions = ''' - spec FooFunc <"an example function level comment">: - inputs: - input1 number (1..1) <"is the one and only input"> - - output: - result number (1..1) <"is the number that is returned"> - - post-condition <"post condition 1 should pass">: - input1 > 42; - input1 < BuzzFunc(); - - post-condition <"post condition 2 should pass">: - input1 < BarFunc(); - - spec BarFunc: - output: - result number (1..1) - - spec BuzzFunc: - output: - result number (1..1) - '''.generateCode(generator) - -// val fooFunc = functions.get(javaPackages.functions.packageName + '.FooFunc') -// println(fooFunc) - } - - - @Test - def void testFunctionGeneration() { - assertEquals( - ''' - package com.rosetta.test.model.functions; - - import com.google.inject.ImplementedBy; - import com.rosetta.model.lib.functions.MapperS; - import com.rosetta.model.lib.functions.RosettaFunction; - import java.lang.Integer; - import java.lang.String; - - import java.math.BigDecimal; - import org.isda.cdm.*; - import com.rosetta.model.lib.meta.*; - import static com.rosetta.model.lib.validation.ValidatorHelper.*; - - @ImplementedBy(FuncFooImpl.class) - public abstract class FuncFoo implements RosettaFunction { - - /** - * @param result - * @param result2 - * @return out - */ - public Integer evaluate(Integer result, String result2) { - // Delegate to implementation - // - Integer out = doEvaluate(result, result2); - // post-conditions - // - assert - greaterThan(MapperS.of(result), MapperS.of(Integer.valueOf(42))).get() - : ""; - return out; - } - - protected abstract Integer doEvaluate(Integer result, String result2); - } - '''.toString, - ''' - func FuncFoo: - inputs: - result int (1..1) - result2 string (1..1) - - output: - out int(1..1) - post-condition: result > 42; - '''.generateCode().get('com.rosetta.test.model.functions.FuncFoo') - ) - } -} diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/object/RosettaEnumGeneratorTest.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/object/RosettaEnumGeneratorTest.xtend index 541c69c4a..f92d93124 100644 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/object/RosettaEnumGeneratorTest.xtend +++ b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/generator/java/object/RosettaEnumGeneratorTest.xtend @@ -1,7 +1,7 @@ package com.regnosys.rosetta.generator.java.object import com.google.inject.Inject -import com.regnosys.rosetta.generator.java.enums.EnumGenerator +import com.regnosys.rosetta.generator.java.enums.EnumHelper import com.regnosys.rosetta.tests.RosettaInjectorProvider import com.regnosys.rosetta.tests.util.CodeGeneratorTestHelper import com.regnosys.rosetta.tests.util.ModelHelper @@ -15,7 +15,7 @@ import static org.hamcrest.MatcherAssert.* @ExtendWith(InjectionExtension) @InjectWith(RosettaInjectorProvider) -class RosettaEnumGeneratorTest { +class RosettaEnumHelperTest { @Inject extension CodeGeneratorTestHelper @Inject extension ModelHelper @@ -59,26 +59,26 @@ class RosettaEnumGeneratorTest { @Test def void shouldGenerateUppercaseUnderscoreFormattedEnumNames() { - assertThat(EnumGenerator.formatEnumName("ISDA1993Commodity"), is("ISDA_1993_COMMODITY")) - assertThat(EnumGenerator.formatEnumName("ISDA1998FX"), is("ISDA1998FX")) - assertThat(EnumGenerator.formatEnumName("iTraxxEuropeDealer"), is("I_TRAXX_EUROPE_DEALER")) - assertThat(EnumGenerator.formatEnumName("StandardLCDS"), is("STANDARD_LCDS")) - assertThat(EnumGenerator.formatEnumName("_1_1"), is("_1_1")) - assertThat(EnumGenerator.formatEnumName("_30E_360_ISDA"), is("_30E_360_ISDA")) - assertThat(EnumGenerator.formatEnumName("ACT_365L"), is("ACT_365L")) - assertThat(EnumGenerator.formatEnumName("OSPPrice"), is("OSP_PRICE")) - assertThat(EnumGenerator.formatEnumName("FRAYield"), is("FRA_YIELD")) - assertThat(EnumGenerator.formatEnumName("AED-EBOR-Reuters"), is("AED_EBOR_REUTERS")) - assertThat(EnumGenerator.formatEnumName("EUR-EURIBOR-Reuters"), is("EUR_EURIBOR_REUTERS")) - assertThat(EnumGenerator.formatEnumName("DJ.iTraxx.Europe"), is("DJ_I_TRAXX_EUROPE")) - assertThat(EnumGenerator.formatEnumName("IVS1OpenMarkets"), is("IVS_1_OPEN_MARKETS")) - assertThat(EnumGenerator.formatEnumName("D"), is("D")) - assertThat(EnumGenerator.formatEnumName("_1"), is("_1")) - assertThat(EnumGenerator.formatEnumName("DJ.CDX.NA"), is("DJ_CDX_NA")) - assertThat(EnumGenerator.formatEnumName("novation"), is("NOVATION")) - assertThat(EnumGenerator.formatEnumName("partialNovation"), is("PARTIAL_NOVATION")) - assertThat(EnumGenerator.formatEnumName("ALUMINIUM_ALLOY_LME_15_MONTH"), is("ALUMINIUM_ALLOY_LME_15_MONTH")) - assertThat(EnumGenerator.formatEnumName("AggregateClient"), is("AGGREGATE_CLIENT")) - assertThat(EnumGenerator.formatEnumName("Currency1PerCurrency2"), is("CURRENCY_1_PER_CURRENCY_2")) + assertThat(EnumHelper.formatEnumName("ISDA1993Commodity"), is("ISDA_1993_COMMODITY")) + assertThat(EnumHelper.formatEnumName("ISDA1998FX"), is("ISDA1998FX")) + assertThat(EnumHelper.formatEnumName("iTraxxEuropeDealer"), is("I_TRAXX_EUROPE_DEALER")) + assertThat(EnumHelper.formatEnumName("StandardLCDS"), is("STANDARD_LCDS")) + assertThat(EnumHelper.formatEnumName("_1_1"), is("_1_1")) + assertThat(EnumHelper.formatEnumName("_30E_360_ISDA"), is("_30E_360_ISDA")) + assertThat(EnumHelper.formatEnumName("ACT_365L"), is("ACT_365L")) + assertThat(EnumHelper.formatEnumName("OSPPrice"), is("OSP_PRICE")) + assertThat(EnumHelper.formatEnumName("FRAYield"), is("FRA_YIELD")) + assertThat(EnumHelper.formatEnumName("AED-EBOR-Reuters"), is("AED_EBOR_REUTERS")) + assertThat(EnumHelper.formatEnumName("EUR-EURIBOR-Reuters"), is("EUR_EURIBOR_REUTERS")) + assertThat(EnumHelper.formatEnumName("DJ.iTraxx.Europe"), is("DJ_I_TRAXX_EUROPE")) + assertThat(EnumHelper.formatEnumName("IVS1OpenMarkets"), is("IVS_1_OPEN_MARKETS")) + assertThat(EnumHelper.formatEnumName("D"), is("D")) + assertThat(EnumHelper.formatEnumName("_1"), is("_1")) + assertThat(EnumHelper.formatEnumName("DJ.CDX.NA"), is("DJ_CDX_NA")) + assertThat(EnumHelper.formatEnumName("novation"), is("NOVATION")) + assertThat(EnumHelper.formatEnumName("partialNovation"), is("PARTIAL_NOVATION")) + assertThat(EnumHelper.formatEnumName("ALUMINIUM_ALLOY_LME_15_MONTH"), is("ALUMINIUM_ALLOY_LME_15_MONTH")) + assertThat(EnumHelper.formatEnumName("AggregateClient"), is("AGGREGATE_CLIENT")) + assertThat(EnumHelper.formatEnumName("Currency1PerCurrency2"), is("CURRENCY_1_PER_CURRENCY_2")) } } diff --git a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend index e1e707dc1..86f9d4439 100644 --- a/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend +++ b/com.regnosys.rosetta.tests/src/com/regnosys/rosetta/tests/util/CodeGeneratorTestHelper.xtend @@ -85,7 +85,11 @@ class CodeGeneratorTestHelper { } def createCalculationInstance(Map> classes, String className) { - return classes.get(javaPackages.calculation.packageName + '.' + className).newInstance + val fqn = javaPackages.functions.packageName + '.' + className + val foundClazz = classes.get(fqn) + if(foundClazz === null) + throw new IllegalStateException('''No generated class '«fqn»' found''') + return foundClazz.newInstance } @Deprecated diff --git a/com.regnosys.rosetta/META-INF/MANIFEST.MF b/com.regnosys.rosetta/META-INF/MANIFEST.MF index 66d665d41..3dcd8aad5 100644 --- a/com.regnosys.rosetta/META-INF/MANIFEST.MF +++ b/com.regnosys.rosetta/META-INF/MANIFEST.MF @@ -32,7 +32,6 @@ Export-Package: com.regnosys.rosetta, com.regnosys.rosetta.generator, com.regnosys.rosetta.generator.external, com.regnosys.rosetta.generator.java, - com.regnosys.rosetta.generator.java.calculation, com.regnosys.rosetta.generator.java.expression, com.regnosys.rosetta.generator.java.function, com.regnosys.rosetta.generator.java.object, diff --git a/com.regnosys.rosetta/model/Rosetta.xcore b/com.regnosys.rosetta/model/Rosetta.xcore index a253a76d1..c49ba2e3e 100644 --- a/com.regnosys.rosetta/model/Rosetta.xcore +++ b/com.regnosys.rosetta/model/Rosetta.xcore @@ -36,7 +36,11 @@ abstract class RosettaTyped { refers RosettaType ^type } -class RosettaFeature extends RosettaNamed, RosettaTyped { +abstract class WithCardinality { + contains RosettaCardinality card +} + +class RosettaFeature extends RosettaNamed, RosettaTyped, WithCardinality { /** * @return The name or 'value' if it's null */ @@ -129,7 +133,6 @@ class RosettaMetaType extends RosettaRootElement, RosettaFeature { abstract class RosettaAttributeBase extends RosettaFeature, RosettaDefinable { contains RosettaRegulatoryReference[] references - contains RosettaCardinality card contains RosettaMarketPractice[] marketPractice } diff --git a/com.regnosys.rosetta/model/RosettaSimple.xcore b/com.regnosys.rosetta/model/RosettaSimple.xcore index 4bc8a4146..316b48fb0 100644 --- a/com.regnosys.rosetta/model/RosettaSimple.xcore +++ b/com.regnosys.rosetta/model/RosettaSimple.xcore @@ -7,18 +7,18 @@ package com.regnosys.rosetta.rosetta.simple import com.regnosys.rosetta.rosetta.RosettaCallable import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs -import com.regnosys.rosetta.rosetta.RosettaCardinality import com.regnosys.rosetta.rosetta.RosettaClassSynonym import com.regnosys.rosetta.rosetta.RosettaDefinable import com.regnosys.rosetta.rosetta.RosettaEnumValueReference import com.regnosys.rosetta.rosetta.RosettaExpression import com.regnosys.rosetta.rosetta.RosettaFeature +import com.regnosys.rosetta.rosetta.RosettaLiteral import com.regnosys.rosetta.rosetta.RosettaNamed import com.regnosys.rosetta.rosetta.RosettaRootElement import com.regnosys.rosetta.rosetta.RosettaSynonym import com.regnosys.rosetta.rosetta.RosettaType -import com.regnosys.rosetta.rosetta.RosettaLiteral - +import org.eclipse.emf.common.util.BasicEList +import com.regnosys.rosetta.rosetta.WithCardinality abstract class RootElement extends RosettaRootElement, RosettaNamed, Definable, Annotated{} @@ -35,13 +35,13 @@ class AnnotationRef { refers Attribute attribute } -abstract class ConvertableCardinality extends RosettaCallable { +abstract class AssignPathRoot extends RosettaCallable, Definable, RosettaNamed { boolean toOne } -class Attribute extends RosettaFeature, Definable, Annotated, RosettaCallable, ConvertableCardinality { + +class Attribute extends RosettaFeature, Definable, Annotated, RosettaCallable, AssignPathRoot, WithCardinality { contains RosettaSynonym[] synonyms - contains RosettaCardinality card } abstract class Definable extends RosettaDefinable {} @@ -89,19 +89,36 @@ enum Necessity { } class Operation extends Definable { container Function function opposite operations - refers Attribute attribute + refers AssignPathRoot assignRoot contains Segment path contains RosettaExpression expression + + op Segment[] pathAsSegmentList() { + return if(path !== null) path.asSegmentList(path) else new BasicEList + } } class Segment { - int index + + Integer index refers RosettaFeature attribute contains Segment next opposite prev container Segment prev opposite next + + op Segment[] asSegmentList(Segment path) { + val result = new BasicEList + if (path !== null) { + result.add(path) + val segmentNext = path?.next + if (segmentNext !== null) { + result.addAll(asSegmentList(segmentNext)) + } + } + return result + } } -class ShortcutDeclaration extends RosettaCallable, RosettaNamed, Definable, ConvertableCardinality { +class ShortcutDeclaration extends RosettaCallable, RosettaNamed, Definable, AssignPathRoot { contains RosettaExpression expression } diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/Rosetta.xtext b/com.regnosys.rosetta/src/com/regnosys/rosetta/Rosetta.xtext index b824d637c..865586fbd 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/Rosetta.xtext +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/Rosetta.xtext @@ -97,7 +97,7 @@ PostCondition returns Condition: ; Operation: - 'assign-output' attribute = [Attribute|ValidID] (path = Segment)? ':' Definable? + 'assign-output' assignRoot = [AssignPathRoot|ValidID] (path = Segment)? ':' Definable? expression = RosettaCalcExpression ; @@ -755,7 +755,7 @@ RosettaCalcAttributeCall returns RosettaExpression: RosettaCalcPrimary returns RosettaExpression: {RosettaCallableWithArgsCall} callable=[RosettaCallableWithArgs|ValidID] '(' (args+=RosettaCalcExpression (',' args+=RosettaCalcExpression)*)? ')' toOne ?= 'only-element'? - | {RosettaCallableCall} callable=[ConvertableCardinality|ValidID] toOne ?= 'only-element' // Hack to allow "only-element" only for attribute and alias callables + | {RosettaCallableCall} callable=[AssignPathRoot|ValidID] toOne ?= 'only-element' // Hack to allow "only-element" only for attribute and alias callables | RosettaCallableCall | RosettaEnumValueReference | RosettaLiteral diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/RosettaExtensions.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/RosettaExtensions.xtend index d465ceb15..9edd690ab 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/RosettaExtensions.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/RosettaExtensions.xtend @@ -18,13 +18,13 @@ import com.regnosys.rosetta.rosetta.RosettaSynonym import com.regnosys.rosetta.rosetta.RosettaType import com.regnosys.rosetta.rosetta.RosettaTyped import com.regnosys.rosetta.rosetta.RosettaWhenPresentExpression +import com.regnosys.rosetta.rosetta.simple.Annotated +import com.regnosys.rosetta.rosetta.simple.Condition +import com.regnosys.rosetta.rosetta.simple.Data import java.util.LinkedHashSet import java.util.Set import org.eclipse.emf.common.util.URI import org.eclipse.emf.ecore.EObject -import com.regnosys.rosetta.rosetta.simple.Data -import com.regnosys.rosetta.rosetta.simple.Condition -import com.regnosys.rosetta.rosetta.simple.Annotated class RosettaExtensions { @@ -247,4 +247,5 @@ class RosettaExtensions { def private allAnnotations(Annotated withAnnotations) { withAnnotations?.annotations?.filter[annotation !== null && !annotation.eIsProxy] } + } diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/RosettaGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/RosettaGenerator.xtend index 5f608a327..b627288df 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/RosettaGenerator.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/RosettaGenerator.xtend @@ -8,9 +8,8 @@ import com.google.inject.Inject import com.regnosys.rosetta.RosettaExtensions import com.regnosys.rosetta.generator.external.ExternalGenerators import com.regnosys.rosetta.generator.java.blueprints.BlueprintGenerator -import com.regnosys.rosetta.generator.java.calculation.CalculationGenerator import com.regnosys.rosetta.generator.java.enums.EnumGenerator -import com.regnosys.rosetta.generator.java.function.FunctionGenerator +import com.regnosys.rosetta.generator.java.function.FuncGenerator import com.regnosys.rosetta.generator.java.object.DataGenerator import com.regnosys.rosetta.generator.java.object.DataValidatorsGenerator import com.regnosys.rosetta.generator.java.object.MetaFieldGenerator @@ -49,12 +48,9 @@ class RosettaGenerator extends AbstractGenerator { @Inject ModelMetaGenerator metaGenerator @Inject ChoiceRuleGenerator choiceRuleGenerator @Inject DataRuleGenerator dataRuleGenerator - @Inject CalculationGenerator calculationGenerator - @Inject FunctionGenerator functionGenerator @Inject BlueprintGenerator blueprintGenerator @Inject QualifyFunctionGenerator qualifyEventsGenerator @Inject QualifyFunctionGenerator qualifyProductsGenerator - //@Inject ClassListGenerator classListGenerator @Inject MetaFieldGenerator metaFieldGenerator @Inject ExternalGenerators externalGenerators @@ -62,8 +58,8 @@ class RosettaGenerator extends AbstractGenerator { @Inject DataValidatorsGenerator validatorsGenerator @Inject extension RosettaFunctionExtensions @Inject extension RosettaExtensions - @Inject JavaNames.Factory factory + @Inject FuncGenerator funcGenerator // For files that are val ignoredFiles = #{'model-no-code-gen.rosetta'} @@ -96,10 +92,8 @@ class RosettaGenerator extends AbstractGenerator { ] } Function:{ - if(handleAsSpecFunction) { - functionGenerator.generate(javaNames, fsa, it, version) - } else if(!isDispatchingFunction){ - calculationGenerator.generateFunction(javaNames, fsa, it, version) + if(!isDispatchingFunction){ + funcGenerator.generate(javaNames, fsa, it, version) } } } @@ -109,8 +103,6 @@ class RosettaGenerator extends AbstractGenerator { choiceRuleGenerator.generate(packages, fsa, elements, version) dataRuleGenerator.generate(packages, fsa, elements, version) metaGenerator.generate(packages, fsa, elements, version) - calculationGenerator.generate(packages, fsa, elements, version) - functionGenerator.generate(packages, fsa, elements, version) blueprintGenerator.generate(packages, fsa, elements, version) qualifyEventsGenerator.generate(packages, fsa, elements, packages.qualifyEvent, RosettaEvent, version) qualifyProductsGenerator.generate(packages, fsa, elements, packages.qualifyProduct, RosettaProduct, version) diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/CalculationGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/CalculationGenerator.xtend deleted file mode 100644 index abc6d85d6..000000000 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/CalculationGenerator.xtend +++ /dev/null @@ -1,728 +0,0 @@ -package com.regnosys.rosetta.generator.java.calculation - -import com.google.inject.ImplementedBy -import com.google.inject.Inject -import com.regnosys.rosetta.RosettaExtensions -import com.regnosys.rosetta.generator.RosettaOutputConfigurationProvider -import com.regnosys.rosetta.generator.java.RosettaJavaPackages -import com.regnosys.rosetta.generator.java.object.ModelObjectBoilerPlate -import com.regnosys.rosetta.generator.java.util.ImportingStringConcatination -import com.regnosys.rosetta.generator.java.util.JavaNames -import com.regnosys.rosetta.generator.java.util.JavaType -import com.regnosys.rosetta.generator.java.util.RosettaGrammarUtil -import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions -import com.regnosys.rosetta.generator.util.Util -import com.regnosys.rosetta.rosetta.RosettaAlias -import com.regnosys.rosetta.rosetta.RosettaArgumentFeature -import com.regnosys.rosetta.rosetta.RosettaArguments -import com.regnosys.rosetta.rosetta.RosettaCalculation -import com.regnosys.rosetta.rosetta.RosettaCalculationFeature -import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs -import com.regnosys.rosetta.rosetta.RosettaCallableWithArgsCall -import com.regnosys.rosetta.rosetta.RosettaEnumeration -import com.regnosys.rosetta.rosetta.RosettaExternalFunction -import com.regnosys.rosetta.rosetta.RosettaFeature -import com.regnosys.rosetta.rosetta.RosettaPackage -import com.regnosys.rosetta.rosetta.RosettaRegularAttribute -import com.regnosys.rosetta.rosetta.RosettaRootElement -import com.regnosys.rosetta.rosetta.simple.Function -import com.regnosys.rosetta.rosetta.simple.Operation -import com.regnosys.rosetta.rosetta.simple.Segment -import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration -import com.regnosys.rosetta.types.RBuiltinType -import com.regnosys.rosetta.types.RRecordType -import com.regnosys.rosetta.types.RUnionType -import com.regnosys.rosetta.types.RosettaTypeProvider -import com.rosetta.model.lib.functions.Formula -import com.rosetta.model.lib.functions.ICalculationInput -import com.rosetta.model.lib.functions.ICalculationResult -import com.rosetta.model.lib.functions.IFunctionResult -import com.rosetta.model.lib.functions.IResult -import com.rosetta.model.lib.functions.IResult.Attribute -import java.util.ArrayList -import java.util.Arrays -import java.util.List -import org.eclipse.xtend.lib.annotations.Data -import org.eclipse.xtend2.lib.StringConcatenationClient -import org.eclipse.xtext.EcoreUtil2 -import org.eclipse.xtext.generator.IFileSystemAccess2 -import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider - -import static com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil.* -import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* - -class CalculationGenerator { - - @Inject JavaNames.Factory factory - @Inject RosettaTypeProvider typeProvider - @Inject extension RosettaExtensions - @Inject extension RosettaFunctionExtensions - @Inject extension RosettaToJavaExtensions - @Inject ModelObjectBoilerPlate boilerPlates - @Inject extension RosettaExternalFunctionDependencyProvider - - @Inject extension ResourceDescriptionsProvider - - def generate(RosettaJavaPackages _packages, IFileSystemAccess2 fsa, List elements, String version) { - val javaNames = factory.create(_packages) - - fsa.generateFunctions(elements, javaNames, version) - fsa.generateCalculation(elements, javaNames, version) - } - - def generateCalculation(IFileSystemAccess2 fsa, List elements, JavaNames javaNames, String version) { - // For the top level args based calcs - val grouped = elements.filter(RosettaArguments).groupBy [ - javaNames.toTargetClassName(it.calculation).firstSegment - ] - grouped.forEach [ String className, List args | - val javaFileContents = generateJava(className, args, javaNames, version) - fsa.generateFile('''«javaNames.packages.calculation.directoryName»/«className».java''', javaFileContents) - ] - - val calculationNames = grouped.values.flatten.map[calculation].map[name].toSet - - // If the WG likes this approach, then the above will be decremented - val groupedCalculations = elements.filter(RosettaCalculation).filter[!calculationNames.contains(name)] - .map[arguments].map[it as RosettaArguments].groupBy [ - javaNames.toTargetClassName(it.calculation).firstSegment - ] - groupedCalculations.forEach[ className, List args | - val javaFileContents = generateJava(className, args as List, javaNames, version) - fsa.generateFile('''«javaNames.packages.calculation.directoryName»/«className».java''', javaFileContents) - ] - } - - def private String generateJava(String className, List args, JavaNames javaNames, String version) { - val concat = new ImportingStringConcatination() - val argsIterable = args as Iterable - concat.append(argsIterable.calculationBody(className, javaNames, version)) - - val javaFileContents = ''' - package «javaNames.packages.calculation.packageName»; - - «FOR imp : concat.imports» - import «imp»; - «ENDFOR» - «FOR imp : concat.staticImports» - import static «imp»; - «ENDFOR» - - «concat.toString» - ''' - javaFileContents - } - - def private calculationBody(Iterable args, String className, extension JavaNames javaNames, String version) { - - val index = args.head?.eResource.resourceDescriptions - - val enum = index.getExportedObjectsByType(RosettaPackage.Literals.ROSETTA_ENUMERATION).filter [ - qualifiedName.lastSegment == className - ].head - - val StringConcatenationClient body = if (enum === null) { - args.head.createCalculationClass(javaNames, className, false) - } else { - val inputType = args.head.commonInputClass - val inputTypeParamName = inputType?.name?.toFirstLower?:'' - val enumTypeName = packages.model.packageName+'.' + args.head.calculation.name.split('\\.').get(0) - - ''' - «emptyJavadocWithVersion(version)» - public class «className» { - «createMembers(javaNames, args)» - «createConstructor(javaNames, className, args)» - public CalculationResult calculate(«toJavaQualifiedType(inputType)» «inputTypeParamName», «enumTypeName» enumValue) { - switch (enumValue) { - «FOR enumVal : args» - «val enumValClass = toTargetClassName(enumVal.calculation).lastSegment» - case «enumValClass»: - return new «enumValClass»(«functionDependencies(enumVal).asArguments.join(', ')»).calculate(«inputTypeParamName»); - «ENDFOR» - default: - throw new IllegalArgumentException("Enum value not implemented: " + enumValue); - } - } - - «FOR enumVal : args» - «val enumValClass = toTargetClassName(enumVal.calculation).lastSegment» - «enumVal.createCalculationClass(javaNames, enumValClass, true)» - «ENDFOR» - «javaNames.createResultClass(args.head.calculation.features, true, true)» - }''' - } - return body - } - - def generateFunctions(IFileSystemAccess2 fsa, List elements, extension JavaNames it, String version) { - elements.filter(RosettaExternalFunction).filter[!isLibrary].forEach [ function | - generateExternalFunction(fsa, function, it, version) - ] - } - def generateFunction(JavaNames javaNames,IFileSystemAccess2 fsa, Function function, String version) { - if(function.handleAsSpecFunction) - generateExternalFunction(fsa, function, javaNames, version) - else - generateCalculationFunction(fsa, function, javaNames, version) - } - - def generateCalculationFunction(IFileSystemAccess2 fsa, Function function, JavaNames names, String version) { - val className = names.toTargetClassName(function).firstSegment - val concat = new ImportingStringConcatination() - if(function.handleAsEnumFunction) { - concat.append(function.enumCalculationFunctionBody(className, names, version)) - } else { - concat.append(function.calculationFunctionBody(className, names, version)) - } - - val javaFileContents = ''' - package «names.packages.calculation.packageName»; - - «FOR imp : concat.imports» - import «imp»; - «ENDFOR» - «FOR imp : concat.staticImports» - import static «imp»; - «ENDFOR» - - «concat.toString» - ''' - fsa.generateFile('''«names.packages.calculation.directoryName»/«className».java''', javaFileContents) - } - - - def private StringConcatenationClient calculationFunctionBody(Function function, String className, extension JavaNames it, String version) { - val enumGeneration = function.isDispatchingFunction - val inputs = getInputs(function) - if(inputs.nullOrEmpty) - return null - - val funcDeps = Util.distinctBy(function.shortcuts.map[functionDependencies()].flatten + function.operations.map[functionDependencies()].flatten,[name]) - val inputArguments = inputs.map[name.toFirstLower].toList + funcDeps.asArguments - - ''' - public «IF enumGeneration»static «ENDIF»class «className» { - «createFunctionMembers(funcDeps)» - «createFunctionConstructor(className, funcDeps)» - public CalculationResult calculate(«FOR input:inputs SEPARATOR ', '»«input.toJavaQualifiedType» «input.name.toFirstLower»«ENDFOR») { - CalculationInput input = new CalculationInput().create(«inputArguments.join(', ')»); -««« // TODO: code generate local variables for fields inside CalculationInput s.t. assignments below can access them as local variables - CalculationResult result = new CalculationResult(input); - «FOR indexed : function.operations.indexed» - «val operation = indexed.value» - «IF operation.path === null» - result.«operation.attribute.name» = «if(operation !== null) assignment(operation) else null»; - «ELSE» - «IF indexed.key == 0» - if(result.«operation.attribute.name» == null) result.«operation.attribute.name» = «operation.attribute.toJavaQualifiedType».builder().build(); - «operation.attribute.toJavaQualifiedType».«operation.attribute.toJavaQualifiedType»Builder __builder = result.«operation.attribute.name».toBuilder(); - «ENDIF» - __builder«FOR seg : operation.path.asSegmentList»«IF seg.next !== null».getOrCreate«seg.attribute.name.toFirstUpper»(«IF seg.attribute.many»«seg.index»«ENDIF»)«ELSE».«IF seg.attribute.isMany»add«ELSE»set«ENDIF»«seg.attribute.name.toFirstUpper»(«assignment(operation)»)«ENDIF»«ENDFOR»; - result.«operation.attribute.name» = __builder.build(); - «ENDIF» - «ENDFOR» - return result; - } - «createInputClass(className, function, funcDeps)» - «IF !enumGeneration» - - «createResultClass(#[getOutput(function)], true, false)» - «ENDIF» - } - ''' - } - - def isMany(RosettaFeature feature) { - switch(feature){ - RosettaRegularAttribute: feature.card.isMany - com.regnosys.rosetta.rosetta.simple.Attribute: feature.card.isMany - default: throw new IllegalStateException('Unsupported type passed '+ feature?.eClass?.name) - } - } - - def List asSegmentList(Segment segment) { - val result = newArrayList - if (segment !== null) { - result.add(segment) - val segmentNext = segment?.next - if(segmentNext !== null) { - result.addAll(asSegmentList(segmentNext)) - } - } - return result - - } - - def private StringConcatenationClient enumCalculationFunctionBody(Function function, String className, extension JavaNames it, String version) { - val dispatchingFuncs = function.dispatchingFunctions.sortBy[name].toList - val enumParam = function.inputs.filter[type instanceof RosettaEnumeration].head.name - val funcDeps = Util.distinctBy(functionDependencies(function),[name]) - ''' - «emptyJavadocWithVersion(version)» - public class «className» { - «createFunctionMembers(funcDeps)» - «createFunctionConstructor(className, funcDeps)» - public CalculationResult calculate(«FOR input:function.inputs SEPARATOR ', '»«input.toJavaQualifiedType» «input.name.toFirstLower»«ENDFOR») { - switch («enumParam») { - «FOR enumVal : dispatchingFuncs» - «val enumValClass = toTargetClassName(enumVal).lastSegment» - case «enumValClass»: - return new «enumValClass»(«functionDependencies(enumVal).asArguments.join(', ')»).calculate(«FOR input:function.inputs SEPARATOR ', '»«input.name.toFirstLower»«ENDFOR»); - «ENDFOR» - default: - throw new IllegalArgumentException("Enum value not implemented: " + «enumParam»); - } - } - - «FOR enumVal : dispatchingFuncs» - «val enumValClass = toTargetClassName(enumVal).lastSegment» - «enumVal.calculationFunctionBody(enumValClass, it, version)» - «ENDFOR» - «createResultClass(#[getOutput(function)], true, true)» - }''' - } - - private dispatch def void generateExternalFunction(IFileSystemAccess2 fsa, Function function, extension JavaNames it, String version) { - - val funcionName = toTargetClassName(function) - val inputs = getInputs(function) - val StringConcatenationClient body = ''' - «emptyJavadocWithVersion(version)» - public interface «funcionName» { - - CalculationResult execute(«FOR param : inputs SEPARATOR ', '»«param.type.toJavaQualifiedType» «param.name»«ENDFOR»); - - «createResultClass(if(getOutput(function) !== null)newArrayList(getOutput(function)) else emptyList, false, false)» - } - ''' - - val concat = new ImportingStringConcatination() - concat.append(body) - - fsa.generateFile('''«packages.functions.directoryName»/«funcionName».java''', ''' - package «packages.functions.packageName»; - - «FOR imp : concat.imports» - import «imp»; - «ENDFOR» - - «concat.toString» - ''') - - val implPath = '''«packages.functions.directoryName»/«funcionName»Impl.java''' - if (!fsa.isFile(implPath, RosettaOutputConfigurationProvider.SRC_MAIN_JAVA_OUTPUT)) { - - val StringConcatenationClient implClazz = ''' - public class «funcionName»Impl implements «funcionName» { - - public «JavaType.create('''«packages.functions.packageName».«funcionName».CalculationResult''')» execute(«FOR param : inputs SEPARATOR ', '»«param.type.toJavaQualifiedType» «param.name»«ENDFOR») { - throw new UnsupportedOperationException("TODO: auto-generated method stub"); - } - } - ''' - val concatImpl = new ImportingStringConcatination() - concatImpl.append(implClazz) - - fsa.generateFile(implPath, RosettaOutputConfigurationProvider.SRC_MAIN_JAVA_OUTPUT, ''' - package «packages.functions.packageName»; - - «FOR imp : concatImpl.imports» - import «imp»; - «ENDFOR» - - «concatImpl.toString» - ''') - } - - } - private dispatch def void generateExternalFunction(IFileSystemAccess2 fsa, RosettaExternalFunction function, extension JavaNames it, String version) { - val funcionName = toTargetClassName(function) - - val StringConcatenationClient body = ''' - «emptyJavadocWithVersion(version)» - @«ImplementedBy»(«funcionName»Impl.class) - public interface «funcionName» { - - CalculationResult execute(«FOR param : function.parameters SEPARATOR ', '»«param.type.toJavaQualifiedType» «param.name»«ENDFOR»); - - «createResultClass(function.features,false, false)» - } - ''' - - val concat = new ImportingStringConcatination() - concat.append(body) - - fsa.generateFile('''«packages.functions.directoryName»/«funcionName».java''', ''' - package «packages.functions.packageName»; - - «FOR imp : concat.imports» - import «imp»; - «ENDFOR» - - «concat.toString» - ''') - - val implPath = '''«packages.functions.directoryName»/«funcionName»Impl.java''' - if (!fsa.isFile(implPath, RosettaOutputConfigurationProvider.SRC_MAIN_JAVA_OUTPUT)) { - - val StringConcatenationClient implClazz = ''' - public class «funcionName»Impl implements «funcionName» { - - public «JavaType.create('''«packages.functions.packageName».«funcionName».CalculationResult''')» execute(«FOR param : function.parameters SEPARATOR ', '»«param.type.toJavaQualifiedType» «param.name»«ENDFOR») { - throw new UnsupportedOperationException("TODO: auto-generated method stub"); - } - } - ''' - val concatImpl = new ImportingStringConcatination() - concatImpl.append(implClazz) - - fsa.generateFile(implPath, RosettaOutputConfigurationProvider.SRC_MAIN_JAVA_OUTPUT, ''' - package «packages.functions.packageName»; - - «FOR imp : concatImpl.imports» - import «imp»; - «ENDFOR» - - «concatImpl.toString» - ''') - } - } - - def private StringConcatenationClient createCalculationClass(RosettaArguments arguments, extension JavaNames it, String className, boolean enumGeneration) { - val calculation = arguments.calculation - val inputType = arguments.commonInputClass - val inputArguments = if(inputType !== null) newArrayList('param' + inputType.name) else newArrayList - inputArguments += functionDependencies(arguments).asArguments - - ''' - public «IF enumGeneration»static «ENDIF»class «className» { - «createMembers(arguments)» - «createConstructor(className, arguments)» - public CalculationResult calculate(«IF inputType !== null»«inputType.toJavaQualifiedType» param«inputType.name»«ENDIF») { - CalculationInput input = new CalculationInput().create(«inputArguments.join(', ')»); -««« // TODO: code generate local variables for fields inside CalculationInput s.t. assignments below can access them as local variables - CalculationResult result = new CalculationResult(input); - «FOR feature : calculation.features.filter(RosettaCalculationFeature)» - result.«feature.getNameOrDefault» = «assignment(feature)»; - «ENDFOR» - return result; - } - - «createInputClass(className, arguments)» - «IF !enumGeneration» - - «createResultClass(arguments.calculation.features, true, false)» - «ENDIF» - } - ''' - } - - def private StringConcatenationClient createConstructor(extension JavaNames it, String className, - Iterable arguments) { - val rosettaFunctions = functionDependencies(arguments) - - if (!rosettaFunctions.empty) { - ''' - public «className»(«rosettaFunctions.asParameters.join(', ')») { - «FOR func : rosettaFunctions» - this.«func.name.toFirstLower» = «func.name.toFirstLower»; - «ENDFOR» - } - - ''' - } - - } - - def private StringConcatenationClient createFunctionConstructor(extension JavaNames it, String className, Iterable rosettaFunctions ) { - if (!rosettaFunctions.empty) { - ''' - public «className»(«rosettaFunctions.asParameters.join(', ')») { - «FOR func : rosettaFunctions» - this.«func.name.toFirstLower» = «func.name.toFirstLower»; - «ENDFOR» - } - - ''' - } - - } - - def private StringConcatenationClient createConstructor(extension JavaNames it, String className, RosettaArguments arguments) { - createConstructor(className, newArrayList(arguments)) - } - - def private asParameters(Iterable functions) { - functions.map[new Parameter(name, name.toFirstLower)] - } - - def private asArguments(Iterable functions) { - functions.map[name.toFirstLower] - } - - @Data - static class Parameter { - String type - String name - - override toString() { - type + ' ' + name - } - } - - dispatch def private StringConcatenationClient createMembers(extension JavaNames it, RosettaArguments arguments) { - createMembers(newArrayList(arguments)) - } - - dispatch def private StringConcatenationClient createMembers(extension JavaNames it, Iterable arguments) { - ''' - «FOR func : functionDependencies(arguments) BEFORE '\n'» - private final «toJavaQualifiedType(func as RosettaCallableWithArgs)» «func.name.toFirstLower»; - «ENDFOR» - - ''' - } - - def private StringConcatenationClient createFunctionMembers(extension JavaNames it, Iterable funcDeps) { - ''' - «FOR func : funcDeps BEFORE '\n'» - private final «toJavaQualifiedType(func)» «func.name.toFirstLower»; - «ENDFOR» - - ''' - } - - def private StringConcatenationClient createInputClass(extension JavaNames it, String calculationName, Function function, Iterable funcDeps) { - val inputs = getInputs(function).map[new Parameter('''«IF it.many»List<«it.type.toJavaType.simpleName»>«ELSE»«it.type.toJavaType.simpleName»«ENDIF»''', name.toFirstLower)] - val functionParameters = inputs + funcDeps.asParameters - - ''' - public static class CalculationInput implements «ICalculationInput» { -««« // hack for code gen to work, when arguments refer to other arguments -««« // We can instead declare all fields of CalculationInput as local variables and switch off the need to generate argument references that prepend 'input.' - private CalculationInput input = this; // For when arguments need to reference other arguments - «IF ! function.shortcuts.map[typeProvider.getRType(expression)].filter(RUnionType).empty» - private final «List»<«ICalculationResult»> calculationResults = new «ArrayList»<>(); - «ENDIF» - «FOR feature : inputs» - private «feature.type» «feature.name»; - «ENDFOR» - «FOR feature : function.shortcuts» - private «shortcutJavaType(feature)» «feature.getName»; - «ENDFOR» - - public CalculationInput create(«FOR parameter:functionParameters SEPARATOR ', '»«parameter»«ENDFOR») { - «FOR feature : inputs» - this.«feature.getName» = «feature.getName»; - «ENDFOR» - «FOR feature : function.shortcuts» - «FOR enumFuncCall : EcoreUtil2.getAllContents(#[feature.expression]).filter(RosettaCallableWithArgsCall).filter[it.callable instanceof Function && (it.callable as Function).handleAsEnumFunction].toList» - «val enumFunc = enumFuncCall.callable as Function» - «enumFunc.toTargetClassName.firstSegment» «enumFunc.name.toFirstLower» = new «enumFunc.toTargetClassName.firstSegment»(«functionDependencies(enumFuncCall).asArguments.join(', ')»); - «ENDFOR» - this.«feature.getName» = «toJava(feature.expression)»; - «ENDFOR» - return this; - } - - @Override - public «List»<«Formula»> getFormulas() { - return «Arrays».asList(«FOR feature : function.operations SEPARATOR ','» - new «Formula»("«calculationName.escape»", "«RosettaGrammarUtil.extractNodeText(feature, OPERATION__EXPRESSION).escape»", this)«ENDFOR»); - } - - «FOR feature : inputs» - public «feature.type» get«feature.name.toFirstUpper»() { - return «feature.name»; - } - - «ENDFOR» - «FOR feature : function.shortcuts» - public «shortcutJavaType(feature)» get«feature.name.toFirstUpper»() { - return «feature.name»; - } - - «ENDFOR» - private static final «List»<«IResult.Attribute»> ATTRIBUTES = «Arrays».asList( - «FOR feature : function.shortcuts SEPARATOR ','» - new «Attribute»<>("«feature.name»", «shortcutJavaType(feature)».class, («IResult» res) -> ((CalculationInput) res).get«feature.name.toFirstUpper»()) - «ENDFOR» - ); - - @Override - public «List»<«Attribute»> getAttributes() { - return ATTRIBUTES; - } - - } - ''' - } - - ///FIXME remove it after complete migration to func - def private StringConcatenationClient shortcutJavaType(JavaNames names, ShortcutDeclaration feature) { - val rType = typeProvider.getRType(feature.expression) - '''«names.toJava(rType)»«IF rType instanceof RRecordType && (rType as RRecordType).record instanceof RosettaExternalFunction».CalculationResult«ENDIF»''' - } - ///FIXME remove it after complete migration to func - def private StringConcatenationClient aliasJavaType(JavaNames names, RosettaAlias feature) { - val rType = typeProvider.getRType(feature.expression) - '''«names.toJava(rType)»«IF rType instanceof RRecordType && (rType as RRecordType).record instanceof RosettaExternalFunction».CalculationResult«ENDIF»''' - } - - def private StringConcatenationClient createInputClass(extension JavaNames it, String calculationName, RosettaArguments arguments) { - val inputType = arguments.commonInputClass - val functionParameters = functionDependencies(arguments).asParameters - - ''' - public static class CalculationInput implements «ICalculationInput» { -««« // hack for code gen to work, when arguments refer to other arguments -««« // We can instead declare all fields of CalculationInput as local variables and switch off the need to generate argument references that prepend 'input.' - private CalculationInput input = this; // For when arguments need to reference other arguments - «IF !arguments.features.filter(RosettaArgumentFeature).map[typeProvider.getRType(expression)].filter(RUnionType).empty» - private final «List»<«ICalculationResult»> calculationResults = new «ArrayList»<>(); - «ENDIF» - «FOR feature : arguments.features.filter(RosettaArgumentFeature)» - private «toJava(typeProvider.getRType(feature.expression))» «feature.getNameOrDefault»; - «ENDFOR» - - public CalculationInput create(«IF inputType !== null»«inputType.toJavaQualifiedType» inputParam«IF functionParameters.size > 0», «ENDIF»«ENDIF»«functionParameters.join(', ')») { - «FOR alias : arguments.aliases» - «aliasJavaType(alias)» «alias.name»Alias = «toJava(alias.expression)»; - «ENDFOR» - «FOR feature : arguments.features.filter(RosettaArgumentFeature)» - «val exprType = typeProvider.getRType(feature.expression)» - «IF (exprType instanceof RUnionType) » - «exprType.converter.toTargetClassName.firstSegment».CalculationResult «toCalculationResultVar(exprType)» = new «exprType.converter.toTargetClassName.firstSegment»(«functionDependencies(feature).asArguments.join(', ')»).calculate(inputParam, «toJava(feature.expression)»); - this.calculationResults.add(«toCalculationResultVar(exprType)»); - this.«feature.getNameOrDefault» = «toCalculationResultVar(exprType)».getValue(); - «ELSE» - this.«feature.getNameOrDefault» = «toJava(feature.expression)»; - «ENDIF» - «ENDFOR» - return this; - } - - @Override - public «List»<«Formula»> getFormulas() { - return «Arrays».asList(«FOR feature : arguments.calculation.features SEPARATOR ','» - new «Formula»("«calculationName.escape»", "«RosettaGrammarUtil.extractGrammarText(feature).escape»", this)«ENDFOR»); - } - - «IF !arguments.features.filter(RosettaArgumentFeature).map[typeProvider.getRType(expression)].filter(RUnionType).empty» - @Override - public «List»<«ICalculationResult»> getCalculationResults() { - return calculationResults; - } - - «ENDIF» - «FOR feature : arguments.features.filter(RosettaArgumentFeature)» - public «toJava(typeProvider.getRType(feature.expression))» get«feature.getNameOrDefault.toFirstUpper»() { - return «feature.getNameOrDefault»; - } - - «ENDFOR» - private static final «List»<«IResult.Attribute»> ATTRIBUTES = «Arrays».asList( - «FOR feature : arguments.features.filter(RosettaArgumentFeature) SEPARATOR ','» - new «Attribute»<>("«feature.getNameOrDefault»", «toJava(typeProvider.getRType(feature.expression))».class, («IResult» res) -> ((CalculationInput) res).get«feature.getNameOrDefault.toFirstUpper»()) - «ENDFOR» - ); - - @Override - public «List»<«Attribute»> getAttributes() { - return ATTRIBUTES; - } - - } - ''' - } - - protected def CharSequence escape(String s) { - s.trim.replace('"', "'").replace('\n',' ').replace("\r", ""); - } - - - protected def CharSequence toCalculationResultVar(extension JavaNames it, RUnionType exprType) { - exprType.converter.toTargetClassName.firstSegment.toFirstLower + "CalculationResult" - } - - - def private StringConcatenationClient createResultClass(extension JavaNames it, List features, boolean isCalculation, boolean enumGeneration) { - - ''' - «IF isCalculation»public static «ENDIF»class CalculationResult implements «IF isCalculation»«ICalculationResult»«ELSE»«IFunctionResult»«ENDIF» { - - «IF isCalculation» - private «calulationInputClass(enumGeneration)» calculationInput; - «ENDIF» - - «FOR feature : features» - «val nameOrDefault = feature.getNameOrDefault» - private «feature.toJavaQualifiedType» «nameOrDefault»; - «ENDFOR» - - public CalculationResult(«IF isCalculation»«calulationInputClass(enumGeneration)» calculationInput«ENDIF») { - «IF isCalculation»this.calculationInput = calculationInput;«ENDIF» - } - «FOR feature : features» - «val nameOrDefault = feature.getNameOrDefault» - public «feature.toJavaQualifiedType» get«nameOrDefault.toFirstUpper»() { - return this.«nameOrDefault»; - } - - public CalculationResult set«nameOrDefault.toFirstUpper»(«feature.toJavaQualifiedType» «nameOrDefault») { - this.«nameOrDefault» = «nameOrDefault»; - return this; - } - - «ENDFOR» - «IF isCalculation» - @Override - public «calulationInputClass(enumGeneration)» getCalculationInput() { - return calculationInput; - } - - «ENDIF» - private static final «List»<«Attribute»> ATTRIBUTES = «Arrays».asList( - «FOR feature : features SEPARATOR ','» - new «Attribute»<>("«feature.getNameOrDefault»", «feature.toJavaQualifiedType».class, («IResult» res) -> ((CalculationResult) res).get«feature.getNameOrDefault.toFirstUpper»()) - «ENDFOR» - ); - - @Override - public «List»<«Attribute»> getAttributes() { - return ATTRIBUTES; - } - - «boilerPlates.calculationResultBoilerPlate("CalculationResult", features)» - } - ''' - } - - - - protected def StringConcatenationClient calulationInputClass(boolean enumGeneration) - '''«IF enumGeneration»«ICalculationInput»«ELSE»CalculationInput«ENDIF»''' - - - dispatch def private StringConcatenationClient assignment(extension JavaNames it, Operation op) { - '''«toJava(op.expression)»''' - } - - dispatch def private StringConcatenationClient assignment(extension JavaNames it, RosettaCalculationFeature feature) { - if (feature.isTypeInferred) { - toJava(feature.expression) - } else { - val expected = typeProvider.getRType(feature.type) - val actual = typeProvider.getRType(feature.expression) - - if (RBuiltinType.NUMBER == expected && RBuiltinType.NUMBER != actual) - '''new «toJavaQualifiedType(feature.type)»(«toJava(feature.expression)»)''' - else if (RBuiltinType.STRING == expected && RBuiltinType.STRING != actual) - '''«toJava(feature.expression)».toString()''' - else { - toJava(feature.expression) - } - } - } -} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaExternalFunctionDependencyProvider.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaExternalFunctionDependencyProvider.xtend deleted file mode 100644 index 1c450e495..000000000 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaExternalFunctionDependencyProvider.xtend +++ /dev/null @@ -1,129 +0,0 @@ -package com.regnosys.rosetta.generator.java.calculation - -import com.google.common.collect.Iterables -import com.google.inject.Inject -import com.regnosys.rosetta.rosetta.RosettaAlias -import com.regnosys.rosetta.rosetta.RosettaArgumentFeature -import com.regnosys.rosetta.rosetta.RosettaArguments -import com.regnosys.rosetta.rosetta.RosettaCalculation -import com.regnosys.rosetta.rosetta.RosettaCallableWithArgsCall -import com.regnosys.rosetta.rosetta.RosettaConditionalExpression -import com.regnosys.rosetta.rosetta.RosettaExistsExpression -import com.regnosys.rosetta.rosetta.RosettaExternalFunction -import com.regnosys.rosetta.rosetta.RosettaFeatureCall -import com.regnosys.rosetta.rosetta.RosettaGroupByFeatureCall -import com.regnosys.rosetta.rosetta.simple.Function -import com.regnosys.rosetta.types.RUnionType -import com.regnosys.rosetta.types.RosettaTypeProvider -import org.eclipse.emf.ecore.EObject -import org.eclipse.emf.ecore.util.EcoreUtil -import org.eclipse.xtext.naming.IQualifiedNameConverter -import org.eclipse.xtext.resource.IEObjectDescription -import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider - -import static com.regnosys.rosetta.generator.util.Util.* -import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* -import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions -import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs -import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration -import com.regnosys.rosetta.rosetta.simple.Operation - -/** - * A class that helps determine which RosettaFunctions a Rosetta object refers to - */ -class RosettaExternalFunctionDependencyProvider { - - @Inject extension RosettaTypeProvider - @Inject extension ResourceDescriptionsProvider - @Inject extension IQualifiedNameConverter - @Inject extension RosettaFunctionExtensions - - def Iterable functionDependencies(EObject object) { - switch object { - RosettaArguments: { - Iterables.concat( - functionDependencies(object.features.filter(RosettaArgumentFeature)), - functionDependencies(object.aliases.filter(RosettaAlias))) - } - RosettaArgumentFeature: { - val rtype = object.expression.getRType - - if (rtype instanceof RUnionType) { - functionDependencies(rtype.converter) - } else { - functionDependencies(object.expression) - } - } - RosettaConditionalExpression: { - Iterables.concat( - functionDependencies(object.^if), - functionDependencies(object.ifthen), - functionDependencies(object.elsethen) - ) - } - RosettaExistsExpression: { - functionDependencies(object.argument) - } - RosettaAlias: { - val rtype = object.expression.getRType - - if (rtype instanceof RUnionType) { - functionDependencies(rtype.converter) - } else { - functionDependencies(object.expression) - } - } - RosettaGroupByFeatureCall: - functionDependencies(object.call) - RosettaFeatureCall: - functionDependencies(object.receiver) - RosettaCallableWithArgsCall: { - Iterables.concat( - functionDependencies(object.callable), - object.args.flatMap[args | functionDependencies(args)] - ) - } - RosettaCalculation: { - val qualifiedName = object.name?.toQualifiedName - - if (qualifiedName !== null && qualifiedName.segmentCount == 2) { - val index = object.eResource.resourceDescriptions - val matchingArgumentFeatures = index.getExportedObjectsByType(ROSETTA_ARGUMENT_FEATURE) - .filter [name.firstSegment == qualifiedName.firstSegment] - val arguments = matchingArgumentFeatures - .map[IEObjectDescription e | EcoreUtil.resolve(e.EObjectOrProxy, object.eResource)] - .map[eContainer].filter(RosettaArguments) - - functionDependencies(arguments).toSet - } else { - newArrayList - } - } - RosettaExternalFunction: { - if(!object.isLibrary) newArrayList(object) else newArrayList - } - Function: { - // TODO change linking to link against main dispatch func only - val me = if(!object.handleAsEnumFunction && !object.dispatchingFunction) newArrayList(object) else newArrayList - if (object.handleAsEnumFunction) { - distinctBy((functionDependencies(object.shortcuts) + - object.dispatchingFunctions.map[functionDependencies].flatten), [name]) - } else { - Iterables.concat(functionDependencies(object.shortcuts), me) - } - } - ShortcutDeclaration: { - functionDependencies(object.expression) - } - Operation: { - functionDependencies(object.expression) - } - default: - newArrayList - } - } - - def Iterable functionDependencies(Iterable objects) { - distinctBy(objects.map[object | functionDependencies(object)].flatten, [f|f.name]); - } -} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumGenerator.xtend index 8a29f24c7..dbbd5a3a5 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumGenerator.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumGenerator.xtend @@ -1,30 +1,24 @@ package com.regnosys.rosetta.generator.java.enums -import com.google.common.base.CaseFormat import com.regnosys.rosetta.generator.java.RosettaJavaPackages import com.regnosys.rosetta.rosetta.RosettaEnumValue import com.regnosys.rosetta.rosetta.RosettaEnumeration import com.regnosys.rosetta.rosetta.RosettaRootElement import java.util.ArrayList -import java.util.Arrays import java.util.List -import java.util.stream.Collectors import org.eclipse.xtext.generator.IFileSystemAccess2 +import static com.regnosys.rosetta.generator.java.enums.EnumHelper.* import static com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil.* class EnumGenerator { - + def generate(RosettaJavaPackages packages, IFileSystemAccess2 fsa, List elements, String version) { elements.filter(RosettaEnumeration).forEach [ fsa.generateFile(packages.model.directoryName + '/' + name + '.java', toJava(packages, version)) ] } - - def static toJavaEnumName(RosettaEnumeration enumeration, RosettaEnumValue rosettaEnumValue) { - return enumeration.name + '.' + convertValues(rosettaEnumValue) - } - + private def allEnumsValues(RosettaEnumeration enumeration) { val enumValues = new ArrayList var e = enumeration; @@ -95,66 +89,14 @@ class EnumGenerator { «ENDFOR» «ENDFOR» ''' - - def static convertValuesWithDisplay(RosettaEnumValue enumValue) { - formatEnumName(enumValue.name) + '''("«enumValue.display»")''' - - } - - def static convertValues(RosettaEnumValue enumValue) { - return formatEnumName(enumValue.name) - } + /** + * Use EnumHelper.formatEnumName(String) instead + */ + @Deprecated def static String formatEnumName(String name) { - if(noFormattingRequired(name)) - return name - - val parts = Arrays.asList(name.replaceSeparatorsWithUnderscores.splitAtNumbers).stream - .map[splitAtUnderscore].flatMap[stream] - .map[splitAtCamelCase].flatMap[stream] - .map[camelCaseToUpperUnderscoreCase] - .map[it.toUpperCase] - .collect(Collectors.toList) - - return String.join("_", parts).prefixWithUnderscoreIfStartsWithNumber.removeDuplicateUnderscores - } - - private def static boolean noFormattingRequired(String name) { - return name.matches("^[A-Z0-9_]*$") - } - - private def static String replaceSeparatorsWithUnderscores(String name) { - return name.replace(".", "_").replace("-", "_").replace(" ", "_") - } - - private def static List splitAtCamelCase(String namePart) { - return Arrays.asList(namePart.split("(? splitAtUnderscore(String namePart) { - return Arrays.asList(namePart.split("_")) - } - - private def static String[] splitAtNumbers(String namePart) { - return namePart.split("(?=[X])(?<=[^X])|(?=[^X])(?<=[X])".replace("X", "\\d")) + EnumHelper.formatEnumName(name); } - private def static String camelCaseToUpperUnderscoreCase(String namePart) { - // if it starts with an upper case and ends with a lower case then assume it's camel case - if(!namePart.empty && Character.isUpperCase(namePart.charAt(0)) && Character.isLowerCase(namePart.charAt(namePart.length()-1))) { - return CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, namePart) - } - return namePart - } - private def static String removeDuplicateUnderscores(String name) { - return name.replace("__", "_") - } - - private def static String prefixWithUnderscoreIfStartsWithNumber(String name) { - if(Character.isDigit(name.charAt(0))) - return "_" + name - else - return name - } } \ No newline at end of file diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumHelper.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumHelper.xtend new file mode 100644 index 000000000..a4640be8d --- /dev/null +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/enums/EnumHelper.xtend @@ -0,0 +1,71 @@ +package com.regnosys.rosetta.generator.java.enums + +import com.google.common.base.CaseFormat +import com.regnosys.rosetta.rosetta.RosettaEnumValue +import java.util.Arrays +import java.util.List +import java.util.stream.Collectors + +class EnumHelper { + + def static convertValuesWithDisplay(RosettaEnumValue enumValue) { + formatEnumName(enumValue.name) + '''("«enumValue.display»")''' + + } + + def static convertValues(RosettaEnumValue enumValue) { + return formatEnumName(enumValue.name) + } + + def static String formatEnumName(String name) { + if (noFormattingRequired(name)) + return name + + val parts = Arrays.asList(name.replaceSeparatorsWithUnderscores.splitAtNumbers).stream.map[splitAtUnderscore]. + flatMap[stream].map[splitAtCamelCase].flatMap[stream].map[camelCaseToUpperUnderscoreCase].map [ + it.toUpperCase + ].collect(Collectors.toList) + + return String.join("_", parts).prefixWithUnderscoreIfStartsWithNumber.removeDuplicateUnderscores + } + + private def static boolean noFormattingRequired(String name) { + return name.matches("^[A-Z0-9_]*$") + } + + private def static String replaceSeparatorsWithUnderscores(String name) { + return name.replace(".", "_").replace("-", "_").replace(" ", "_") + } + + private def static List splitAtCamelCase(String namePart) { + return Arrays.asList(namePart.split("(? splitAtUnderscore(String namePart) { + return Arrays.asList(namePart.split("_")) + } + + private def static String[] splitAtNumbers(String namePart) { + return namePart.split("(?=[X])(?<=[^X])|(?=[^X])(?<=[X])".replace("X", "\\d")) + } + + private def static String camelCaseToUpperUnderscoreCase(String namePart) { + // if it starts with an upper case and ends with a lower case then assume it's camel case + if (!namePart.empty && Character.isUpperCase(namePart.charAt(0)) && + Character.isLowerCase(namePart.charAt(namePart.length() - 1))) { + return CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, namePart) + } + return namePart + } + + private def static String removeDuplicateUnderscores(String name) { + return name.replace("__", "_") + } + + private def static String prefixWithUnderscoreIfStartsWithNumber(String name) { + if (Character.isDigit(name.charAt(0))) + return "_" + name + else + return name + } +} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/Expression.java b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/Expression.java deleted file mode 100644 index 3276ffe2f..000000000 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/Expression.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.regnosys.rosetta.generator.java.expression; - -import java.util.Arrays; -import java.util.MissingFormatArgumentException; -import java.util.function.Function; - -import com.regnosys.rosetta.rosetta.RosettaExpression; - -/* FIXME Unused - remove if not needed*/ -public interface Expression { - - String javaCode(); - - String description(); - - ResultType resultType(); - - - enum ResultType { - LIST, OBJECT, BOOLEAN - } - - interface Handler { - Expression handle(RosettaExpression expression, ExprFunc exprFunc); - } - - interface ExprFunc extends Function { - - } - - static ExprFunc NoFunc = x->x; - - class Decision implements Expression { - private String format; - private Expression[] expressions; - public Decision(ExprFunc exprFunc, String format, Expression... expressions) { - this.format = format; - this.expressions = expressions; - } - - @Override - public String javaCode() { - try { - return (String.format(format, Arrays.stream(expressions).map(x -> x.javaCode()).toArray())); - } catch (MissingFormatArgumentException e) { - throw new MissingFormatArgumentException(e.getMessage() + " on " + format + " with "+expressions.length+" args " + Arrays.toString(expressions)); - } - } - - @Override - public String description() { - return javaCode(); - } - - @Override - public ResultType resultType() { - return ResultType.BOOLEAN; - } - } - - class Decorator implements Expression { - private String format; - private Expression expression; - private ResultType resultType; - private ExprFunc exprFunc; - - public Decorator(ExprFunc exprFunc, String format, Expression expression) { - this.exprFunc = exprFunc; - this.format = format; - this.expression = expression; - this.resultType = expression.resultType(); - } - - public Decorator(ExprFunc exprFunc, String format, Expression expression, ResultType resultType) { - this.exprFunc = exprFunc; - this.format = format; - this.expression = expression; - this.resultType = resultType; - } - - @Override - public String javaCode() { - try { - return exprFunc.apply(String.format(format, expression.javaCode())); - } catch (MissingFormatArgumentException e) { - throw new MissingFormatArgumentException(e.getMessage() + " on " + format + " with 1 arg " + expression); - } - } - - @Override - public String description() { - return javaCode(); - } - - @Override - public ResultType resultType() { - return resultType; - } - } - - - class Call implements Expression { - private String result; - private ResultType resultType; - private ExprFunc exprFunc; - - public Call(ExprFunc exprFunc, String result, ResultType resultType) { - this.exprFunc = exprFunc; - this.result = result; - this.resultType = resultType; - } - - @Override - public String javaCode() { - return exprFunc.apply(result); - } - - @Override - public String description() { - return result; - } - - @Override - public ResultType resultType() { - return resultType; - } - } - -} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorWithBuilder.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorWithBuilder.xtend new file mode 100644 index 000000000..971b0f12a --- /dev/null +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/ExpressionGeneratorWithBuilder.xtend @@ -0,0 +1,232 @@ +package com.regnosys.rosetta.generator.java.expression + +import com.google.common.base.Objects +import com.google.inject.Inject +import com.regnosys.rosetta.generator.java.function.ConvertableCardinalityProvider +import com.regnosys.rosetta.generator.java.util.ImportManagerExtension +import com.regnosys.rosetta.generator.java.util.JavaNames +import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions +import com.regnosys.rosetta.rosetta.RosettaBigDecimalLiteral +import com.regnosys.rosetta.rosetta.RosettaBinaryOperation +import com.regnosys.rosetta.rosetta.RosettaCallableCall +import com.regnosys.rosetta.rosetta.RosettaCallableWithArgsCall +import com.regnosys.rosetta.rosetta.RosettaConditionalExpression +import com.regnosys.rosetta.rosetta.RosettaEnumValueReference +import com.regnosys.rosetta.rosetta.RosettaExistsExpression +import com.regnosys.rosetta.rosetta.RosettaExpression +import com.regnosys.rosetta.rosetta.RosettaExternalFunction +import com.regnosys.rosetta.rosetta.RosettaFeature +import com.regnosys.rosetta.rosetta.RosettaFeatureCall +import com.regnosys.rosetta.rosetta.RosettaGroupByFeatureCall +import com.regnosys.rosetta.rosetta.RosettaLiteral +import com.regnosys.rosetta.rosetta.RosettaNamed +import com.regnosys.rosetta.rosetta.RosettaParenthesisCalcExpression +import com.regnosys.rosetta.rosetta.RosettaRecordType +import com.regnosys.rosetta.rosetta.RosettaRegularAttribute +import com.regnosys.rosetta.rosetta.RosettaType +import com.regnosys.rosetta.rosetta.simple.Attribute +import com.regnosys.rosetta.rosetta.simple.EmptyLiteral +import com.regnosys.rosetta.rosetta.simple.Function +import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration +import com.regnosys.rosetta.types.RBuiltinType +import com.regnosys.rosetta.types.RosettaTypeCompatibility +import com.regnosys.rosetta.types.RosettaTypeProvider +import com.rosetta.model.lib.functions.ExpressionOperators +import com.rosetta.model.lib.math.BigDecimalExtensions +import com.rosetta.model.lib.records.Date +import org.eclipse.xtend.lib.annotations.Accessors +import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor +import org.eclipse.xtend2.lib.StringConcatenationClient + +import static extension com.regnosys.rosetta.generator.java.enums.EnumHelper.convertValues + +class ExpressionGeneratorWithBuilder { + + @Inject extension RosettaTypeCompatibility + @Inject RosettaTypeProvider typeProvider + @Inject ConvertableCardinalityProvider cardinalityProvider + @Inject RosettaFunctionExtensions funcExt + @Inject extension ImportManagerExtension + + dispatch def StringConcatenationClient toJava(RosettaExpression ele, Context ctx) { + throw new UnsupportedOperationException('Not supported expression: ' + ele.eClass.name) + } + + dispatch def StringConcatenationClient toJava(RosettaFeatureCall ele, Context ctx) { + // if the attribute being referenced is WithMeta and we aren't accessing the meta fields then access the value by default + val feature = ele.feature + val StringConcatenationClient right = if (feature instanceof RosettaRegularAttribute) + feature.attributeAccess(ctx) + else if (feature instanceof Attribute) + feature.attributeAccess(ctx) + else + throw new UnsupportedOperationException("Unsupported expression type of " + feature.class.simpleName) + '''«ele.receiver.toJava(ctx)».«right»(«IF cardinalityProvider.isMulti(feature)»0«ENDIF»)''' + } + + def dispatch StringConcatenationClient toJava(Function ele, Context ctx) { + '''«ele.name.toFirstLower»''' + } + + def dispatch StringConcatenationClient toJava(RosettaGroupByFeatureCall ele, Context ctx) { + toJava(ele.call, ctx) + } + + def dispatch StringConcatenationClient toJava(RosettaEnumValueReference ele, Context ctx) { + '''«ctx.names.toJavaQualifiedType(ele.enumeration)».«ele.value.convertValues»''' + } + + def dispatch StringConcatenationClient toJava(RosettaRegularAttribute ele, Context ctx) { + if (ele.metaTypes === null || ele.metaTypes.isEmpty) + '''get«ele.name.toFirstUpper»()''' + else { + '''get«ele.name.toFirstUpper»().getValue()''' + } + } + + def dispatch StringConcatenationClient toJava(RosettaFeature ele, Context ctx) { + '''get«ele.name.toFirstUpper»()''' + } + + def dispatch StringConcatenationClient toJava(RosettaCallableWithArgsCall ele, Context ctx) { + val callable = ele.callable + switch (callable) { + Function: { + val returnVal = funcExt.getOutput(callable) + if (returnVal !== null) { + val toBuilder = if(funcExt.needsBuilder(returnVal)) '.toBuilder()' else '' + val StringConcatenationClient result = + '''«toJava(ele.callable, ctx)».evaluate(«ctx.setInFunctionCall = true»«FOR arg : ele.args SEPARATOR ','»«toJava(arg, ctx)»«ENDFOR»«ctx.setInFunctionCall = false»)«toBuilder»''' + return result + } + } + default: '''«toJava(ele.callable, ctx)».execute(«FOR arg : ele.args SEPARATOR ','»«toJava(arg, ctx)»«ENDFOR»)''' + } + } + + def dispatch StringConcatenationClient toJava(RosettaExternalFunction ele, Context ctx) { + if (ele.isLibrary) { + '''new «ctx.names.toJavaQualifiedType(ele as RosettaType)»()''' + } else { + '''«ele.name.toFirstLower»''' + } + } + + def dispatch StringConcatenationClient toJava(RosettaRecordType ele, Context ctx) { + '''new «ctx.names.toJavaQualifiedType(ele as RosettaType)»()''' + } + + def dispatch StringConcatenationClient toJava(RosettaLiteral ele, Context ctx) { + '''«ele.stringValue»''' + } + + def dispatch StringConcatenationClient toJava(EmptyLiteral ele, Context ctx) { + '''null''' + } + + def dispatch StringConcatenationClient toJava(RosettaBigDecimalLiteral ele, Context ctx) { + toBigDecimal('''«ele.stringValue»''') + } + + def dispatch StringConcatenationClient toJava(RosettaParenthesisCalcExpression ele, Context ctx) { + '''(«toJava(ele.expression, ctx)»)''' + } + + def dispatch StringConcatenationClient toJava(RosettaBinaryOperation ele, Context ctx) { + val leftType = typeProvider.getRType(ele.left) + val rightType = typeProvider.getRType(ele.right) + + val leftIsBD = RBuiltinType.NUMBER.isUseableAs(leftType) + val rightIsBD = RBuiltinType.NUMBER.isUseableAs(rightType) + + val leftStr = if(leftIsBD) toJava(ele.left, ctx) else toBigDecimal(toJava(ele.left, ctx)) + val rightStr = if(rightIsBD) toJava(ele.right, ctx) else toBigDecimal(toJava(ele.right, ctx)) + + if (ele.operator == '/') { + '''«BigDecimalExtensions».divide(«leftStr», «rightStr»)''' + } else if (leftIsBD || rightIsBD) { + switch (ele.operator) { + case "*": '''«BigDecimalExtensions».multiply(«leftStr», «rightStr»)''' + case "+": '''«BigDecimalExtensions».add(«leftStr», «rightStr»)''' + case "-": '''«BigDecimalExtensions».subtract(«leftStr», «rightStr»)''' + case ">": '''«leftStr».compareTo(«rightStr») > 0''' + case ">=": '''«leftStr».compareTo(«rightStr») >= 0''' + case "<": '''«leftStr».compareTo(«rightStr») < 0''' + case "<=": '''«leftStr».compareTo(«rightStr») <= 0''' + case "=": '''«leftStr».compareTo(«rightStr») == 0''' + default: '''«toJava(ele.left, ctx)» «ele.operator» «toJava(ele.right, ctx)»''' + } + } else if (ele.operator == '+' && leftType == RBuiltinType.DATE && rightType == RBuiltinType.TIME) { + '''«Date».of(«toJava(ele.left, ctx)», «toJava(ele.right, ctx)»)''' + } else { + switch (ele.operator) { + case "=": '''«Objects».equals(«toJava(ele.left, ctx)», «toJava(ele.right, ctx)»)''' + case "and": '''(«toJava(ele.left, ctx)») && («toJava(ele.right, ctx)»)''' + case "or": '''(«toJava(ele.left, ctx)») || («toJava(ele.right, ctx)»)''' + default: '''(«toJava(ele.left, ctx)» «ele.operator» «toJava(ele.right, ctx)»)''' + } + + } + } + + dispatch def StringConcatenationClient toJava(RosettaCallableCall expr, Context ctx) { + val callee = expr.callable + switch (callee) { + Attribute: { + if(funcExt.needsBuilder(callee) && !funcExt.isOutput(callee) && !ctx.inFunctionCall) + '''«callee.name»«IF expr.toOne».get(0)«ENDIF».toBuilder()''' + else + '''«callee.name»«IF expr.toOne».get(0)«ENDIF»''' + } + ShortcutDeclaration: { + '''«callee.name»(«funcExt.inputsAsArgs(callee)»)«IF expr.toOne».get(0)«ENDIF».get()«IF funcExt.needsBuilder(callee)».toBuilder()«ENDIF»''' + } + RosettaNamed: { + '''«callee.name»«IF expr.toOne».get(0)«ENDIF»''' + } + default: + throw new UnsupportedOperationException("Unsupported callable type of " + callee.class.simpleName) + } + } + + def dispatch StringConcatenationClient toJava(RosettaConditionalExpression ele, Context ctx) { + '''«toJava(ele.^if, ctx)» ? «toJava(ele.ifthen, ctx)» : «IF ele.elsethen !== null»«toJava(ele.elsethen, ctx)»«ELSE»null«ENDIF»''' + } + + def dispatch StringConcatenationClient toJava(RosettaExistsExpression ele, Context ctx) { + '''«importMethod(ExpressionOperators, 'exists')»(«toJava(ele.argument, ctx)»)''' + } + + private def StringConcatenationClient attributeAccess(RosettaFeature feature, Context ctx) { + '''«IF funcExt.needsBuilder(feature)»getOrCreate«ELSE»get«ENDIF»«feature.name.toFirstUpper»''' + } + +// +// private dispatch def metaClass(RosettaRegularAttribute attribute) { +// if (attribute.metaTypes.exists[m|m.name == "reference"]) +// "ReferenceWithMeta" + attribute.type.name.toJavaType.toFirstUpper +// else +// "FieldWithMeta" + attribute.type.name.toJavaType.toFirstUpper +// } +// +// private dispatch def metaClass(Attribute attribute) { +// if (attribute.annotations.exists[a|a.annotation?.name == "metadata" && a.attribute?.name == "reference"]) +// "ReferenceWithMeta" + attribute.type.name.toJavaType.toFirstUpper +// else +// "FieldWithMeta" + attribute.type.name.toJavaType.toFirstUpper +// } + private def StringConcatenationClient toBigDecimal(StringConcatenationClient sequence) { + '''«BigDecimalExtensions».valueOf(«sequence»)''' + } + +} + +@Accessors +@FinalFieldsConstructor +class Context { + final JavaNames names + boolean inFunctionCall + static def create(JavaNames names) { + new Context(names) + } +} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaExpressionJavaGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaExpressionJavaGenerator.xtend index c7bfec8e6..2bcf477df 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaExpressionJavaGenerator.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaExpressionJavaGenerator.xtend @@ -33,7 +33,7 @@ import com.regnosys.rosetta.types.RosettaTypeProvider import java.util.HashMap import org.eclipse.xtend.lib.annotations.Data -import static extension com.regnosys.rosetta.generator.java.enums.EnumGenerator.convertValues +import static extension com.regnosys.rosetta.generator.java.enums.EnumHelper.convertValues import static extension com.regnosys.rosetta.generator.java.util.JavaClassTranslator.toJavaType import static extension com.regnosys.rosetta.generator.util.RosettaAttributeExtensions.cardinalityIsListValue @@ -436,7 +436,7 @@ class RosettaExpressionJavaGenerator { else "FieldWithMeta"+attribute.type.name.toJavaType.toFirstUpper } - def static buildMapFunc(RosettaMetaType meta, boolean isLast) { + def buildMapFunc(RosettaMetaType meta, boolean isLast) { if (meta.name=="reference") { '''.map("get«meta.name.toFirstUpper»", a->a.getGlobalReference())''' } diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/RosettaExpressionJavaGeneratorForFunctions.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaExpressionJavaGeneratorForFunctions.xtend similarity index 76% rename from com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/RosettaExpressionJavaGeneratorForFunctions.xtend rename to com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaExpressionJavaGeneratorForFunctions.xtend index 35637653a..c32970676 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/RosettaExpressionJavaGeneratorForFunctions.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaExpressionJavaGeneratorForFunctions.xtend @@ -1,7 +1,10 @@ -package com.regnosys.rosetta.generator.java.function +package com.regnosys.rosetta.generator.java.expression import com.google.inject.Inject import com.regnosys.rosetta.RosettaExtensions +import com.regnosys.rosetta.generator.java.RosettaJavaPackages +import com.regnosys.rosetta.generator.java.function.ConvertableCardinalityProvider +import com.regnosys.rosetta.generator.java.util.ImportManagerExtension import com.regnosys.rosetta.generator.java.util.JavaNames import com.regnosys.rosetta.generator.java.util.JavaType import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions @@ -20,8 +23,9 @@ import com.regnosys.rosetta.rosetta.RosettaCountOperation import com.regnosys.rosetta.rosetta.RosettaEnumValueReference import com.regnosys.rosetta.rosetta.RosettaExistsExpression import com.regnosys.rosetta.rosetta.RosettaExpression +import com.regnosys.rosetta.rosetta.RosettaExternalFunction +import com.regnosys.rosetta.rosetta.RosettaFeature import com.regnosys.rosetta.rosetta.RosettaFeatureCall -import com.regnosys.rosetta.rosetta.RosettaFunction import com.regnosys.rosetta.rosetta.RosettaFunctionInput import com.regnosys.rosetta.rosetta.RosettaGroupByExpression import com.regnosys.rosetta.rosetta.RosettaGroupByFeatureCall @@ -29,39 +33,43 @@ import com.regnosys.rosetta.rosetta.RosettaIntLiteral import com.regnosys.rosetta.rosetta.RosettaLiteral import com.regnosys.rosetta.rosetta.RosettaMetaType import com.regnosys.rosetta.rosetta.RosettaModel -import com.regnosys.rosetta.rosetta.RosettaNamed import com.regnosys.rosetta.rosetta.RosettaParenthesisCalcExpression import com.regnosys.rosetta.rosetta.RosettaRegularAttribute import com.regnosys.rosetta.rosetta.RosettaStringLiteral import com.regnosys.rosetta.rosetta.RosettaType import com.regnosys.rosetta.rosetta.RosettaWhenPresentExpression import com.regnosys.rosetta.rosetta.simple.Attribute +import com.regnosys.rosetta.rosetta.simple.EmptyLiteral import com.regnosys.rosetta.rosetta.simple.Function +import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration +import com.regnosys.rosetta.types.RosettaOperators import com.regnosys.rosetta.types.RosettaTypeProvider +import com.regnosys.rosetta.utils.ExpressionHelper import com.rosetta.model.lib.functions.MapperMaths import com.rosetta.model.lib.functions.MapperS import com.rosetta.model.lib.functions.MapperTree +import com.rosetta.model.lib.meta.FieldWithMeta +import com.rosetta.model.lib.validation.ValidatorHelper import java.math.BigDecimal import java.util.HashMap import org.eclipse.xtend.lib.annotations.Data import org.eclipse.xtend2.lib.StringConcatenationClient import org.eclipse.xtext.EcoreUtil2 -import static extension com.regnosys.rosetta.generator.java.enums.EnumGenerator.convertValues +import static extension com.regnosys.rosetta.generator.java.enums.EnumHelper.convertValues import static extension com.regnosys.rosetta.generator.java.util.JavaClassTranslator.toJavaType import static extension com.regnosys.rosetta.generator.util.RosettaAttributeExtensions.cardinalityIsListValue -import com.rosetta.model.lib.validation.ComparisonResult -import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration -import com.regnosys.rosetta.rosetta.simple.EmptyLiteral -import com.regnosys.rosetta.generator.java.function.RosettaExpressionJavaGeneratorForFunctions.ParamMap class RosettaExpressionJavaGeneratorForFunctions { @Inject RosettaTypeProvider typeProvider + @Inject RosettaOperators operators @Inject ConvertableCardinalityProvider cardinalityProvider @Inject JavaNames.Factory factory - @Inject RosettaFunctionExtensions func + @Inject RosettaFunctionExtensions funcExt @Inject extension RosettaExtensions + @Inject extension ImportManagerExtension + @Inject ExpressionHelper exprHelper def StringConcatenationClient javaCode(RosettaExpression expr, ParamMap params) { expr.javaCode(params, true); @@ -114,13 +122,13 @@ class RosettaExpressionJavaGeneratorForFunctions { '''«MapperS».of("«expr.value»")''' } RosettaEnumValueReference : { - '''«MapperS».of(«expr.enumeration.name».«expr.value.convertValues»)''' + '''«MapperS».of(«expr.enumeration.toJavaType».«expr.value.convertValues»)''' } RosettaConditionalExpression : { - '''doIf(«expr.^if.javaCode(params)»,«expr.ifthen.javaCode(params)»,«IF expr.elsethen !== null»«expr.elsethen.javaCode(params)»«ELSE»«ComparisonResult».success()«ENDIF»)''' + '''«importMethod(ValidatorHelper,"doIf")»(«expr.^if.javaCode(params)»,«expr.ifthen.javaCode(params)»«IF expr.elsethen !== null»,«expr.elsethen.javaCode(params)»«ENDIF»)''' } RosettaContainsExpression : { - '''contains(«expr.container.javaCode(params)», «expr.contained.javaCode(params)»)''' + '''«importMethod(ValidatorHelper,"contains")»(«expr.container.javaCode(params)», «expr.contained.javaCode(params)»)''' } RosettaParenthesisCalcExpression : { expr.expression.javaCode(params, isLast) @@ -136,22 +144,21 @@ class RosettaExpressionJavaGeneratorForFunctions { def StringConcatenationClient callableWithArgs(RosettaCallableWithArgsCall expr, ParamMap params) { val callable = expr.callable - val many = switch (callable) { - Function: - func.getOutput(callable).card.isMany - RosettaFunction: - callable.output.card.isMany - default: - null - } - if (many !== null) - if (!many) { - val evaluate = if(callable instanceof Function && !(callable as Function).operations.nullOrEmpty) 'calculate' else 'evaluate' - '''«MapperS».of(«callable.name.toFirstLower».«evaluate»(«FOR arg : expr.args SEPARATOR ', '»«arg.javaCode(params)»«IF !(arg instanceof EmptyLiteral)»«IF cardinalityProvider.isMulti(arg)».getMulti()«ELSE».get()«ENDIF»«ENDIF»«ENDFOR»))''' - } else { - throw new IllegalArgumentException( - 'Calling Functions with multiple cardinality return types not yet supported') + return switch (callable) { + Function: { + funcExt.getOutput(callable).card.isMany + '''«MapperS».of(«callable.name.toFirstLower».evaluate(«args(expr, params)»))''' } + RosettaExternalFunction case callable.isLibrary: + '''«MapperS».of(new «new RosettaJavaPackages(null).libFunctions.javaType(callable.name)»().execute(«args(expr, params)»))''' + default: + throw new UnsupportedOperationException("Unsupported callable with args type of " + expr.eClass.name) + } + + } + + private def StringConcatenationClient args(RosettaCallableWithArgsCall expr, ParamMap params) { + '''«FOR arg : expr.args SEPARATOR ', '»«arg.javaCode(params)»«IF !(arg instanceof EmptyLiteral)»«IF cardinalityProvider.isMulti(arg)».getMulti()«ELSE».get()«ENDIF»«ENDIF»«ENDFOR»''' } def StringConcatenationClient existsExpr(RosettaExistsExpression exists, RosettaExpression argument, ParamMap params) { @@ -171,11 +178,11 @@ class RosettaExpressionJavaGeneratorForFunctions { private def StringConcatenationClient doExistsExpr(RosettaExistsExpression exists, StringConcatenationClient arg) { if(exists.single) - '''singleExists(«arg», «exists.only»)''' + '''«importMethod(ValidatorHelper,"singleExists")»(«arg», «exists.only»)''' else if(exists.multiple) - '''multipleExists(«arg», «exists.only»)''' + '''«importMethod(ValidatorHelper,"multipleExists")»(«arg», «exists.only»)''' else - '''exists(«arg», «exists.only»)''' + '''«importMethod(ValidatorHelper,"exists")»(«arg», «exists.only»)''' } def StringConcatenationClient absentExpr(RosettaAbsentExpression notSet, RosettaExpression argument, ParamMap params) { @@ -183,13 +190,13 @@ class RosettaExpressionJavaGeneratorForFunctions { if (arg instanceof RosettaBinaryOperation) { if(arg.operator.equals("or") || arg.operator.equals("and")) - '''notExists(«arg.binaryExpr(notSet, params)»)''' + '''«importMethod(ValidatorHelper,"notExists")»(«arg.binaryExpr(notSet, params)»)''' else //if the arg is binary then the operator needs to be pushed down arg.binaryExpr(notSet, params) } else { - '''notExists(«arg.javaCode(params)»)''' + '''«importMethod(ValidatorHelper,"notExists")»(«arg.javaCode(params)»)''' } } @@ -215,25 +222,38 @@ class RosettaExpressionJavaGeneratorForFunctions { '''«MapperS».of(«call.name»)''' } ShortcutDeclaration : { - '''«MapperS».of(«call.name»)''' + '''«MapperS».of(«call.name»(«aliasCallArgs(call)»).«IF exprHelper.usesOutputParameter(call.expression)»build()«ELSE»get()«ENDIF»)''' } default: throw new UnsupportedOperationException("Unsupported callable type of "+call.class.simpleName) } } + def aliasCallArgs(ShortcutDeclaration alias) { + val func = EcoreUtil2.getContainerOfType(alias, Function) + val attrs = newArrayList + attrs.addAll(funcExt.getInputs(func).map[name].toList) + if(exprHelper.usesOutputParameter(alias.expression)) { + attrs.add(0, funcExt.getOutput(func)?.name + '.toBuilder()') + } + attrs.join(', ') + } + /** * feature call is a call to get an attribute of an object e.g. Quote->amount */ def StringConcatenationClient featureCall(RosettaFeatureCall call, ParamMap params, boolean isLast, boolean autoValue) { val feature = call.feature - val StringConcatenationClient right = - if (feature instanceof RosettaRegularAttribute) + val StringConcatenationClient right = switch (feature) { + RosettaRegularAttribute: feature.buildMapFunc(isLast, autoValue) - else if (feature instanceof Attribute) + Attribute: feature.buildMapFunc(isLast, autoValue) - else - throw new UnsupportedOperationException("Unsupported expression type of "+feature.class.simpleName) + RosettaFeature: + '''.map("get«feature.name.toFirstUpper»", «EcoreUtil2.getContainerOfType(feature, RosettaType).toJavaType»::get«feature.name.toFirstUpper»)''' + default: + throw new UnsupportedOperationException("Unsupported expression type of " + feature.eClass.name) + } '''«javaCode(call.receiver, params, false)»«right»''' } @@ -277,14 +297,20 @@ class RosettaExpressionJavaGeneratorForFunctions { } } case ("+"): { - val leftType = '''«typeProvider.getRType(expr.left).name.toJavaType»''' - val rightType = '''«typeProvider.getRType(expr.right).name.toJavaType»''' - '''«MapperMaths».<«BigDecimal», «leftType», «rightType»>add(«expr.left.javaCode(params)», «expr.right.javaCode(params)»)''' + val leftRtype = typeProvider.getRType(expr.left) + val rightRtype = typeProvider.getRType(expr.right) + val commontype = operators.resultType(expr.operator, leftRtype,rightRtype) + val leftType = '''«leftRtype.name.toJavaType»''' + val rightType = '''«rightRtype.name.toJavaType»''' + '''«MapperMaths».<«commontype.name.toJavaType», «leftType», «rightType»>add(«expr.left.javaCode(params)», «expr.right.javaCode(params)»)''' } case ("-"): { + val leftRtype = typeProvider.getRType(expr.left) + val rightRtype = typeProvider.getRType(expr.right) + val commontype = operators.resultType(expr.operator, leftRtype,rightRtype) val leftType = '''«typeProvider.getRType(expr.left).name.toJavaType»''' val rightType = '''«typeProvider.getRType(expr.right).name.toJavaType»''' - '''«MapperMaths».<«BigDecimal», «leftType», «rightType»>subtract(«expr.left.javaCode(params)», «expr.right.javaCode(params)»)''' + '''«MapperMaths».<«commontype.name.toJavaType», «leftType», «rightType»>subtract(«expr.left.javaCode(params)», «expr.right.javaCode(params)»)''' } default: { toComparisonOp('''«expr.left.javaCode(params)»''', expr.operator, '''«expr.right.javaCode(params)»''') @@ -336,19 +362,19 @@ class RosettaExpressionJavaGeneratorForFunctions { switch operator { case ("="): - '''areEqual(«left», «right»)''' + '''«importMethod(ValidatorHelper, "areEqual")»(«left», «right»)''' case ("<>"): - '''notEqual(«left», «right»)''' + '''«importMethod(ValidatorHelper,"notEqual")»(«left», «right»)''' case ("<") : - '''lessThan(«left», «right»)''' + '''«importMethod(ValidatorHelper,"lessThan")»(«left», «right»)''' case ("<=") : - '''lessThanEquals(«left», «right»)''' + '''«importMethod(ValidatorHelper,"lessThanEquals")»(«left», «right»)''' case (">") : - '''greaterThan(«left», «right»)''' + '''«importMethod(ValidatorHelper,"greaterThan")»(«left», «right»)''' case (">=") : - '''greaterThanEquals(«left», «right»)''' + '''«importMethod(ValidatorHelper,"greaterThanEquals")»(«left», «right»)''' default: - throw new UnsupportedOperationException("Unsupported binary operation of "+operator) + throw new UnsupportedOperationException("Unsupported binary operation of " + operator) } } @@ -386,12 +412,12 @@ class RosettaExpressionJavaGeneratorForFunctions { val mapFunc = attribute.buildMapFuncAttribute if (attribute.cardinalityIsListValue) { if (attribute.metaTypes===null || attribute.metaTypes.isEmpty) - '''.<«attribute.type.name.toJavaType»>mapC(«mapFunc»)''' + '''.<«attribute.type.toJavaType»>mapC(«mapFunc»)''' else if (!autoValue) { '''.<«attribute.metaClass»>mapC(«mapFunc»)''' } else { - '''.mapC(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", FieldWithMeta::getValue)''' + '''.mapC(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", «FieldWithMeta»::getValue)''' } } else @@ -406,7 +432,7 @@ class RosettaExpressionJavaGeneratorForFunctions { '''.<«attribute.metaClass»>map(«mapFunc»)''' } else - '''.map(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", FieldWithMeta::getValue)''' + '''.map(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", «FieldWithMeta»::getValue)''' } } /** @@ -416,12 +442,12 @@ class RosettaExpressionJavaGeneratorForFunctions { val mapFunc = attribute.buildMapFuncAttribute if (attribute.card.isIsMany) { if (attribute.metaAnnotations.nullOrEmpty) - '''.<«attribute.type.name.toJavaType»>mapC(«mapFunc»)''' + '''.<«attribute.type.toJavaType»>mapC(«mapFunc»)''' else if (!autoValue) { '''.<«attribute.metaClass»>mapC(«mapFunc»)''' } else { - '''.mapC(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", FieldWithMeta::getValue)''' + '''.mapC(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", «FieldWithMeta»::getValue)''' } } else @@ -436,17 +462,24 @@ class RosettaExpressionJavaGeneratorForFunctions { '''.<«attribute.metaClass»>map(«mapFunc»)''' } else - '''.map(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", FieldWithMeta::getValue)''' + '''.map(«mapFunc»).<«attribute.type.toJavaType»>map("getValue", «FieldWithMeta»::getValue)''' } } - def JavaType toJavaType(RosettaType rosType) { + private def JavaType toJavaType(RosettaType rosType) { val model = EcoreUtil2.getContainerOfType(rosType, RosettaModel) if (model !== null) factory.create(model).toJavaType(rosType) else JavaType.create(rosType.name.toJavaType) } + private def JavaType toJavaType(com.regnosys.rosetta.rosetta.simple.Data ele) { + val model = EcoreUtil2.getContainerOfType(ele, RosettaModel) + if (model !== null) + factory.create(model).toJavaType(ele) + else + JavaType.create(ele.name.toJavaType) + } def static metaClass(RosettaRegularAttribute attribute) { if (attribute.metaTypes.exists[m|m.name=="reference"]) "ReferenceWithMeta"+attribute.type.name.toJavaType.toFirstUpper @@ -477,11 +510,13 @@ class RosettaExpressionJavaGeneratorForFunctions { '''.<«expr.attribute.type.name.toJavaType»>groupBy(g->new «MapperS»<>(g)«FOR ex:exprs»«buildMapFunc(ex.attribute, isLast, true)»«ENDFOR»)''' } - private def static String buildMapFuncAttribute(RosettaRegularAttribute attribute) - '''"get«attribute.name.toFirstUpper»", «(attribute.eContainer as RosettaClass).name»::get«attribute.name.toFirstUpper»''' + private def StringConcatenationClient buildMapFuncAttribute(RosettaRegularAttribute attribute) + '''"get«attribute.name.toFirstUpper»", «(attribute.eContainer as RosettaClass).toJavaType»::get«attribute.name.toFirstUpper»''' - private def static String buildMapFuncAttribute(Attribute attribute) - '''"get«attribute.name.toFirstUpper»", «(attribute.eContainer as RosettaNamed).name»::get«attribute.name.toFirstUpper»''' + private def StringConcatenationClient buildMapFuncAttribute(Attribute attribute) { + if(attribute.eContainer instanceof com.regnosys.rosetta.rosetta.simple.Data) + '''"get«attribute.name.toFirstUpper»", «(attribute.eContainer as com.regnosys.rosetta.rosetta.simple.Data).toJavaType»::get«attribute.name.toFirstUpper»''' + } /** * The id for a parameter - either a Class name or a positional index @@ -559,7 +594,7 @@ class RosettaExpressionJavaGeneratorForFunctions { val receiver = call.receiver val left = switch receiver { - RosettaCallableCall: ''''''//(receiver.callable as RosettaClass).name + RosettaCallableCall: '''''' //(receiver.callable as RosettaClass).name RosettaFeatureCall: toNodeLabel(receiver) default: throw new UnsupportedOperationException("Unsupported expression type") } @@ -585,4 +620,4 @@ class RosettaExpressionJavaGeneratorForFunctions { private def isMapperTree(StringConcatenationClient code) { return code.toString.startsWith('MapperTree') } -} \ No newline at end of file +} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaToJavaExtensions.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaToJavaExtensions.xtend similarity index 95% rename from com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaToJavaExtensions.xtend rename to com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaToJavaExtensions.xtend index 5c3e50b25..4c24d56b6 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaToJavaExtensions.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/expression/RosettaToJavaExtensions.xtend @@ -1,4 +1,4 @@ -package com.regnosys.rosetta.generator.java.calculation +package com.regnosys.rosetta.generator.java.expression import com.google.inject.Inject import com.regnosys.rosetta.generator.java.util.JavaNames @@ -43,7 +43,8 @@ import java.math.BigDecimal import java.util.Objects import org.eclipse.xtend2.lib.StringConcatenationClient -import static extension com.regnosys.rosetta.generator.java.enums.EnumGenerator.convertValues +import static extension com.regnosys.rosetta.generator.java.enums.EnumHelper.convertValues +import com.regnosys.rosetta.rosetta.simple.EmptyLiteral class RosettaToJavaExtensions { @Inject RosettaTypeProvider typeProvider @@ -104,8 +105,6 @@ class RosettaToJavaExtensions { if (returnVal !== null) { if(callable.handleAsSpecFunction) { return '''«toJava(ele.callable)».evaluate(«FOR arg : ele.args SEPARATOR ','»«toJava(arg)»«ENDFOR»)''' - } else if(!callable.operations.nullOrEmpty || callable.handleAsEnumFunction) { - return '''«toJava(ele.callable)».calculate(«FOR arg : ele.args SEPARATOR ','»«toJava(arg)»«ENDFOR»).get«returnVal.name.toFirstUpper»()''' } else { return '''«toJava(ele.callable)».execute(«FOR arg : ele.args SEPARATOR ','»«toJava(arg)»«ENDFOR»).get«returnVal.name.toFirstUpper»()''' } @@ -130,6 +129,9 @@ class RosettaToJavaExtensions { def dispatch StringConcatenationClient toJava(extension JavaNames it, RosettaLiteral ele) { '''«ele.stringValue»''' } + def dispatch StringConcatenationClient toJava(extension JavaNames it, EmptyLiteral ele) { + '''null''' + } def dispatch StringConcatenationClient toJava(extension JavaNames it, RosettaBigDecimalLiteral ele) { toBigDecimal('''«ele.stringValue»''') @@ -222,7 +224,7 @@ class RosettaToJavaExtensions { def dispatch StringConcatenationClient toJava(extension JavaNames names, RosettaCallableCall ele) { val callable = ele.callable switch (callable) { - RosettaArgumentFeature, ShortcutDeclaration: '''input.«callable.name»''' + RosettaArgumentFeature, ShortcutDeclaration: '''«callable.name»''' RosettaAlias: '''«callable.name»Alias''' RosettaClass: '''inputParam''' default: '''«callable.name»''' diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/CardinalityProvider.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/CardinalityProvider.xtend index 7fa1b60cd..f164c0beb 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/CardinalityProvider.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/CardinalityProvider.xtend @@ -21,7 +21,7 @@ class CardinalityProvider { RosettaFunction: obj.output.card.isIsMany Function: if(obj.output === null) false else obj.output.isMulti Attribute: obj.card.isMany - default: {println(obj.eClass) false} + default: false } } } \ No newline at end of file diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FuncGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FuncGenerator.xtend new file mode 100644 index 000000000..517f6dd20 --- /dev/null +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FuncGenerator.xtend @@ -0,0 +1,300 @@ +package com.regnosys.rosetta.generator.java.function + +import com.google.inject.ImplementedBy +import com.google.inject.Inject +import com.regnosys.rosetta.RosettaExtensions +import com.regnosys.rosetta.generator.java.expression.Context +import com.regnosys.rosetta.generator.java.expression.ExpressionGeneratorWithBuilder +import com.regnosys.rosetta.generator.java.expression.RosettaExpressionJavaGeneratorForFunctions +import com.regnosys.rosetta.generator.java.expression.RosettaExpressionJavaGeneratorForFunctions.ParamMap +import com.regnosys.rosetta.generator.java.util.ImportManagerExtension +import com.regnosys.rosetta.generator.java.util.JavaNames +import com.regnosys.rosetta.generator.java.util.JavaType +import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions +import com.regnosys.rosetta.generator.util.Util +import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs +import com.regnosys.rosetta.rosetta.RosettaEnumeration +import com.regnosys.rosetta.rosetta.RosettaFeature +import com.regnosys.rosetta.rosetta.RosettaNamed +import com.regnosys.rosetta.rosetta.RosettaRegularAttribute +import com.regnosys.rosetta.rosetta.simple.Annotated +import com.regnosys.rosetta.rosetta.simple.AssignPathRoot +import com.regnosys.rosetta.rosetta.simple.Attribute +import com.regnosys.rosetta.rosetta.simple.Condition +import com.regnosys.rosetta.rosetta.simple.Function +import com.regnosys.rosetta.rosetta.simple.Operation +import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration +import com.regnosys.rosetta.types.RosettaTypeProvider +import com.regnosys.rosetta.utils.ExpressionHelper +import com.rosetta.model.lib.functions.Mapper +import com.rosetta.model.lib.functions.MapperBuilder +import com.rosetta.model.lib.functions.MapperS +import com.rosetta.model.lib.functions.RosettaFunction +import java.util.Map +import org.eclipse.xtend2.lib.StringConcatenationClient +import org.eclipse.xtext.generator.IFileSystemAccess2 + +import static com.regnosys.rosetta.generator.java.util.ModelGeneratorUtil.* + +class FuncGenerator { + + @Inject RosettaExpressionJavaGeneratorForFunctions expressionGenerator + @Inject ExpressionGeneratorWithBuilder expressionWithBuilder + @Inject RosettaFunctionDependencyProvider functionDependencyProvider + @Inject RosettaTypeProvider typeProvider + @Inject extension RosettaFunctionExtensions + @Inject extension RosettaExtensions + @Inject ExpressionHelper exprHelper + @Inject extension ImportManagerExtension + + def void generate(JavaNames javaNames, IFileSystemAccess2 fsa, Function func, String version) { + val fileName = javaNames.packages.functions.directoryName + '/' + func.name + '.java' + + + val dependencies = collectFunctionDependencies(func) + + val classBody = if (func.handleAsEnumFunction) { + tracImports(func.dispatchClassBody(func.name, dependencies, javaNames, version)) + } else { + tracImports(func.classBody(func.name, dependencies, javaNames, version, false)) + } + val content = ''' + package «javaNames.packages.functions.packageName»; + + «FOR imp : classBody.imports» + import «imp»; + «ENDFOR» + + «FOR imp : classBody.staticImports» + import static «imp»; + «ENDFOR» + + «classBody.toString» + ''' + fsa.generateFile(fileName, content) + } + + private def collectFunctionDependencies(Function func) { + val deps = func.shortcuts.flatMap[functionDependencyProvider.functionDependencies(it.expression)] + + func.operations.flatMap[functionDependencyProvider.functionDependencies(it.expression)] + val condDeps = (func.conditions + func.postConditions).flatMap[expressions].flatMap [ + functionDependencyProvider.functionDependencies(it) + ] + return Util.distinctBy(deps + condDeps, [name]).sortBy[it.name] + } + + private def StringConcatenationClient classBody(Function func, String className, + Iterable dependencies, extension JavaNames names, String version, boolean isStatic) { + val isAbstract = func.operations.nullOrEmpty + val outputName = getOutput(func)?.name + val outputType = func.outputTypeOrVoid(names) + val aliasOut = func.shortcuts.toMap([it], [exprHelper.usesOutputParameter(it.expression)]) + val outNeedsBuilder = needsBuilder(getOutput(func)) + ''' + «IF isAbstract»@«ImplementedBy»(«className»Impl.class)«ENDIF» + public «IF isStatic»static «ENDIF»«IF isAbstract»abstract «ENDIF»class «className» implements «RosettaFunction» { + «IF !dependencies.empty» + + // RosettaFunction dependencies + // + «ENDIF» + «FOR dep : dependencies» + @«Inject» protected «dep.toJavaQualifiedType» «dep.name.toFirstLower»; + «ENDFOR» + + /** + «FOR input : getInputs(func)» + * @param «input.name» «input.definition» + «ENDFOR» + «IF getOutput(func) !== null» + * @return «outputName» «getOutput(func).definition» + «ENDIF» + */ + public «outputType» evaluate(«func.inputsAsParameters(names)») { + «IF !func.conditions.empty» + // pre-conditions + «FOR cond:func.conditions» + + «cond.contributeCondition» + «ENDFOR» + «ENDIF» + + «outputType» «outputName» = doEvaluate(«func.inputsAsArguments(names)»)«IF outNeedsBuilder».build()«ENDIF»; + + «IF !func.postConditions.empty» + // post-conditions + «FOR cond:func.postConditions» + + «cond.contributeCondition» + «ENDFOR» + «ENDIF» + return «outputName»; + } + + «IF isAbstract» + protected abstract «getOutput(func).toBuilderType(names)» doEvaluate(«func.inputsAsParameters(names)»); + «ELSE» + protected «getOutput(func).toBuilderType(names)» doEvaluate(«func.inputsAsParameters(names)») { + «IF getOutput(func) !== null» + «getOutput(func).toHolderType(names)» «outputName»Holder = «IF outNeedsBuilder»«getOutput(func).toJavaQualifiedType».builder()«ELSE»null«ENDIF»; + «ENDIF» + «FOR indexed : func.operations.indexed» + «IF outNeedsBuilder»«IF indexed.key == 0»@«SuppressWarnings»("unused") «outputType» «ENDIF»«outputName» = «outputName»Holder.build();«ENDIF» + «indexed.value.assign(aliasOut, names)»; + «ENDFOR» + return «outputName»Holder«IF !outNeedsBuilder».get()«ENDIF»; + } + + «ENDIF» + «FOR alias : func.shortcuts» + + «IF aliasOut.get(alias)» + protected «names.shortcutJavaType(alias)» «alias.name»(«getOutput(func).toBuilderType(names)» «outputName», «IF !getInputs(func).empty»«func.inputsAsParameters(names)»«ENDIF») { + return «expressionWithBuilder.toJava(alias.expression, Context.create(names))»; + } + «ELSE» + protected «IF needsBuilder(alias)»«MapperBuilder»«ELSE»«Mapper»«ENDIF»<«toJavaType(typeProvider.getRType(alias.expression))»> «alias.name»(«func.inputsAsParameters(names)») { + return «expressionGenerator.javaCode(alias.expression, new ParamMap)»; + } + «ENDIF» + «ENDFOR» + } + ''' + } + + + def private StringConcatenationClient dispatchClassBody(Function function,String className, Iterable dependencies, extension JavaNames names, String version) { + val dispatchingFuncs = function.dispatchingFunctions.sortBy[name].toList + val enumParam = function.inputs.filter[type instanceof RosettaEnumeration].head.name + val outputType = function.outputTypeOrVoid(names) + ''' + «emptyJavadocWithVersion(version)» + public class «className» { + «FOR dep : dependencies» + @«Inject» protected «dep.toJavaQualifiedType» «dep.name.toFirstLower»; + «ENDFOR» + + «FOR enumFunc : dispatchingFuncs» + @«Inject» protected «toTargetClassName(enumFunc)» «toTargetClassName(enumFunc).lastSegment»; + «ENDFOR» + + public «outputType» evaluate(«function.inputsAsParameters(names)») { + switch («enumParam») { + «FOR enumFunc : dispatchingFuncs» + «val enumValClass = toTargetClassName(enumFunc).lastSegment» + case «enumValClass»: + return «enumValClass».evaluate(«function.inputsAsArguments(names)»); + «ENDFOR» + default: + throw new IllegalArgumentException("Enum value not implemented: " + «enumParam»); + } + } + + «FOR enumFunc : dispatchingFuncs» + + «val enumValClass = toTargetClassName(enumFunc).lastSegment» + «enumFunc.classBody(enumValClass, collectFunctionDependencies(enumFunc), names, version, true)» + «ENDFOR» + }''' + } + + + private def StringConcatenationClient assign(Operation operation, Map outs, + JavaNames names) { + val pathAsList = operation.pathAsSegmentList + val ctx = Context.create(names) + if (pathAsList.isEmpty) + ''' + «IF needsBuilder(operation.assignRoot)» + «operation.assignTarget(outs, names)» = «expressionWithBuilder.toJava(operation.expression, ctx)» + «ELSE» + «operation.assignTarget(outs, names)» = «MapperS».of(«expressionWithBuilder.toJava(operation.expression, ctx)»)«ENDIF»''' + else + ''' + «operation.assignTarget(outs, names)» + «FOR seg : pathAsList»«IF seg.next !== null».getOrCreate«seg.attribute.name.toFirstUpper»(«IF seg.attribute.many»«seg.index?:0»«ENDIF»)«IF isReference(seg.attribute)».getValue()«ENDIF»«ELSE» + .«IF seg.attribute.isMany»add«ELSE»set«ENDIF»«seg.attribute.name.toFirstUpper»«IF operation.assignTarget().reference»Ref«ENDIF»(«expressionGenerator.javaCode(operation.expression, new ParamMap)».get())«ENDIF»«ENDFOR»; + ''' + } + + def boolean isReference(RosettaNamed ele) { + switch(ele) { + Annotated: hasMetaReferenceAnnotations(ele) + RosettaRegularAttribute: !ele.metaTypes.empty + default:false + } + } + + private def assignTarget(Operation operation) { + if (operation.path === null) { + return operation.assignRoot + } else { + operation.pathAsSegmentList.last.attribute + } + } + private def StringConcatenationClient assignTarget(Operation operation, Map outs, + JavaNames names) { + val root = operation.assignRoot + switch (root) { + Attribute: '''«root.name»Holder''' + ShortcutDeclaration: '''«root.name»(«IF outs.get(root)»«getOutput(operation.function)?.name»Holder«IF !getInputs(operation.function).empty», «ENDIF»«ENDIF»«inputsAsArguments(operation.function, names)»)''' + } + } + + private def StringConcatenationClient contributeCondition(Condition condition) { + ''' + assert + «FOR expr : condition.expressions SEPARATOR ' &&'» + «expressionGenerator.javaCode(expr, null)».get() + «ENDFOR» + : "«condition.definition»"; + ''' + } + + private def JavaType outputTypeOrVoid(Function function, extension JavaNames names) { + val out = getOutput(function) + if (out === null) { + JavaType.create('void') + } else { + out.type.toJavaType() + } + } + + private def StringConcatenationClient inputsAsArguments(extension Function function, extension JavaNames names) { + '''«FOR input : getInputs(function) SEPARATOR ', '»«input.name»«ENDFOR»''' + } + + private def StringConcatenationClient inputsAsParameters(extension Function function, extension JavaNames names) { + '''«FOR input : getInputs(function) SEPARATOR ', '»«input.toJavaQualifiedType()» «input.name»«ENDFOR»''' + } + + def private StringConcatenationClient shortcutJavaType(JavaNames names, ShortcutDeclaration feature) { + val rType = typeProvider.getRType(feature.expression) + val javaType = names.toJavaType(rType) + '''«javaType»«IF needsBuilder(rType)».«javaType»Builder«ENDIF»''' + } + + private def StringConcatenationClient toBuilderType(Attribute attr, JavaNames names) { + val javaType = names.toJavaType(attr.type) + '''«IF needsBuilder(attr)»«javaType».«javaType»Builder«ELSE»«javaType»«ENDIF»''' + } + + private def StringConcatenationClient toHolderType(Attribute attr, JavaNames names) { + val javaType = names.toJavaType(attr.type) + '''«IF needsBuilder(attr)»«javaType».«javaType»Builder«ELSE»«Mapper»<«javaType»>«ENDIF»''' + } + + private def isMany(AssignPathRoot root) { + switch (root) { + Attribute: root.card.isMany + } + } + + private def isMany(RosettaFeature feature) { + switch (feature) { + RosettaRegularAttribute: feature.card.isMany + Attribute: feature.card.isMany + default: throw new IllegalStateException('Unsupported type passed ' + feature?.eClass?.name) + } + } +} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend deleted file mode 100644 index a1c534178..000000000 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/FunctionGenerator.xtend +++ /dev/null @@ -1,340 +0,0 @@ -package com.regnosys.rosetta.generator.java.function - -import com.google.common.collect.ClassToInstanceMap -import com.google.inject.ImplementedBy -import com.google.inject.Inject -import com.regnosys.rosetta.generator.RosettaInternalGenerator -import com.regnosys.rosetta.generator.java.RosettaJavaPackages -import com.regnosys.rosetta.generator.java.calculation.RosettaFunctionDependencyProvider -import com.regnosys.rosetta.generator.java.util.ImportingStringConcatination -import com.regnosys.rosetta.generator.java.util.JavaNames -import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions -import com.regnosys.rosetta.rosetta.RosettaCallableWithArgs -import com.regnosys.rosetta.rosetta.RosettaDefinable -import com.regnosys.rosetta.rosetta.RosettaFuncitonCondition -import com.regnosys.rosetta.rosetta.RosettaFunction -import com.regnosys.rosetta.rosetta.RosettaNamed -import com.regnosys.rosetta.rosetta.RosettaRootElement -import com.regnosys.rosetta.rosetta.simple.Condition -import com.regnosys.rosetta.rosetta.simple.Function -import java.util.List -import org.eclipse.xtend2.lib.StringConcatenationClient -import org.eclipse.xtext.generator.IFileSystemAccess2 - -class FunctionGenerator implements RosettaInternalGenerator { - - @Inject JavaQualifiedTypeProvider.Factory factory - @Inject RosettaExpressionJavaGeneratorForFunctions rosettaExpressionGenerator - @Inject RosettaFunctionDependencyProvider functionDependencyProvider - @Inject extension RosettaFunctionExtensions - - override generate(RosettaJavaPackages packages, IFileSystemAccess2 fsa, List elements, String version) { - val javaNames = factory.create(packages) - - elements.filter(RosettaFunction).forEach [ - val name = javaNames.packages.functions.directoryName + '/' + name + '.java' - - try { - val content = generate(it, javaNames) - fsa.generateFile(name, content) - } catch (Exception e) { - throw new UnsupportedOperationException('Unable to generate code for: ' + name) - } - - ] - } - - - def void generate(JavaNames javaNames, IFileSystemAccess2 fsa, Function func, String version) { - val fileName = javaNames.packages.functions.directoryName + '/' + func.name + '.java' - - try { - val concatenator = new ImportingStringConcatination() - val dependencies = (func.conditions + func.postConditions).flatMap[expressions].flatMap [ - functionDependencyProvider.functionDependencies(it) - ].toSet.sortBy[it.name] - - concatenator.append(functionClass(func, dependencies, javaNames)) - val content = ''' - package «javaNames.packages.functions.packageName»; - - «FOR _import : concatenator.imports» - import «_import»; - «ENDFOR» - «FOR staticImport : concatenator.staticImports» - import static «staticImport»; - «ENDFOR» - «IF (!func.conditions.nullOrEmpty || !func.postConditions.nullOrEmpty) /*FIXME add static imports */» - - import java.math.BigDecimal; - import org.isda.cdm.*; - import com.rosetta.model.lib.meta.*; - import static com.rosetta.model.lib.validation.ValidatorHelper.*; - - «ENDIF» - «concatenator.toString» - ''' - fsa.generateFile(fileName, content) - } catch (Exception e) { - throw new UnsupportedOperationException('Unable to generate code for: ' + fileName, e) - } - } - - private def String generate(RosettaFunction function, JavaQualifiedTypeProvider javaNames) { - val concatenator = new ImportingStringConcatination() - concatenator.append(functionClass(function, javaNames)) - - return ''' - package «javaNames.packages.functions.packageName»; - -««« (DONE) Make RosettaExpression support StringConcatClient to add these imports -««« Now have RosettaExpressionToJava support actually use types (not just strings) - import com.rosetta.model.lib.functions.MapperTree; - import com.rosetta.model.lib.meta.FieldWithMeta; - import java.time.LocalDate; - import java.math.BigDecimal; - - import org.isda.cdm.*; - - import static com.rosetta.model.lib.validation.ValidatorHelper.*; - - «FOR _import : concatenator.imports» - import «_import»; - «ENDFOR» - «FOR staticImport : concatenator.staticImports» - import static «staticImport»; - «ENDFOR» - - «concatenator.toString» - ''' - } - - private def StringConcatenationClient functionClass(Function function, Iterable dependencies, JavaNames javaNames) { - ''' - «function.contributeJavaDoc» - @«ImplementedBy»(«function.name»Impl.class) - public abstract class «function.name» implements «com.rosetta.model.lib.functions.RosettaFunction» { - «contributeFieldDependencies(javaNames, dependencies)» - «contributeEvaluateMethod(function, javaNames)» - «contributeEnrichMethod(function, javaNames)» - } - ''' - } - - private def StringConcatenationClient functionClass(RosettaFunction function, JavaQualifiedTypeProvider javaNames) { - val dependencies = (function.preConditions + function.postConditions) - .flatMap[expressions] - .flatMap[functionDependencyProvider.functionDependencies(it)] - .sortBy[name] - .toSet - - ''' - «function.contributeJavaDoc» - public abstract class «function.name» implements «com.rosetta.model.lib.functions.RosettaFunction» { - «contributeFields(javaNames)» - «contributeConstructor(function, javaNames)» - «contributeEvaluateMethod(function, javaNames, dependencies)» - «contributeEnrichMethod(function, javaNames)» - } - ''' - } - - - def StringConcatenationClient contributeJavaDoc(extension RosettaDefinable function) { - if (definition !== null) { - ''' - /** - «IF definition !== null» - * «definition» - «ENDIF» - */ - ''' - } - } - - - def dispatch StringConcatenationClient contributeEnrichMethod(extension RosettaFunction function, extension JavaQualifiedTypeProvider names) ''' - - protected abstract «output.toJavaQualifiedType(false)» doEvaluate(«function.inputsAsParameters(names)»); - ''' - - def dispatch StringConcatenationClient contributeEnrichMethod(extension Function function, extension JavaNames names) ''' - - protected abstract «function.outputTypeOrVoid(names)» doEvaluate(«function.inputsAsParameters(names)»); - ''' - - - def StringConcatenationClient contributeEvaluateMethod(extension RosettaFunction function, extension JavaQualifiedTypeProvider names, Iterable dependencies) ''' - - /** - «FOR input : inputs» - * @param «input.name» «input.definition» - «ENDFOR» - * @return «output.name» «output.definition» - */ - public «output.toJavaQualifiedType(false)» evaluate(«function.inputsAsParameters(names)») { - «contributeDependencies(names, dependencies)» - «function.contributePreConditions(names)» - - // Delegate to implementation - // - «output.toJavaQualifiedType(false)» «output.name» = doEvaluate(«function.inputsAsArguments(names)»); - «function.contributePostConditions(names)» - - return «output.name»; - } - ''' - - def StringConcatenationClient contributeEvaluateMethod(Function function, extension JavaNames names) ''' - - /** - «FOR input : getInputs(function)» - * @param «input.name» «input.definition» - «ENDFOR» - «IF getOutput(function) !== null» - * @return «getOutput(function).name» «getOutput(function).definition» - «ENDIF» - */ - public «function.outputTypeOrVoid(names)» evaluate(«function.inputsAsParameters(names)») { - «IF !function.conditions.empty» - // pre-conditions - // - «FOR cond:function.conditions» - «cond.contributeCondition» - «ENDFOR» - «ENDIF» - // Delegate to implementation - // - «IF getOutput(function) !== null»«getOutput(function).toJavaQualifiedType()» «getOutput(function).name» = «ENDIF»doEvaluate(«function.inputsAsArguments(names)»); - «IF !function.postConditions.empty» - // post-conditions - // - «FOR cond:function.postConditions» - «cond.contributeCondition» - «ENDFOR» - «ENDIF» - «IF getOutput(function) !== null» - return «getOutput(function).name»; - «ENDIF» - } - ''' - - def outputTypeOrVoid(Function function, extension JavaNames names) { - val out = getOutput(function) - if (out === null) { - 'void' - } else { - out.toJavaQualifiedType() - } - } - - private def StringConcatenationClient contributeFieldDependencies(extension JavaNames javaNames, Iterable dependencies) { - if (!dependencies.empty) { - ''' - - // RosettaFunction dependencies - // - «FOR dep : dependencies» - @«Inject» protected «dep.toJavaQualifiedType» «dep.name.toFirstLower»; - «ENDFOR» - ''' - } - } - private def StringConcatenationClient contributeDependencies(extension JavaQualifiedTypeProvider provider, Iterable dependencies) { - if (!dependencies.empty) { - ''' - - // RosettaFunction dependencies - // - «FOR dep : dependencies» - final «dep.name» «dep.name.toFirstLower» = classRegistry.getInstance(«dep.toJavaQualifiedType()».class); - «ENDFOR» - ''' - } - } - - def StringConcatenationClient contributePostConditions(extension RosettaFunction function, extension JavaQualifiedTypeProvider names) { - if (!postConditions.empty) { - ''' - - // post-conditions - // - «FOR cond:postConditions» - «cond.contributeCondition» - «ENDFOR» - ''' - } - } - - def StringConcatenationClient contributePreConditions(extension RosettaFunction function, extension JavaQualifiedTypeProvider names) { - if (!preConditions.empty) { - ''' - - // pre-conditions - // - «FOR cond:preConditions» - «cond.contributeCondition» - «ENDFOR» - ''' - } - } - - private dispatch def StringConcatenationClient contributeCondition(Condition condition) { - ''' - assert -««« «rosettaExpressionGenerator.javaCode(condition.expressions.head, null)» - «FOR expr : condition.expressions SEPARATOR ' &&'» - «rosettaExpressionGenerator.javaCode(expr, null)».get() - «ENDFOR» - : "«condition.definition»"; - ''' - } - - - private dispatch def StringConcatenationClient contributeCondition(RosettaFuncitonCondition condition) - ''' - assert -««« «rosettaExpressionGenerator.javaCode(condition.expressions.head, null)» - «FOR expr : condition.expressions SEPARATOR ' &&'» - «rosettaExpressionGenerator.javaCode(expr, null)».get() - «ENDFOR» - : "«condition.definition»"; - ''' - - - def StringConcatenationClient contributeConstructor(extension RosettaNamed function, extension JavaQualifiedTypeProvider names) { - ''' - - protected «name.toFirstUpper»(«ClassToInstanceMap»<«com.rosetta.model.lib.functions.RosettaFunction»> classRegistry) { - - // On concrete instantiation, register implementation with function to implementation container - // - classRegistry.putInstance(«name».class, this); - this.classRegistry = classRegistry; - } - ''' - } - - def StringConcatenationClient contributeFields(extension JavaQualifiedTypeProvider names) { - ''' - - protected final «ClassToInstanceMap»<«com.rosetta.model.lib.functions.RosettaFunction»> classRegistry; - ''' - } - - - private def StringConcatenationClient inputsAsParameters(extension RosettaFunction function, extension JavaQualifiedTypeProvider names) { - '''«FOR input : inputs SEPARATOR ', '»«input.toJavaQualifiedType(false)» «input.name»«ENDFOR»''' - } - private def StringConcatenationClient inputsAsParameters(extension Function function, extension JavaNames names) { - '''«FOR input : getInputs(function) SEPARATOR ', '»«input.toJavaQualifiedType()» «input.name»«ENDFOR»''' - } - - private dispatch def StringConcatenationClient inputsAsArguments(extension RosettaFunction function, extension JavaQualifiedTypeProvider names) { - '''«FOR input : inputs SEPARATOR ', '»«input.name»«ENDFOR»''' - } - private dispatch def StringConcatenationClient inputsAsArguments(extension Function function, extension JavaNames names) { - '''«FOR input : getInputs(function) SEPARATOR ', '»«input.name»«ENDFOR»''' - } - -} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/JavaQualifiedTypeProvider.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/JavaQualifiedTypeProvider.xtend index 2f419daf3..494cd58f8 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/JavaQualifiedTypeProvider.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/JavaQualifiedTypeProvider.xtend @@ -51,7 +51,7 @@ class JavaQualifiedTypeProvider { def StringConcatenationClient toJavaQualifiedType(RosettaType type) { type.toJavaQualifiedType(false) } - + def StringConcatenationClient toJavaQualifiedType(RosettaType type, boolean asBuilder) { switch (type) { RosettaBasicType: diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaFunctionDependencyProvider.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/RosettaFunctionDependencyProvider.xtend similarity index 97% rename from com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaFunctionDependencyProvider.xtend rename to com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/RosettaFunctionDependencyProvider.xtend index 4de3c546f..57fcbece6 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/calculation/RosettaFunctionDependencyProvider.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/function/RosettaFunctionDependencyProvider.xtend @@ -1,4 +1,4 @@ -package com.regnosys.rosetta.generator.java.calculation +package com.regnosys.rosetta.generator.java.function import com.google.inject.Inject import com.regnosys.rosetta.rosetta.RosettaAlias diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend index ab1dc388d..79133f937 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/object/ModelObjectBuilderGenerator.xtend @@ -22,6 +22,7 @@ import java.util.ArrayList import com.regnosys.rosetta.generator.java.util.JavaType import com.regnosys.rosetta.RosettaExtensions import com.regnosys.rosetta.generator.java.util.JavaNames +import com.rosetta.model.lib.functions.MapperBuilder class ModelObjectBuilderGenerator { @@ -40,7 +41,7 @@ class ModelObjectBuilderGenerator { Optional.ofNullable(clazz.superType).map[builderName].orElse('RosettaModelObjectBuilder') } - dispatch def StringConcatenationClient builderClass(Data c, JavaNames names) ''' + def StringConcatenationClient builderClass(Data c, JavaNames names) ''' public static class «builderName(c)» extends «IF c.hasSuperType»«c.superType.builderName»«ELSE»«RosettaModelObjectBuilder»«ENDIF»{ «FOR attribute : c.expandedAttributes» @@ -262,13 +263,21 @@ class ModelObjectBuilderGenerator { «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»(«attribute.toTypeSingle(names)» «attribute.name») { if(this.«attribute.name» == null){ this.«attribute.name» = new ArrayList<>(); - this.«attribute.name».add(«attribute.toBuilder»); - } else { - this.«attribute.name».add(«attribute.toBuilder»); } + this.«attribute.name».add(«attribute.toBuilder»); return this; } - + + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»(List<«attribute.toTypeSingle(names)»> «attribute.name»s) { + if(this.«attribute.name» == null){ + this.«attribute.name» = new «ArrayList»<>(); + } + for («attribute.toTypeSingle(names)» toAdd : «attribute.name»s) { + this.«attribute.name».add(toAdd.toBuilder()); + } + return this; + } + «IF attribute.isRosettaClassOrData» «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»Builder(«attribute.toBuilderTypeSingle(names)» «attribute.name») { if(this.«attribute.name» == null){ @@ -291,6 +300,11 @@ class ModelObjectBuilderGenerator { this.«attribute.name» = «attribute.toBuilder»; return this; } + + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» set«attribute.name.toFirstUpper»(«MapperBuilder»<«attribute.toType(names)»> «attribute.name») { + set«attribute.name.toFirstUpper»(«attribute.name».get()); + return this; + } «IF attribute.isRosettaClassOrData» «IF isSuper»@Override «ENDIF»public «thisClass.builderName» set«attribute.name.toFirstUpper»Builder(«attribute.toBuilderType» «attribute.name») { @@ -309,24 +323,35 @@ class ModelObjectBuilderGenerator { «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»(«attribute.toTypeSingle» «attribute.name») { if(this.«attribute.name» == null){ this.«attribute.name» = new ArrayList<>(); - this.«attribute.name».add(«attribute.toBuilder»); - } else { - this.«attribute.name».add(«attribute.toBuilder»); + } + this.«attribute.name».add(«attribute.toBuilder»); + return this; + } + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»(List<«attribute.toTypeSingle()»> «attribute.name»s) { + if(this.«attribute.name» == null){ + this.«attribute.name» = new ArrayList<>(); + } + for («attribute.toTypeSingle()» toAdd : «attribute.name»s) { + this.«attribute.name».add(toAdd«IF needsBuilder(attribute)».toBuilder()«ENDIF»); } return this; } - «IF attribute.isRosettaClassOrData» «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»Builder(«attribute.toBuilderTypeSingle» «attribute.name») { if(this.«attribute.name» == null){ this.«attribute.name» = new ArrayList<>(); - this.«attribute.name».add(«attribute.name»); - } else { - this.«attribute.name».add(«attribute.name»); } + this.«attribute.name».add(«attribute.name»); return this; } - + «IF !attribute.metas.empty» + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»Ref(«attribute.toBuilderTypeUnderlying» «attribute.name») { + return add«attribute.name.toFirstUpper»(«attribute.toTypeSingle».builder().setValueBuilder(«attribute.name»).build()); + } + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» add«attribute.name.toFirstUpper»Ref(«attribute.type.name» «attribute.name») { + return add«attribute.name.toFirstUpper»Ref(«attribute.name».toBuilder()); + } + «ENDIF» «ENDIF» «IF isSuper»@Override «ENDIF»public «thisClass.builderName» clear«attribute.name.toFirstUpper»() { @@ -338,13 +363,25 @@ class ModelObjectBuilderGenerator { this.«attribute.name» = «attribute.toBuilder»; return this; } - «IF attribute.isRosettaClassOrData» «IF isSuper»@Override «ENDIF»public «thisClass.builderName» set«attribute.name.toFirstUpper»Builder(«attribute.toBuilderType» «attribute.name») { this.«attribute.name» = «attribute.name»; return this; } - + «IF !attribute.metas.empty» + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» set«attribute.name.toFirstUpper»Ref(«attribute.toBuilderTypeUnderlying» «attribute.name») { + return set«attribute.name.toFirstUpper»(«attribute.toTypeSingle».builder().setValueBuilder(«attribute.name»).build()); + } + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» set«attribute.name.toFirstUpper»Ref(«attribute.type.name» «attribute.name») { + return set«attribute.name.toFirstUpper»Ref(«attribute.type.name».builder()); + } + «ENDIF» + «ELSE» + «IF !attribute.metas.empty» + «IF isSuper»@Override «ENDIF»public «thisClass.builderName» set«attribute.name.toFirstUpper»Ref(«attribute.toBuilderTypeUnderlying» «attribute.name») { + return set«attribute.name.toFirstUpper»(«attribute.toTypeSingle».builder().setValue(«attribute.name»).build()); + } + «ENDIF» «ENDIF» «ENDIF» «ENDFOR» @@ -382,7 +419,9 @@ class ModelObjectBuilderGenerator { if (attribute.isMultiple) '''List<«attribute.toBuilderTypeSingle»>''' else attribute.toBuilderTypeSingle; } - + /** + * Use toBuilderTypeSingle(ExpandedAttribute, JavaNames) + */ @Deprecated private def StringConcatenationClient toBuilderTypeSingle(ExpandedAttribute attribute) { if (attribute.hasMetas) { @@ -424,10 +463,13 @@ class ModelObjectBuilderGenerator { private def toBuilder(ExpandedAttribute attribute) { - if(attribute.isRosettaClassOrData || attribute.hasMetas) { + if(needsBuilder(attribute)) { '''«attribute.name».toBuilder()''' } else { attribute.name } - } + } + private def needsBuilder(ExpandedAttribute attribute){ + attribute.isRosettaClassOrData || attribute.hasMetas + } } \ No newline at end of file diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/rule/DataRuleGenerator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/rule/DataRuleGenerator.xtend index 502bf2db3..e096c4de9 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/rule/DataRuleGenerator.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/rule/DataRuleGenerator.xtend @@ -5,8 +5,8 @@ import com.google.inject.Inject import com.regnosys.rosetta.RosettaExtensions import com.regnosys.rosetta.generator.java.RosettaJavaPackages import com.regnosys.rosetta.generator.java.expression.RosettaExpressionJavaGenerator -import com.regnosys.rosetta.generator.java.function.RosettaExpressionJavaGeneratorForFunctions -import com.regnosys.rosetta.generator.java.function.RosettaExpressionJavaGeneratorForFunctions.ParamMap +import com.regnosys.rosetta.generator.java.expression.RosettaExpressionJavaGeneratorForFunctions +import com.regnosys.rosetta.generator.java.expression.RosettaExpressionJavaGeneratorForFunctions.ParamMap import com.regnosys.rosetta.generator.java.util.ImportGenerator import com.regnosys.rosetta.generator.java.util.ImportManagerExtension import com.regnosys.rosetta.generator.java.util.JavaNames @@ -53,10 +53,6 @@ class DataRuleGenerator { «FOR imp : classBody.staticImports» import static «imp»; «ENDFOR» -««« TODO fix it in com.regnosys.rosetta.generator.java.expression.RosettaExpressionJavaGenerator.javaCode(RosettaExpression, ParamMap) - import org.isda.cdm.*; - //import static com.rosetta.model.lib.validation.MapperTreeValidatorHelper.*; - import static com.rosetta.model.lib.validation.ValidatorHelper.*; «classBody.toString» ''' diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/util/JavaNames.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/util/JavaNames.xtend index 74ffe4334..a00459de6 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/util/JavaNames.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/java/util/JavaNames.xtend @@ -14,16 +14,25 @@ import com.regnosys.rosetta.rosetta.RosettaFeature import com.regnosys.rosetta.rosetta.RosettaModel import com.regnosys.rosetta.rosetta.RosettaRecordType import com.regnosys.rosetta.rosetta.RosettaType +import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.Function import com.regnosys.rosetta.rosetta.simple.FunctionDispatch +import com.regnosys.rosetta.types.RBuiltinType +import com.regnosys.rosetta.types.RClassType +import com.regnosys.rosetta.types.RDataType +import com.regnosys.rosetta.types.REnumType +import com.regnosys.rosetta.types.RFeatureCallType +import com.regnosys.rosetta.types.RRecordType +import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.RUnionType import com.regnosys.rosetta.types.RosettaTypeProvider +import java.util.List import org.eclipse.xtend.lib.annotations.Accessors import org.eclipse.xtend2.lib.StringConcatenationClient import org.eclipse.xtext.naming.QualifiedName -import com.regnosys.rosetta.rosetta.simple.Attribute -import java.util.List +import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration +import com.regnosys.rosetta.rosetta.simple.AssignPathRoot class JavaNames { @@ -41,6 +50,12 @@ class JavaNames { } } + def StringConcatenationClient toJavaQualifiedType(AssignPathRoot ele) { + switch(ele) { + Attribute: toJavaQualifiedType(ele.type) + ShortcutDeclaration: '''«toJavaType(typeProvider.getRType(ele.expression))»''' + } + } def StringConcatenationClient toJavaQualifiedType(String typeName) { return '''«JavaType.create(JavaClassTranslator.toJavaFullType(typeName)?:"missing builtin type " + typeName)»''' @@ -63,8 +78,6 @@ class JavaNames { def JavaType toJavaType(RosettaCallableWithArgs func) { switch (func) { - Function case !func.operations.nullOrEmpty: - JavaType.create(packages.calculation.packageName+'.'+ func.name) Function: JavaType.create(packages.functions.packageName+'.'+ func.name) default: @@ -86,7 +99,7 @@ class JavaNames { throw new UnsupportedOperationException("Not implemented for type " + type?.class?.name) } } - + private def JavaType createForBasicType(String typeName) { return JavaType.create(JavaClassTranslator.toJavaFullType(typeName)?:"missing builtin type " + typeName) } @@ -115,6 +128,26 @@ class JavaNames { else '''«attribute.type.toJavaQualifiedType()»''' } + + def JavaType toJavaType(RType rType) { + switch (rType) { + RBuiltinType: + rType.name.createForBasicType + REnumType: + rType.enumeration.toJavaType + RClassType: + rType.clazz.toJavaType + RDataType: + rType.data.toJavaType + RFeatureCallType: + rType.featureType.toJavaType + RRecordType: + (rType.record as RosettaType).toJavaType + default: + JavaType.create(rType.name) + } + } + def QualifiedName toTargetClassName(RosettaCalculation ele) { return QualifiedName.create(ele.name.split('\\.')) } diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend index d33b60fb3..5fc386f00 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/generator/util/RosettaFunctionExtensions.xtend @@ -1,11 +1,28 @@ package com.regnosys.rosetta.generator.util +import com.google.inject.Inject +import com.regnosys.rosetta.rosetta.RosettaClass +import com.regnosys.rosetta.rosetta.RosettaType +import com.regnosys.rosetta.rosetta.RosettaTyped +import com.regnosys.rosetta.rosetta.simple.AssignPathRoot +import com.regnosys.rosetta.rosetta.simple.Attribute +import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.Function import com.regnosys.rosetta.rosetta.simple.FunctionDispatch +import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration +import com.regnosys.rosetta.rosetta.simple.SimplePackage +import com.regnosys.rosetta.types.RClassType +import com.regnosys.rosetta.types.RDataType +import com.regnosys.rosetta.types.RType +import com.regnosys.rosetta.types.RosettaTypeProvider import org.eclipse.xtext.EcoreUtil2 class RosettaFunctionExtensions { + + @Inject RosettaTypeProvider typeProvider + /** + * * spec functions do not have operation hence, do not provide an implementation */ def Boolean handleAsSpecFunction(Function function) { @@ -15,12 +32,11 @@ class RosettaFunctionExtensions { def Boolean handleAsEnumFunction(Function function) { function.operations.nullOrEmpty && !function.dispatchingFunctions.empty } - + def Boolean isDispatchingFunction(Function function) { (function instanceof FunctionDispatch) } - def getDispatchingFunctions(Function function) { // TODO Look-up other Rosetta files? EcoreUtil2.getSiblingsOfType(function, FunctionDispatch).filter[it.name == function.name] @@ -57,4 +73,45 @@ class RosettaFunctionExtensions { } emptyList } + + def inputsAsArgs(ShortcutDeclaration alias) { + val func = EcoreUtil2.getContainerOfType(alias, Function) + getInputs(func).join(', ')[name] + } + + dispatch def boolean needsBuilder(RosettaTyped ele) { + needsBuilder(ele.type) + } + + dispatch def boolean needsBuilder(ShortcutDeclaration alias) { + needsBuilder(typeProvider.getRType(alias.expression)) + } + + dispatch def boolean needsBuilder(AssignPathRoot root) { + switch (root) { + Attribute: root.type.needsBuilder + ShortcutDeclaration: typeProvider.getRType(root.expression).needsBuilder + default: false + } + } + + dispatch def boolean needsBuilder(RosettaType type) { + switch (type) { + RosettaClass, + Data: true + default: false + } + } + + dispatch def boolean needsBuilder(RType type) { + switch (type) { + RClassType, + RDataType: true + default: false + } + } + + def boolean isOutput(Attribute attr) { + attr.eContainingFeature === SimplePackage.Literals.FUNCTION__OUTPUT + } } diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend index bd060ea07..9497322ad 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/scoping/RosettaScopeProvider.xtend @@ -121,19 +121,22 @@ class RosettaScopeProvider extends AbstractRosettaScopeProvider { } return IScope.NULLSCOPE } - case OPERATION__ATTRIBUTE: { + case OPERATION__ASSIGN_ROOT: { if (context instanceof Operation) { + val outAndAliases = newArrayList val out = getOutput(context.function) if (out !== null) { - return Scopes.scopeFor(#[out]) + outAndAliases.add(out) } + outAndAliases.addAll(context.function.shortcuts) + return Scopes.scopeFor(outAndAliases) } return IScope.NULLSCOPE } case SEGMENT__ATTRIBUTE: { switch (context) { Operation: { - val receiverType = typeProvider.getRType(context.attribute) + val receiverType = typeProvider.getRType(context.assignRoot) val featureScope = receiverType.createFeatureScope if (featureScope !== null) { return featureScope; diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaExpectedTypeProvider.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaExpectedTypeProvider.xtend index b822e2418..921e955a5 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaExpectedTypeProvider.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaExpectedTypeProvider.xtend @@ -13,7 +13,9 @@ import org.eclipse.emf.ecore.EObject import org.eclipse.emf.ecore.EReference import static com.regnosys.rosetta.rosetta.RosettaPackage.Literals.* +import static com.regnosys.rosetta.rosetta.simple.SimplePackage.Literals.* import com.regnosys.rosetta.rosetta.RosettaExternalFunction +import com.regnosys.rosetta.rosetta.simple.Operation class RosettaExpectedTypeProvider { @@ -54,6 +56,11 @@ class RosettaExpectedTypeProvider { } } } + Operation case reference == OPERATION__EXPRESSION:{ + if(owner.path === null) + owner.assignRoot.RType + else owner.pathAsSegmentList.last?.attribute?.RType + } } } diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaOperators.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaOperators.xtend index b3576d095..6ad414c4a 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaOperators.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaOperators.xtend @@ -112,7 +112,7 @@ class RosettaOperators { operation(number, number) => number } '+'.operation(string, string) => string - '+'.operation(date, time) => dateTime + '+'.operation(date, time) => string '-'.operation(date, date) => number for (it : COMPARISON_OPS) { diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaTypeProvider.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaTypeProvider.xtend index 7e97b8484..f64d4c122 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaTypeProvider.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/types/RosettaTypeProvider.xtend @@ -58,6 +58,7 @@ class RosettaTypeProvider { @Inject extension RosettaFunctionExtensions @Inject IQualifiedNameProvider qNames @Inject IDValueConverter idConverter + @Inject RosettaTypeCompatibility compatibility def RType getRType(EObject expression) { expression.safeRType(newHashMap) @@ -265,8 +266,25 @@ class RosettaTypeProvider { } RosettaParenthesisCalcExpression: expression.expression.safeRType(cycleTracker) - RosettaConditionalExpression: - expression.ifthen.safeRType(cycleTracker) + RosettaConditionalExpression: { + val ifT = expression.ifthen.safeRType(cycleTracker) + if (expression.elsethen === null) { + ifT + } else { + val elseT = expression.elsethen.safeRType(cycleTracker) + if (ifT instanceof RErrorType) { + elseT + } else if (elseT instanceof RErrorType) { + ifT + } else if (compatibility.isUseableAs(ifT, elseT)) { + elseT + } else if (compatibility.isUseableAs(elseT, ifT)) { + ifT + } else { + new RErrorType("Can not infer common type for '" + ifT.name + "' and " + elseT.name + "'.") + } + } + } RosettaMapPathValue: RBuiltinType.STRING RosettaMapPath: diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/utils/ExpressionHelper.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/utils/ExpressionHelper.xtend new file mode 100644 index 000000000..427598ef4 --- /dev/null +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/utils/ExpressionHelper.xtend @@ -0,0 +1,42 @@ +package com.regnosys.rosetta.utils + +import com.regnosys.rosetta.rosetta.RosettaCallable +import com.regnosys.rosetta.rosetta.RosettaCallableCall +import com.regnosys.rosetta.rosetta.RosettaExpression +import com.regnosys.rosetta.rosetta.simple.Attribute +import com.regnosys.rosetta.rosetta.simple.ShortcutDeclaration +import java.util.List +import java.util.Stack +import org.eclipse.emf.ecore.EObject +import com.regnosys.rosetta.rosetta.simple.SimplePackage + +class ExpressionHelper { + + def usesOutputParameter(RosettaExpression expr) { + return !expr.findOutputRef(new Stack).nullOrEmpty + } + + def List findOutputRef(EObject ele, Stack trace) { + switch (ele) { + ShortcutDeclaration: { + trace.push(ele.name) + val result = findOutputRef(ele.expression, trace) + if (result.empty) + trace.pop() + return result + } + RosettaCallableCall: { + if (ele.callable instanceof Attribute && + ele.callable.eContainingFeature === SimplePackage.Literals.FUNCTION__OUTPUT) + return #[ele.callable] + return findOutputRef(ele.callable, trace) + } + } + return (ele.eContents + ele.eCrossReferences.filter [ + it instanceof RosettaExpression || it instanceof ShortcutDeclaration + ]).flatMap [ + findOutputRef(trace) + ].toList + } + +} diff --git a/com.regnosys.rosetta/src/com/regnosys/rosetta/validation/RosettaValidator.xtend b/com.regnosys.rosetta/src/com/regnosys/rosetta/validation/RosettaValidator.xtend index 109e035d8..aa00eaf31 100644 --- a/com.regnosys.rosetta/src/com/regnosys/rosetta/validation/RosettaValidator.xtend +++ b/com.regnosys.rosetta/src/com/regnosys/rosetta/validation/RosettaValidator.xtend @@ -8,12 +8,12 @@ import com.google.common.collect.HashMultimap import com.google.common.collect.LinkedHashMultimap import com.google.inject.Inject import com.regnosys.rosetta.RosettaExtensions +import com.regnosys.rosetta.generator.java.function.ConvertableCardinalityProvider import com.regnosys.rosetta.generator.util.RosettaFunctionExtensions import com.regnosys.rosetta.rosetta.RosettaAlias import com.regnosys.rosetta.rosetta.RosettaArguments import com.regnosys.rosetta.rosetta.RosettaBlueprint import com.regnosys.rosetta.rosetta.RosettaCalculation -import com.regnosys.rosetta.rosetta.RosettaCallable import com.regnosys.rosetta.rosetta.RosettaCallableCall import com.regnosys.rosetta.rosetta.RosettaCallableWithArgsCall import com.regnosys.rosetta.rosetta.RosettaChoiceRule @@ -22,7 +22,6 @@ import com.regnosys.rosetta.rosetta.RosettaDataRule import com.regnosys.rosetta.rosetta.RosettaEnumValueReference import com.regnosys.rosetta.rosetta.RosettaEnumeration import com.regnosys.rosetta.rosetta.RosettaEvent -import com.regnosys.rosetta.rosetta.RosettaExpression import com.regnosys.rosetta.rosetta.RosettaExternalFunction import com.regnosys.rosetta.rosetta.RosettaFeature import com.regnosys.rosetta.rosetta.RosettaFeatureCall @@ -39,10 +38,11 @@ import com.regnosys.rosetta.rosetta.RosettaRegularAttribute import com.regnosys.rosetta.rosetta.RosettaTreeNode import com.regnosys.rosetta.rosetta.RosettaType import com.regnosys.rosetta.rosetta.RosettaWorkflowRule -import com.regnosys.rosetta.rosetta.simple.Attribute import com.regnosys.rosetta.rosetta.simple.Data import com.regnosys.rosetta.rosetta.simple.Function import com.regnosys.rosetta.rosetta.simple.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.RBuiltinType import com.regnosys.rosetta.types.RErrorType @@ -51,6 +51,7 @@ import com.regnosys.rosetta.types.RType import com.regnosys.rosetta.types.RosettaExpectedTypeProvider import com.regnosys.rosetta.types.RosettaTypeCompatibility import com.regnosys.rosetta.types.RosettaTypeProvider +import com.regnosys.rosetta.utils.ExpressionHelper import com.regnosys.rosetta.utils.RosettaQualifiableExtension import com.regnosys.rosetta.validation.RosettaBlueprintTypeResolver.BlueprintUnresolvedTypeException import java.util.List @@ -86,7 +87,9 @@ class RosettaValidator extends AbstractRosettaValidator implements RosettaIssueC @Inject extension ResourceDescriptionsProvider @Inject extension RosettaBlueprintTypeResolver @Inject extension RosettaFunctionExtensions - + @Inject ExpressionHelper exprHelper + @Inject ConvertableCardinalityProvider cardinality + @Check def void checkClassNameStartsWithCapital(RosettaClass classe) { if (!Character.isUpperCase(classe.name.charAt(0))) { @@ -323,6 +326,16 @@ class RosettaValidator extends AbstractRosettaValidator implements RosettaIssueC } ] } + @Check + def checkFunctionElementNamesAreUnique(Function ele) { + (ele.inputs + ele.shortcuts + #[ele.output]).filterNull.groupBy[name].forEach [ k, v | + if (v.size > 1) { + v.forEach [ + error('''Duplicate feature "«k»"''', it, ROSETTA_NAMED__NAME) + ] + } + ] + } // TODO This probably should be made namespace aware @Check(FAST) // switch to NORMAL if it becomes slow @@ -488,6 +501,10 @@ class RosettaValidator extends AbstractRosettaValidator implements RosettaIssueC val callerArg = indexed.value val param = callable.inputs.get(indexed.key) checkType(param.type.RType, callerArg, element, ROSETTA_CALLABLE_WITH_ARGS_CALL__ARGS, indexed.key) + if(!param.card.isMany && cardinality.isMulti(callerArg)) { + error('''Expecting single cardinality for parameter '«param.name»'.''', element, + ROSETTA_CALLABLE_WITH_ARGS_CALL__ARGS, indexed.key) + } ] } } @@ -600,7 +617,7 @@ class RosettaValidator extends AbstractRosettaValidator implements RosettaIssueC ele.conditions.filter[!isPostCondition].forEach [ cond | cond.expressions.forEach [ val trace = new Stack - val outRef = findOutputRef(trace) + val outRef = exprHelper.findOutputRef(it, trace) if (!outRef.nullOrEmpty) { error(''' output '«outRef.head.name»' or alias' on output '«outRef.head.name»' not allowed in condition blocks. @@ -610,26 +627,16 @@ class RosettaValidator extends AbstractRosettaValidator implements RosettaIssueC ] ] } - - def List findOutputRef(EObject ele, Stack trace) { - switch (ele) { - ShortcutDeclaration: { - trace.push(ele.name) - val result = findOutputRef(ele.expression, trace) - if (result.empty) - trace.pop() - return result - } - RosettaCallableCall: { - if (ele.callable instanceof Attribute && ele.callable.eContainingFeature === FUNCTION__OUTPUT) - return #[ele.callable] - return findOutputRef(ele.callable, trace) - } - } - return (ele.eContents + ele.eCrossReferences.filter [ - it instanceof RosettaExpression || it instanceof ShortcutDeclaration - ]).flatMap [ - findOutputRef(trace) - ].toList + + @Check + def checkAssignAnAlias(Operation ele) { + if (ele.path === null && ele.assignRoot instanceof ShortcutDeclaration) + error('''An alias can not be assigned. Assign target must be an attribute.''', ele, OPERATION__ASSIGN_ROOT) + } + + @Check + def checkListElementAccess(Segment ele) { + if (ele.index !== null && !ele.attribute.card.isIsMany) + error('''Element access only possible for multiple cardinality.''', ele, SEGMENT__NEXT) } }