Skip to content

refactor: migrate Xtend to Java - com.avaloq.tools.ddk.xtext.export - #1518

Merged
joaodinissf merged 2 commits into
masterfrom
migrate/xtend-to-java/export-step-2
Sep 8, 2026
Merged

joaodinissf merged 2 commits into
masterfrom
migrate/xtend-to-java/export-step-2

Conversation

@joaodinissf

Copy link
Copy Markdown
Collaborator

Summary

Migrates the last legacy Xtend source of com.avaloq.tools.ddk.xtext.export to Java 21: generator/ExportGeneratorX — the naming, prefix, supertype/interface lookup and user-data helpers the export inferrer uses.

The module keeps its Xbase layer in jvmmodel/*.xtend, so the Xtend build infrastructure (xtend-gen, .classpath, build.properties) is untouched.

Stacked on the xtext.expression migration (this class injects its Naming).

Public API is unchanged. The sole consumer is ExportJvmModelInferrer.xtend, which binds through @Inject extension — allUserData(...), constantName(...), typeMap(...), sortedExportsByEPackage(...), getSuperInterfaces(...), exportForType(...) and the get* provider-name helpers. It compiles unchanged against the Java. (superType and getName(ExportModel) are reached only from inside this class; model.name in the inferrer resolves to ExportModel's own EMF getter, which takes priority over a same-named extension.)

Faithfulness notes

  • Dispatcher allUserData(Export) keeps the compiler's shape: protected _allUserData(Export) / _allUserData(Void), non-null branch first, Void sentinel second, no terminal exception (as in the compiler output); the recursion through the public dispatcher is preserved.
  • constantName(EAttribute, EClass) / constantName(UserData, EClass) and getResourceDescriptionManager(ExportModel) / (Grammar) stay plain overloads.
  • getGrammar keeps IterableExtensions.head semantics via Guava Iterables.getFirst(contents, null) (Guava is already a dependency through ListMultimap): empty contents yield null, the vestigial ?. guard is preserved.
  • exportForType uses Stream.filter(...).findFirst(), matching IterableExtensions.findFirst. getInterfacesForType collects eagerly, matching the compiler output: Interface.getType() resolves EMF proxies in place, so evaluating the predicate over every declaration is an observable side effect rather than wasted work.
  • Guava Function lambdas → Export::getType method references; newArrayList() → new ArrayList<>() (results stay mutable, as the callers rely on). Xtend == → Objects.equals, matching the compiler output.
  • Javadoc preserved verbatim; only @param/@return tags and two sentence-ending periods were added to Javadoc that already existed, because Checkstyle enforces them on .java and never saw the .xtend. No Javadoc was invented for undocumented methods. The literal 3 in getPrefix became the named constant FIRST_PACKAGE_SEGMENT.

Known deltas

  • The Xtend compiler stamped @XbaseGenerated on the generated dispatchers; the migrated Java does not. The annotation is runtime-retained on public methods in an exported package, so it is a reflectively observable delta — but nothing in the target platform reads it (only JvmModelGenerator emits it), and re-applying a "machine-generated" marker to hand-written code would be false.

Verification

  • Ground truth: fresh xtend-gen from a clean build of the base commit.
  • Two independent translations were produced and reconciled against the compiler output. An independent review of the reconciled result then caught one fidelity-relevant difference — getInterfacesForType had been left short-circuiting where the compiler output evaluates the predicate over every declaration — which is fixed above; the other translation had it right. Remaining differences between the two were style.
  • Bytecode comparison against the compiler's classes: public/protected API identical; ordered string constants per method identical; dispatcher branch order identical. Only private members differ (the removed compiler lambdas).
  • Gates: -amd compile (rebuilds the Xtend caller against the Java) ✅; pmd:check checkstyle:check spotbugs:check ✅; full reactor clean verify with the CI static-analysis goals ✅ (354 tests, 0 failures, 2 skipped). No version bump needed: the bundle is already at 17.3.3, ahead of the baseline.

🤖 Generated with Claude Code

rubenporras
rubenporras previously approved these changes Sep 7, 2026
@joaodinissf
joaodinissf marked this pull request as ready for review September 7, 2026 10:37
Base automatically changed from migrate/xtend-to-java/expression-step-1 to master September 7, 2026 21:28
@joaodinissf
joaodinissf force-pushed the migrate/xtend-to-java/export-step-2 branch from 45056db to c5c406b Compare September 7, 2026 21:28
joaodinissf and others added 2 commits September 8, 2026 09:36
…1/2: rename sources)

Pure `git mv` of ExportGeneratorX.xtend to ExportGeneratorX.java with the
file content unchanged, so Git's rename detection permanently connects the
Java history to its Xtend past (`git log --follow`, `git blame`).

This commit intentionally does not compile: the renamed file still holds
Xtend syntax. The following commit translates it in place.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…2/2: translate to Java 21)

In-place translation of the renamed ExportGeneratorX, faithful to the
Xtend compiler's own xtend-gen output (fresh ground-truth build off the
base commit, byte-identical to the reference).

Public API is unchanged: every method keeps its name, parameter types
and order, and return type, including the `allUserData(Export)`
dispatcher over `_allUserData(Export)` / `_allUserData(Void)` with the
same branch order (non-null first, null sentinel second). The sole
caller, ExportJvmModelInferrer.xtend, still binds against it as an
`@Inject extension`; the -amd build recompiles it.

Faithfulness notes:
- `@Inject extension Naming` becomes `@Inject private Naming naming`
  with explicit-receiver calls (getResourceDescriptionManager(Grammar)).
- getGrammar keeps IterableExtensions.head semantics exactly via Guava
  `Iterables.getFirst(contents, null)`: empty contents yield null, and a
  null contents (only reachable if the `?.` guard fires) throws, as
  before.
- Xtend `==` on objects becomes `Objects.equals`, matching xtend-gen.
- `newArrayList()` becomes `new ArrayList<>()` — the results stay
  modifiable, which _allUserData and getInterfacesForType rely on.
- Class/member Javadoc is preserved verbatim; the only additions are the
  `@param`/`@return` tags and two sentence-ending periods that checkstyle
  JavadocMethod/SummaryJavadoc require on Javadoc that already exists.
  No Javadoc was invented for previously undocumented methods.
- The literal 3 in getPrefix becomes the named constant
  FIRST_PACKAGE_SEGMENT (checkstyle MagicNumber) rather than a
  suppression comment.

Documented deviations (behaviour-identical):
- exportForType uses Stream filter/findFirst instead of
  IterableExtensions.findFirst; both stop at the first match.
  getInterfacesForType collects the filtered stream eagerly, matching the
  compiler output: Interface.getType() resolves EMF proxies in place, so
  evaluating the predicate over every declaration is an observable side
  effect and not merely wasted work.
- The Guava Function lambdas of sortedExportsByEPackage/typeMap become
  the `Export::getType` method reference.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@joaodinissf
joaodinissf force-pushed the migrate/xtend-to-java/export-step-2 branch from c5c406b to 57da560 Compare September 8, 2026 07:36
@joaodinissf
joaodinissf merged commit a50225a into master Sep 8, 2026
4 checks passed
@joaodinissf
joaodinissf deleted the migrate/xtend-to-java/export-step-2 branch September 8, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants