Skip to content

refactor: migrate Xtend to Java - com.avaloq.tools.ddk.xtext.expression - #1517

Merged
joaodinissf merged 3 commits into
masterfrom
migrate/xtend-to-java/expression-step-1
Sep 7, 2026
Merged

joaodinissf merged 3 commits into
masterfrom
migrate/xtend-to-java/expression-step-1

Conversation

@joaodinissf

Copy link
Copy Markdown
Collaborator

Summary

Migrates the three remaining legacy Xtend sources of com.avaloq.tools.ddk.xtext.expression to Java 21:

  • generator/GenModelUtilX — GenModel/EMF-registry lookups, the literalIdentifier and instanceClassName dispatch families, the per-thread resource context
  • generator/GeneratorUtilX — contributor-comment and grammar helpers
  • generator/Naming — qualified-name helpers

The module keeps jvmmodel/ExpressionJvmModelInferrer.xtend, so the Xtend build infrastructure (xtend-gen, .classpath, build.properties) is untouched. For the Tycho baseline gate, the bundle is micro-bumped to 17.3.3 and the runtime feature that includes it to 19.1.1 (content changed, API identical).

Public API is unchanged. These classes are consumed only by the Xtend inferrers in xtext.export and xtext.scope, which bind by dispatcher name (literalIdentifier(ENamedElement), instanceClassName(EClassifier)), by extension syntax (javaContributorComment, location, canContain, toJavaPackage) and by the property write genModelUtil.resource = … → setResource(Resource). All of them compile unchanged against the Java.

Faithfulness notes

  • Dispatchers keep the compiler's shape: protected _name(Type) cases including the Void overload as the null case, and a public dispatcher with the compiler's branch order (EClass → EStructuralFeature/EDataType → supertype → null). As in the compiler output there is no terminal IllegalArgumentException: the Void case makes both dispatchers total.
  • ThreadLocal<Resource> context and setResource/getContext are unchanged.
  • xbase.lib replacements preserve exact semantics rather than the idiomatic short form: firstInterface() reproduces IterableExtensions.head returning null on an empty interface array (so the failure mode on an interface-less EPackage subclass is unchanged), and toFirstUpper() reproduces StringExtensions.toFirstUpper including its null/empty/already-upper pass-throughs. Xtend ==/!= on objects → Objects.equals; identity !== stays !=. The lazy filter feeding the Xcore candidate loop became a guard inside the loop (same elements, same order).
  • Dropped: the vestigial /*cached*/ markers on fifteen defs (remnants of former create methods, no behaviour, already absent from the compiler output). Every other comment and Javadoc is preserved verbatim; the only new Javadoc documents the two private helpers.
  • Repeated literals extracted to constants (DOES_NOT_EXIST, DOT) for Checkstyle; values unchanged. Parameters named it renamed per the migration skill.

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.
  • Runtime coverage boundary: with a standalone ResourceSet the .genmodel index is empty, so genPackage() always returns null and the registry/heuristic fallbacks are taken. The isLiteralsInterface() branches of _literalIdentifier and qualifiedSwitchClassName, and genClass/genDataType/genModel, are therefore exercised only in a workspace build, not by the differential above.

Verification

  • Ground truth: fresh xtend-gen from a clean build of the Xtend sources as they stood at the point the branch was cut. The branch has since been rebased onto a Checkstyle version bump, which cannot affect the Xtend compiler's output, so the reference still holds; the gates below were re-run after the rebase.
  • Two independent translations were produced and reconciled against the compiler output; they differed only in style (parameter names, if-chain vs pattern switch, constant extraction, one Guava-vs-local-helper choice). No fidelity-relevant discrepancy.
  • Bytecode comparison of the final classes against the compiler's: public/protected API identical; ordered string constants per method identical; dispatcher branch order identical. Only private members differ (the two helpers replacing xbase.lib calls, the extracted constants, the removed lambda).
  • Gates: -amd compile (rebuilds the export/scope Xtend callers) ✅; pmd:check checkstyle:check spotbugs:check ✅; full reactor clean verify with the CI static-analysis goals ✅ (354 tests, 0 failures, 2 skipped).

