Convert RuleGenerator, ReportGenerator and FunctionGenerator to Java and migrate them to the fluent API - #1293
Conversation
✅ Deploy Preview for finos-rune canceled.
|
| StringConcatenationClient classCode = generator.generateClass(func, typeRepresentation, "test", classScope); | ||
| String javaFileCode = importManager.buildClass(typeRepresentation.getPackageName(), classCode, classScope.getFileScope()); | ||
| CodeRenderer classCode = generator.generateClass(func, typeRepresentation, "test", classScope); | ||
| String javaFileCode = generator.buildClass(typeRepresentation.getPackageName(), classCode, classScope.getFileScope()); |
There was a problem hiding this comment.
How come this pattern changes? E.g., I would have thought this would just use a similar fluent API service.
There was a problem hiding this comment.
Added FluentImportManager in 7aa66d4, the fluent counterpart of ImportManagerExtension. FluentJavaClassGenerator no longer declares a buildClass method at all - it now injects FluentImportManager and delegates to it, the same way XtendJavaClassGenerator injects ImportManagerExtension. FluentImportManager#buildClass is public, matching ImportManagerExtension#buildClass.
Why this was needed: buildClass had been made public on FluentJavaClassGenerator purely so FunctionGeneratorHelper could call it - it's declared in package generator.java, while FunctionGeneratorHelper is in generator.java.function, a different package, and not a subclass, so it wasn't reachable via protected either way. FunctionGeneratorHelper now injects FluentImportManager directly instead of reaching through the generator instance.
Move RuleGenerator from the deprecated XtendJavaClassGenerator base class to FluentRObjectJavaClassGenerator, so generateClass returns a CodeRenderer. The class body is still produced as an Xtend template by the (not-yet-migrated) FunctionGenerator, so add a reusable compatibility bridge, LegacyTemplateRenderer, that adapts a legacy StringConcatenationClient class body into a CodeRenderer. Layout is produced by Xtend's StringConcatenation (as the legacy ImportManagerExtension#buildClass does), while embedded JavaClasses and Methods are resolved against the RecordingCodeWriter so the fluent file builder collects their imports and resolves their identifiers. Output is byte-identical, letting generators move onto the fluent API one at a time, before their templates have been rewritten with the fluent CodeWriter API.
Like RuleGenerator, move ReportGenerator from the deprecated RObjectJavaClassGenerator base class to FluentRObjectJavaClassGenerator, with generateClass returning a CodeRenderer. The class body is still produced as an Xtend template by FunctionGenerator, so wrap it with LegacyTemplateRenderer. The @RosettaReport and @RuneLabelProvider annotation bodies, previously Xtend '''...''' templates, are built as StringConcatenationClient instances (the same idiom already used in AliasUtil); the label-provider class is embedded as an object so its import is registered.
Convert FunctionGenerator.xtend to Java, rendering the function class with the fluent CodeWriter API instead of Xtend templates. generateClass and rBuildClass now return a CodeRenderer. Because rBuildClass returns a CodeRenderer, RuleGenerator and ReportGenerator no longer wrap its result, and ReportGenerator's annotation bodies become CodeRenderers too. The class-body LegacyTemplateRenderer bridge is therefore no longer needed and is removed; its expression-level counterpart is extracted from JavaExpression into a reusable CodeWriterTargetStringConcatenation, which FunctionGenerator uses to embed the few fragments still produced as Xtend templates by not-yet-migrated code (method parameter lists, the nested class declaration, alias call arguments). Unlike the Xtend-backed renderer, this streaming bridge defers identifier resolution, keeping the scope open while the rest of the class is rendered. FluentJavaClassGenerator#buildClass is made public so the function test helper can drive single-class generation. Generated function and rule code now uses four-space indentation and no trailing whitespace (the fluent CodeWriter defaults), so the affected expectations are updated accordingly; behaviour is unchanged (all generated code still compiles and runs).
Make classBody return a CodeRenderer (like dispatchClassBody and the original StringConcatenationClient-returning classBody) instead of being a void method taking a CodeWriter, so rBuildClass returns it directly. Render the default class declaration inline (class XDefault extends ...) instead of via RGeneratedJavaClass#asClassDeclaration, which lets defaultClass be typed as JavaClass<?> and removes one legacy-template-fragment bridge usage.
Change RGeneratedJavaClass#asClassDeclaration to return a dual TargetLanguage representation that renders via the fluent CodeWriter API and, through a reverse bridge, still appends to the legacy Xtend template machinery. FunctionGenerator now renders the default class via asClassDeclaration() again (so defaultClass is an RGeneratedJavaClass<?>), and the not-yet-migrated generators that embed it in Xtend templates keep working unchanged. Extract the reverse bridge TargetStringConcatenationCodeWriter from JavaExpression into a reusable public class (the counterpart of CodeWriterTargetStringConcatenation). For consistency with FunctionGenerator#classBody, DeepPathUtilGenerator#renderClass now returns a CodeRenderer instead of being a void method taking a CodeWriter.
4efcde7 to
a068d54
Compare
…sion FluentJavaClassGenerator#buildClass had been made public so FunctionGeneratorHelper could call it directly to assemble a single function's class file from its CodeRenderer. That widened an internal detail of the generator base class purely to serve one external caller, instead of exposing an independently-usable service the way ImportManagerExtension always was for the legacy Xtend path. Extract buildClass into FluentImportManager, injected the same way XtendJavaClassGenerator injects ImportManagerExtension, and have FunctionGeneratorHelper depend on it directly instead of reaching through the generator instance. buildClass reverts to protected.
…to Java and migrate them to the fluent API (#1375) * Convert RuleGenerator Xtend file to Java * Migrate RuleGenerator to the fluent API via a legacy-template bridge Move RuleGenerator from the deprecated XtendJavaClassGenerator base class to FluentRObjectJavaClassGenerator, so generateClass returns a CodeRenderer. The class body is still produced as an Xtend template by the (not-yet-migrated) FunctionGenerator, so add a reusable compatibility bridge, LegacyTemplateRenderer, that adapts a legacy StringConcatenationClient class body into a CodeRenderer. Layout is produced by Xtend's StringConcatenation (as the legacy ImportManagerExtension#buildClass does), while embedded JavaClasses and Methods are resolved against the RecordingCodeWriter so the fluent file builder collects their imports and resolves their identifiers. Output is byte-identical, letting generators move onto the fluent API one at a time, before their templates have been rewritten with the fluent CodeWriter API. * Use readable declared type for baseInterface * Convert ReportGenerator to Java and migrate it to the fluent API Like RuleGenerator, move ReportGenerator from the deprecated RObjectJavaClassGenerator base class to FluentRObjectJavaClassGenerator, with generateClass returning a CodeRenderer. The class body is still produced as an Xtend template by FunctionGenerator, so wrap it with LegacyTemplateRenderer. The @RosettaReport and @RuneLabelProvider annotation bodies, previously Xtend '''...''' templates, are built as StringConcatenationClient instances (the same idiom already used in AliasUtil); the label-provider class is embedded as an object so its import is registered. * Convert FunctionGenerator to Java and migrate it to the fluent API Ported from #1293, with the choice-type implied-key handling (#1252) omitted: that logic depends on RosettaDeepFeatureCall.feature being widened from Attribute to RosettaFeature, an EMF API change that hasn't been backported to 9.x.x. FunctionOperationGeneratorTest.java, which only tests that logic, is dropped for the same reason. * Return a CodeRenderer from classBody; narrow defaultClass to JavaClass Make classBody return a CodeRenderer (like dispatchClassBody and the original StringConcatenationClient-returning classBody) instead of being a void method taking a CodeWriter, so rBuildClass returns it directly. Render the default class declaration inline (class XDefault extends ...) instead of via RGeneratedJavaClass#asClassDeclaration, which lets defaultClass be typed as JavaClass<?> and removes one legacy-template-fragment bridge usage. * Make asClassDeclaration fluent; align DeepPathUtilGenerator Change RGeneratedJavaClass#asClassDeclaration to return a dual TargetLanguage representation that renders via the fluent CodeWriter API and, through a reverse bridge, still appends to the legacy Xtend template machinery. FunctionGenerator now renders the default class via asClassDeclaration() again (so defaultClass is an RGeneratedJavaClass<?>), and the not-yet-migrated generators that embed it in Xtend templates keep working unchanged. Extract the reverse bridge TargetStringConcatenationCodeWriter from JavaExpression into a reusable public class (the counterpart of CodeWriterTargetStringConcatenation). For consistency with FunctionGenerator#classBody, DeepPathUtilGenerator#renderClass now returns a CodeRenderer instead of being a void method taking a CodeWriter. * Add FluentImportManager, the fluent counterpart of ImportManagerExtension FluentJavaClassGenerator#buildClass had been made public so FunctionGeneratorHelper could call it directly to assemble a single function's class file from its CodeRenderer. That widened an internal detail of the generator base class purely to serve one external caller, instead of exposing an independently-usable service the way ImportManagerExtension always was for the legacy Xtend path. Extract buildClass into FluentImportManager, injected the same way XtendJavaClassGenerator injects ImportManagerExtension, and have FunctionGeneratorHelper depend on it directly instead of reaching through the generator instance. buildClass reverts to protected. * Update CalculationFunctionGeneratorTest expectations for the new FunctionGenerator output format FunctionGenerator's fluent-API migration (previous commit) changed the generator's whitespace conventions (4-space indent, no blank line after an opening brace, condensed Javadoc). This still-Xtend test file (never migrated on 9.x.x, unlike its main-branch counterpart FunctionOperationGeneratorTest) asserts against hardcoded expected Java source, so it needed its expectations regenerated to match. No generator or production logic changed; only the literal expected strings were regenerated from a passing test run's actual output. --------- Co-authored-by: Simon Cockx <47859223+SimonCockx@users.noreply.github.com>
Migrated generators had been dropped from the "Remaining Xtend files" checklist entirely rather than ticked off (FunctionGenerator, ReportGenerator and RuleGenerator in finos#1293, CardinalityValidatorGenerator in this PR). Bring all four back with [x], matching how every other completed item in the plan is tracked.
…erator and TypeFormatValidatorGenerator to Java and migrate them to the fluent API (#1377) * Convert CardinalityValidatorGenerator to Java and migrate it to the fluent API Move CardinalityValidatorGenerator off the deprecated Xtend template machinery onto FluentRObjectJavaClassGenerator, rendering the validator class with the fluent CodeRenderer/CodeWriter API. AbstractValidatorGenerator is left untouched since OnlyExistsValidatorGenerator and TypeFormatValidatorGenerator still depend on its StringConcatenationClient contract; the small streamObjects/createTypeRepresentation logic is duplicated here instead. Also correct the migrate-xtend-generator skill's pitfalls section, which still described CodeWriterTargetStringConcatenation and TargetStringConcatenationCodeWriter as living inside JavaExpression -- they were extracted into standalone classes under generator/java/util/ in #1293. * Use static imports for ValidationResult/checkCardinality calls Resolve the handful of static method calls the generator writes (isNullOrEmpty, checkCardinality, ValidationResult.success/failure, Collectors.toList) as java.lang.reflect.Method objects instead of Class + literal method name, so RecordingCodeWriter registers static imports for them instead of regular ones. This keeps the regression fixture diffs to whitespace only against the pre-migration Xtend output, matching how the generator previously rendered these calls. * Convert OnlyExistsValidatorGenerator to Java and migrate it to the fluent API * Convert TypeFormatValidatorGenerator to Java and migrate it to the fluent API AbstractValidatorGenerator is deleted as this was its last subclass. Regenerated *TypeFormatValidator.java expectation files reflect only the fluent writer's whitespace conventions (4-space indent, no trailing whitespace), verified with `git diff --ignore-all-space`.
…nd TypeFormatValidatorGenerator to Java and migrate them to the fluent API (#1376) * Convert CardinalityValidatorGenerator to Java and migrate it to the fluent API Move CardinalityValidatorGenerator off the deprecated Xtend template machinery onto FluentRObjectJavaClassGenerator, rendering the validator class with the fluent CodeRenderer/CodeWriter API. AbstractValidatorGenerator is left untouched since OnlyExistsValidatorGenerator and TypeFormatValidatorGenerator still depend on its StringConcatenationClient contract; the small streamObjects/createTypeRepresentation logic is duplicated here instead. Also correct the migrate-xtend-generator skill's pitfalls section, which still described CodeWriterTargetStringConcatenation and TargetStringConcatenationCodeWriter as living inside JavaExpression -- they were extracted into standalone classes under generator/java/util/ in #1293. * Use static imports for ValidationResult/checkCardinality calls Resolve the handful of static method calls the generator writes (isNullOrEmpty, checkCardinality, ValidationResult.success/failure, Collectors.toList) as java.lang.reflect.Method objects instead of Class + literal method name, so RecordingCodeWriter registers static imports for them instead of regular ones. This keeps the regression fixture diffs to whitespace only against the pre-migration Xtend output, matching how the generator previously rendered these calls. * Restore checked-off rows to the xtend migration plan Migrated generators had been dropped from the "Remaining Xtend files" checklist entirely rather than ticked off (FunctionGenerator, ReportGenerator and RuleGenerator in #1293, CardinalityValidatorGenerator in this PR). Bring all four back with [x], matching how every other completed item in the plan is tracked. * Convert OnlyExistsValidatorGenerator to Java and migrate it to the fluent API * Convert TypeFormatValidatorGenerator to Java and migrate it to the fluent API AbstractValidatorGenerator is deleted as this was its last subclass. Regenerated *TypeFormatValidator.java expectation files reflect only the fluent writer's whitespace conventions (4-space indent, no trailing whitespace), verified with `git diff --ignore-all-space`.
Continues the Xtend → Java migration of the code generators, moving them onto the fluent
CodeRenderer/CodeWriterAPI. Migrates the report generators (RuleGenerator,ReportGenerator) and the big one,FunctionGenerator.What changed
RuleGenerator,ReportGenerator,FunctionGenerator: converted.xtend→.javaand moved ontoFluentRObjectJavaClassGenerator;generateClass/rBuildClassnow return aCodeRenderer.FunctionGeneratoris rendered with the fluentCodeWriterAPI (out.writeln/out.indented/…) instead of Xtend'''…'''templates. Its annotation map and the report annotations are nowCodeRenderers.FunctionGenerator.rBuildClassreturns aCodeRenderer,RuleGenerator/ReportGeneratorno longer wrap its result — the call-site bridging is gone.Bridges
The migration is incremental, so two small bridges — both extracted out of
JavaExpression, counterparts of each other — let fluent generators interoperate with code that still produces XtendStringConcatenationClients:CodeWriterTargetStringConcatenation— a streaming bridge that forwards each appended object to theCodeWriter, so aRecordingCodeWriterregisters imports and defers identifier resolution (keeping the scope open).FunctionGeneratoruses it to embed the few fragments still generated as templates by not-yet-migrated code (method parameter lists, the nested class declaration, alias call arguments).TargetStringConcatenationCodeWriter— the reverse bridge: it exposes aCodeWriterthat appends into the legacy Xtend template machinery, so a value produced by the fluent API can still be embedded in a not-yet-migrated Xtend template (e.g.RGeneratedJavaClass#asClassDeclaration, used by generators that haven't moved to the fluent API yet).Both are to be removed once all generators use the fluent API.
FluentImportManager(new) is the fluent counterpart ofImportManagerExtension: given a class body as aCodeRenderer, it assembles the full file (package, resolved imports, body).FluentJavaClassGeneratorinjects it rather than exposing its own file-assembly logic as a method on the generator;FunctionGeneratorHelper, which needs to drive single-class generation directly to assert on one function's output, injects it the same way the not-yet-migrated tests (TypeCoercionTest,ExpressionGeneratorTest) injectImportManagerExtension.Output formatting
The fluent
CodeWriterdefaults to four-space indentation and emits no trailing whitespace, so generated function/rule code is reformatted accordingly (tabs → spaces, no trailing whitespace, conventional Javadoc). The affected expectations are updated; behaviour is unchanged — all generated code still compiles and runs.Testing
Full multi-module build passes (
mvn install), 0 Checkstyle violations. The fullrune-integration-testssuite passes (1537 tests, 0 failures), including the byte-comparison generation regression tests and the 147FunctionGeneratorTestcases (which compile and execute the generated functions).