🤖 Generated with Claude Code

joaodinissf and others added 3 commits September 4, 2026 21:24
…on (1/2: rename sources)

Pure `git mv` of the three generator sources from .xtend to .java with
their content unchanged, so Git's rename detection permanently links the
Java history to its Xtend past (git log --follow, git blame).

This commit intentionally does not compile: the renamed files still hold
Xtend syntax. The following commit rewrites them in place to Java 21.

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

In-place translation of the three renamed generator sources, faithful to
the Xtend compiler's own xtend-gen output (fresh ground-truth build off
the base commit, byte-identical to the reference). The module keeps
ExpressionJvmModelInferrer.xtend, so the Xtend build infrastructure
stays and there is no infrastructure commit.

Public API is unchanged: the export and scope jvmmodel inferrers bind by
dispatcher name (literalIdentifier(ENamedElement),
instanceClassName(EClassifier)), by extension syntax
(javaContributorComment, location, canContain, toJavaPackage) and by the
Xtend property write `genModelUtil.resource = ...` (setResource(Resource)),
all of which compile green with -amd.

Notable per-file points:
- GenModelUtilX: the dispatch cases keep their `_` prefix, their
  protected visibility, the Void overload as the null case and the
  reference's branch order (EClass, EStructuralFeature/EDataType,
  supertype, null). The dispatchers are Java 21 pattern switches; like
  xtend-gen they have no terminal IllegalArgumentException because the
  Void case makes them total.
- GenModelUtilX: the ThreadLocal<Resource> context field stays a
  ThreadLocal, and the `_literalIdentifier(EStructuralFeature)` literals
  branch keeps calling the public dispatcher (not the _ case) for the
  containing EClass, exactly as xtend-gen does.
- GenModelUtilX: two private helpers replace xbase.lib calls with their
  exact semantics rather than the lossy idiomatic form -
  firstInterface() reproduces IterableExtensions.head returning null on
  an empty interface array (so an EPackage subclass declaring no
  interface still fails with an NPE, not an out-of-bounds), and
  toFirstUpper() reproduces StringExtensions.toFirstUpper including its
  null, empty and already-upper-case pass-throughs.
- GenModelUtilX: Xtend `==`/`!=` on objects is translated to
  Objects.equals as xtend-gen did; the identity `!==` comparisons stay
  reference comparisons. The lazy IterableExtensions.filter feeding the
  Xcore candidate loop becomes a guard inside that loop, which iterates
  the same elements in the same order.
- Naming / GeneratorUtilX: plain `+` concatenation preserved (no
  templates in these files); the Avaloq banner header is applied to all
  three files per the migration skill, replacing the two files' missing
  header.
- One comment deviation: the vestigial `/*cached*/` markers the Xtend
  source carried on fifteen `def`s (remnants of former Xtend `create`
  methods, carrying no behaviour and already absent from xtend-gen) are
  dropped. Every other comment and Javadoc is preserved verbatim, and no
  Javadoc is invented - the only new Javadoc documents the two new
  private helpers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…(content changed vs baseline)

The compare-version-with-baselines gate rejects the migrated bundle and
the runtime feature that includes it: their content differs from the
19.1.0 baseline while the versions were unchanged. Micro bumps per OSGi
conventions; the public API is identical. com.avaloq.tools.ddk.feature
is already at 19.1.1 on master.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@joaodinissf
joaodinissf marked this pull request as ready for review September 7, 2026 10:37
@joaodinissf
joaodinissf merged commit 667b840 into master Sep 7, 2026
4 checks passed
@joaodinissf
joaodinissf deleted the migrate/xtend-to-java/expression-step-1 branch September 7, 2026 21:28
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