diff --git a/.claude/migration-verification.md b/.claude/migration-verification.md new file mode 100644 index 0000000000..f91d1efe64 --- /dev/null +++ b/.claude/migration-verification.md @@ -0,0 +1,220 @@ +# Migration Verification Ledger + +Tracks the byte-by-byte / faithfulness verification status of every Xtend→Java migrated file. +Local working doc — **never commit** (lives under untracked `.claude/`). Last updated 2026-06-25. + +## Why two kinds of "vetted" +These migrations include **code generators** whose emitted text becomes committed `src-gen`; for those, output must be **byte-identical** to what the Xtend compiler produced (`xtend-gen`) — CI cannot catch generator drift (no regen in the build). Non-generator files (tests, plain logic) emit no generated text, so byte-vetting is N/A; they need only **behavioural faithfulness**. + +## Verification tiers (strongest → weakest) +- **HARNESS** — executable `StringConcatenation` byte-equality harness vs `xtend-gen` (gold standard; `.claude/coalesce-verify/*.java`) +- **APPEND-DIFF** — append-for-append comparison of migrated `.java` vs `xtend-gen` (authoritative for emitted bytes) +- **FAITHFULNESS** — behavioural read of logic/test files (emit no generated text → byte-vet N/A) +- **SWEEP** — 51-agent ultracode faithfulness review (the merged batch, read-based) +- **NONE** — not yet vetted + +--- + +## A. Generators (emit committed text) — BYTE-VETTED + +| File | PR | Tier | Result | +|------|----|------|--------| +| `LspBuilderIntegrationFragment2` | #1430 | HARNESS (`Probe`) | IDENTICAL | +| `StandaloneBuilderIntegrationFragment2` | #1430 | HARNESS (`SB`) | IDENTICAL | +| `FormatterFragment2` | #1430 | HARNESS (`FF`) | IDENTICAL | +| `LanguageConstantsFragment2` | #1430 | HARNESS (`LC`) | IDENTICAL | +| `AnnotationAwareContentAssistFragment2` | #1430 | HARNESS (`AACA`) | **DRIFT found → fixed in #1446** | +| `ResourceFactoryFragment2` | #1430 | APPEND-DIFF | IDENTICAL | +| `BuilderIntegrationFragment2` | #1430 | APPEND-DIFF (per-pair) | IDENTICAL | +| `BundleVersionStripperFragment`, `DefaultFragmentWithOverride`, `ProjectConfig`, `ModelInferenceFragment2`, `CompareFragment2` | #1430 | APPEND-DIFF / per-pair read | IDENTICAL | +| `FormatGenerator` | #1428 | APPEND-DIFF (Phase 2) | IDENTICAL | +| `FormatJvmModelInferrer` | #1428 | APPEND-DIFF + deep-dive | `getDirectiveName` Group0 bug → fixed via A0/#1443; `List.of`→`Arrays.asList` → #1446 | +| `FormatFragment2` (format.generator) | #1428-era | APPEND-DIFF (Phase 2) | IDENTICAL | +| `CheckQuickfixProviderFragment2` (check.generator) | merged | APPEND-DIFF (Phase 2) | IDENTICAL | +| `CheckCfgGenerator` (checkcfg.core) | merged | APPEND-DIFF (Phase 2) | IDENTICAL | +| `AbstractAnnotationAwareAntlrGrammarGenerator` | #1429 | APPEND-DIFF (manual) | IDENTICAL | +| `AnnotationAwareAntlrGrammarGenerator` | #1429 | HARNESS (`AAGG1`/`AAGG2`) | IDENTICAL | +| `AnnotationAwareAntlrContentAssistGrammarGenerator` | #1429 | HARNESS (`CA1`) + APPEND-DIFF (agent, empirical StringConcatenation) | IDENTICAL (incl. uncommitted batch-1) | +| `AnnotationAwareXtextAntlrGeneratorFragment2` | #1429 | APPEND-DIFF (agent) | IDENTICAL | + +**Every byte-output generator across the campaign is byte-vetted.** The only confirmed generator drift (`AnnotationAware`) is fixed in #1446; the only generator behavioural defect (`getDirectiveName`) was split out and merged as #1443. + +## B. Logic / test files — FAITHFULNESS-VETTED (byte-vet N/A) + +- **#1429**: `GrammarRuleAnnotations`, `PredicatesNaming` — workflow agents, logic faithful. +- **#1427 check.core.test (11)**: per-pair read by me — all OK (1 nit, §D). +- **Merged batch (~31)** — checkcfg.core(.test), sample.helloworld, xtext.ui, xtext.scope/export generators, format.ide/ui, check.test.runtime, check/format .test, xtext.check.generator — **SWEEP** (51-agent ultracode), all CLEAR (1 nit, §D). Generators within this batch were additionally APPEND-DIFF'd (§A). + +## C. Pure deletions — N/A (no `.java` counterpart, not migrations) +- `AbstractResourceDescriptionManagerTest.xtend` (dead JUnit4 infra, `d920b3bad`) +- `CheckNewProject.xtend`, `CheckQuickfixProvider.xtend` (wizard removal, `06de65661`) + +## D. Known divergences (all fixed or benign) +| File | Divergence | Status | +|------|-----------|--------| +| `AnnotationAwareContentAssistFragment2` | multi-line `Alternatives` continuation-indent drift | **fixed in #1446** | +| `FormatJvmModelInferrer.getDirectiveName` | `Group0` varargs-trap bug | **fixed, merged #1443** | +| `FormatJvmModelInferrer` dispatcher throws | `List.of` null-intolerant; empty-`[]` noise | **fixed in #1446** (9 → `Arrays.asList`, 3 empty simplified) | +| `CheckModelUtil.modelWithContexts` (#1427) | trailing `"\n "` vs Xtend | **deferred** — only caller is `@Disabled`; near-zero impact | +| `CheckCfgScopeProviderTest` (checkcfg.core.test) | `NPE`→`IllegalStateException` (same msg/outcome) | **kept** — intentional quality improvement, documented | + +## E. Not yet byte-vetted +- **No generator remains unvetted.** Non-generator merged files have FAITHFULNESS/SWEEP coverage only (byte-vet N/A — they emit no generated text). +- **#1429 is the last open migration** — all 6 of its files are now byte-/faithfulness-vetted (§A/§B); pending: Ruben re-review of the remaining "multi-line string" comments + the `:90` decision. + +--- + +## F. Verification machinery — how to (re)trigger + +### Ground truth: `xtend-gen` +The authoritative byte target is **`xtend-gen/`** — the Xtend compiler's own `.java` output. After a `.xtend` is migrated (deleted), its `xtend-gen` is gone from that branch, but **sibling worktrees on older commits still have it**: +- locate: `find /Users/joao/Git/Avaloq/dsl-devkit* -path '*xtend-gen*.java'` +- original `.xtend`: `git show ^:` (or `upstream/master:` while the PR is open) +- **validity gates (always check):** (a) the worktree's `.xtend` is byte-identical to the migrated original (`diff`), and (b) `xtend-gen` mtime ≥ `.xtend` mtime (fresh, i.e. built from that source). + +### Method 1 — executable HARNESS (gold standard) — `.claude/coalesce-verify/*.java` +Lift BOTH the original append sequence (a real `new StringConcatenation("\n")`, dynamic values as params) and the candidate (text block `.formatted(...)`) into a tiny Java program; assert `old.toString().equals(neu)` over an input battery (empty / multi-line / single-line / quotes / `%` / unicode / stub-vs-non-stub / options-vs-single). Compile/run: +``` +JAR=~/.m2/repository/org/eclipse/xtext/org.eclipse.xtext.xbase.lib/2.43.0/org.eclipse.xtext.xbase.lib-2.43.0.jar +javac -cp "$JAR" -d . X.java && java -cp "$JAR:." X +``` +Existing harnesses: `Probe, SB, FF, LC, AACA, AAGG1, AAGG2, CA1`. Use real `StringConcatenation`/`StringConcatenationClient` (not a re-implementation) so two-arg `append(v,indent)` re-indent + `newLineIfNotEmpty` semantics are exact. `AACA` is the template that caught the only real drift (two-arg→single-arg on a multi-line value). + +### Method 2 — ULTRACODE Opus workflow (scale) — `Workflow({scriptPath})` +Parallel Opus agents, one per file, each comparing migrated `.java` vs `xtend-gen` **append-for-append**, then an adversarial **refute-by-default** pass; schema-validated structured output; `pipeline(review → refute)`; `model: 'opus'`; **FIND-ONLY (read-only, no side effects).** +- `.claude/wf-sweep.js` — the 51-file merged-migration sweep (CFG `.claude/sweep-cfg.json`, built from `git log --diff-filter=D --name-only -- '*.xtend'`). +- `.claude/wf-p1429.js` — the 4 remaining #1429 generators/logic. +- re-run/iterate: `Workflow({scriptPath, resumeFromRunId})` (completed agents return cached results). + +**The drift checklist baked into the agent prompt (and what to look for manually):** +1. **two-arg `append(value,indent)` vs single-arg** — single-arg drops re-indentation of *continuation lines* of a MULTI-LINE value (the `AnnotationAware` bug). For every two-arg in `xtend-gen`, confirm migrated matches (or value is provably single-line). +2. trailing whitespace from loop bodies (`«FOR»` strips last-element trailing; a `for` appending `"\n "` does not — the `modelWithContexts` bug). +3. `newLine()` vs `newLineIfNotEmpty()` (differ on empty/whitespace-only lines). +4. `StringBuilder "\n"` vs `StringConcatenation` default delimiter. +5. Java text-block stripping (min indent incl. closing delim + trailing ws) vs Xtend rich-string min-content-indent stripping. +6. import-managed `append(TypeReference/Class)` folded into a `%s` text block (output/import drift). +7. `List.of` (null-intolerant) vs `Arrays.asList` (xtend-gen). +**Benign — NOT findings:** `xtend-gen` splits `append(" ")+append("x")` vs migrated combined `append(" x")`; `_xxx` temp vars vs inlined calls; source-style/whitespace that doesn't change emitted bytes. + +### Adjudication rule +Agent CLEAR is not final for generators — the only proofs that *caught* drift were the executable harness + append-for-append. Treat agent verdicts as triage; **harness-confirm any non-trivial generator** (esp. multi-line two-arg cases) before trusting CLEAR. + +--- + +## G. Complete file inventory (all 69 deleted `.xtend`, every file recorded) + +*Auto-derived 2026-06-25 from `git log --diff-filter=D` on master + the open #1429 branch. 66 migrated + 3 deletions.* +Tier totals: APPEND-DIFF=14, DELETION (not a migration)=3, FAITHFULNESS (binding-only, no text)=3, FAITHFULNESS (per-pair)=11, FAITHFULNESS (workflow)=2, HARNESS=7, SWEEP=29 + + +**com.avaloq.tools.ddk.check.core.test** (11) +- `IssueCodeValueTest.xtend` — FAITHFULNESS (per-pair) +- `BasicModelTest.xtend` — FAITHFULNESS (per-pair) +- `BugAig830.xtend` — FAITHFULNESS (per-pair) +- `CheckScopingTest.xtend` — FAITHFULNESS (per-pair) +- `IssueCodeToLabelMapGenerationTest.xtend` — FAITHFULNESS (per-pair) +- `ProjectBasedTests.xtend` — FAITHFULNESS (per-pair) +- `CheckModelUtil.xtend` — FAITHFULNESS (per-pair) +- `CheckTestUtil.xtend` — FAITHFULNESS (per-pair) +- `CheckFormattingTest.xtend` — FAITHFULNESS (per-pair) +- `CheckApiAccessValidationsTest.xtend` — FAITHFULNESS (per-pair) +- `CheckValidationTest.xtend` — FAITHFULNESS (per-pair) + +**com.avaloq.tools.ddk.check.test.runtime** (1) +- `TestLanguageGenerator.xtend` — SWEEP + +**com.avaloq.tools.ddk.check.test.runtime.tests** (3) +- `CheckConfigurationIsAppliedTest.xtend` — SWEEP +- `CheckExecutionEnvironmentProjectTest.xtend` — SWEEP +- `IssueLabelTest.xtend` — SWEEP + +**com.avaloq.tools.ddk.check.ui** (2) +- `CheckNewProject.xtend` — DELETION (not a migration) +- `CheckQuickfixProvider.xtend` — DELETION (not a migration) + +**com.avaloq.tools.ddk.check.ui.test** (1) +- `CheckQuickfixTest.xtend` — SWEEP + +**com.avaloq.tools.ddk.checkcfg.core** (4) +- `CheckCfgGenerator.xtend` — APPEND-DIFF +- `CheckCfgJvmModelInferrer.xtend` — SWEEP +- `PropertiesInferenceHelper.xtend` — SWEEP +- `ConfiguredParameterChecks.xtend` — SWEEP + +**com.avaloq.tools.ddk.checkcfg.core.test** (7) +- `CheckCfgContentAssistTest.xtend` — SWEEP +- `CheckCfgScopeProviderTest.xtend` — SWEEP +- `CheckCfgSyntaxTest.xtend` — SWEEP +- `CheckCfgModelUtil.xtend` — SWEEP +- `CheckCfgTestUtil.xtend` — SWEEP +- `CheckCfgConfiguredParameterValidationsTest.xtend` — SWEEP +- `CheckCfgTest.xtend` — SWEEP + +**com.avaloq.tools.ddk.sample.helloworld.ui.test** (3) +- `CheckConfigurationIsAppliedTest.xtend` — SWEEP +- `CheckExecutionEnvironmentProjectTest.xtend` — SWEEP +- `IssueLabelTest.xtend` — SWEEP + +**com.avaloq.tools.ddk.xtext.check.generator** (2) +- `CheckValidatorFragment2.xtend` — FAITHFULNESS (binding-only, no text) +- `CheckQuickfixProviderFragment2.xtend` — APPEND-DIFF + +**com.avaloq.tools.ddk.xtext.export.generator** (1) +- `ExportFragment2.xtend` — FAITHFULNESS (binding-only, no text) + +**com.avaloq.tools.ddk.xtext.format** (6) +- `FormatRuntimeModule.xtend` — SWEEP +- `FormatStandaloneSetup.xtend` — SWEEP +- `FormatGenerator.xtend` — APPEND-DIFF +- `FormatJvmModelInferrer.xtend` — APPEND-DIFF +- `FormatScopeProvider.xtend` — SWEEP +- `FormatValidator.xtend` — SWEEP + +**com.avaloq.tools.ddk.xtext.format.generator** (1) +- `FormatFragment2.xtend` — APPEND-DIFF + +**com.avaloq.tools.ddk.xtext.format.ide** (2) +- `FormatIdeModule.xtend` — SWEEP +- `FormatIdeSetup.xtend` — SWEEP + +**com.avaloq.tools.ddk.xtext.format.test** (1) +- `FormatParsingTest.xtend` — SWEEP + +**com.avaloq.tools.ddk.xtext.format.ui** (1) +- `FormatUiModule.xtend` — SWEEP + +**com.avaloq.tools.ddk.xtext.generator** (18) +- `BundleVersionStripperFragment.xtend` — APPEND-DIFF +- `DefaultFragmentWithOverride.xtend` — APPEND-DIFF +- `BuilderIntegrationFragment2.xtend` — APPEND-DIFF +- `LspBuilderIntegrationFragment2.xtend` — HARNESS +- `StandaloneBuilderIntegrationFragment2.xtend` — HARNESS +- `FormatterFragment2.xtend` — HARNESS +- `LanguageConstantsFragment2.xtend` — HARNESS +- `ProjectConfig.xtend` — APPEND-DIFF +- `ModelInferenceFragment2.xtend` — APPEND-DIFF +- `AbstractAnnotationAwareAntlrGrammarGenerator.xtend` — APPEND-DIFF +- `AnnotationAwareAntlrContentAssistGrammarGenerator.xtend` — HARNESS +- `AnnotationAwareAntlrGrammarGenerator.xtend` — HARNESS +- `AnnotationAwareXtextAntlrGeneratorFragment2.xtend` — APPEND-DIFF +- `GrammarRuleAnnotations.xtend` — FAITHFULNESS (workflow) +- `PredicatesNaming.xtend` — FAITHFULNESS (workflow) +- `ResourceFactoryFragment2.xtend` — APPEND-DIFF +- `CompareFragment2.xtend` — APPEND-DIFF +- `AnnotationAwareContentAssistFragment2.xtend` — HARNESS + +**com.avaloq.tools.ddk.xtext.generator.test** (1) +- `XbaseGeneratorFragmentTest.xtend` — SWEEP + +**com.avaloq.tools.ddk.xtext.scope.generator** (1) +- `ScopingFragment2.xtend` — FAITHFULNESS (binding-only, no text) + +**com.avaloq.tools.ddk.xtext.test.core** (1) +- `AbstractResourceDescriptionManagerTest.xtend` — DELETION (not a migration) + +**com.avaloq.tools.ddk.xtext.ui** (1) +- `TemplateProposalProviderHelper.xtend` — SWEEP + +**com.avaloq.tools.ddk.xtext.ui.test** (1) +- `TemplateProposalProviderHelperTest.xtend` — SWEEP + diff --git a/.gitignore b/.gitignore index b71c920bc2..e39e074afb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,6 @@ *.java._trace *.smap *.checkbin -*.xtendbin -/*/xtend-gen/* -!/*/xtend-gen/.gitignore # macOS .DS_Store diff --git a/AGENTS.md b/AGENTS.md index 42c3653354..efe52940e2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,7 +9,7 @@ This document helps AI coding agents work effectively with the DSL DevKit codeba - **Java**: 21+ - **Maven** - **Tycho** -- **Xtext/Xtend** +- **Xtext** ## Setup @@ -55,7 +55,7 @@ mvn checkstyle:check pmd:check spotbugs:check -f ./ddk-parent/pom.xml ### PMD - **Ruleset**: `ddk-configuration/pmd/ruleset.xml` -- Excludes: `src-gen/`, `src-model/`, `xtend-gen/` +- Excludes: `src-gen/`, `src-model/` ### Checkstyle - **Config**: `ddk-configuration/checkstyle/avaloq.xml` @@ -109,10 +109,6 @@ The project runs **all tests through one aggregator module**, not per-`.test`-mo These directories contain generated code - do not edit manually: - `src-gen/` - Xtext generated sources - `src-model/` - EMF model generated sources -- `xtend-gen/` - Xtend transpiled Java sources - -### Xtend -- `.xtend` files in `src/` compile to Java in `xtend-gen/` ## Common Tasks @@ -125,7 +121,7 @@ These directories contain generated code - do not edit manually: ### Fixing PMD Violations 1. Check ruleset at `ddk-configuration/pmd/ruleset.xml` -2. Violations in generated code (`src-gen/`, `xtend-gen/`) are excluded +2. Violations in generated code (`src-gen/`) are excluded 3. Run `mvn pmd:check -f ./ddk-parent/pom.xml` to verify fixes ### Fixing Checkstyle Violations @@ -146,3 +142,4 @@ xvfb-run mvn verify -f ./ddk-parent/pom.xml -pl :com.avaloq.tools.ddk.xtext.test - **Platform**: GitHub Actions - **Workflow**: `.github/workflows/verify.yml` - Triggers on: push to master, pull requests + diff --git a/HANDOVER.md b/HANDOVER.md new file mode 100644 index 0000000000..41bc6e23aa --- /dev/null +++ b/HANDOVER.md @@ -0,0 +1,273 @@ +# Handover — Xtend→Java migration campaign + open-PR shepherding + +*Generated: 2026-06-15* +*Branch: master (synced to upstream/master `cf1c4cb4b`)* +*Living document — keep referring to and updating it; NOT read-and-delete.* +*Live per-module pipeline status: see `MIGRATION-PIPELINE.md` (companion tracker, also untracked, never commit).* + +## Mission + +Two parallel tracks: +1. **Xtend→Java migration** — peel the complete migration on PR #1274 into small, independently-reviewable per-module PRs, easiest→hardest, using a four-eyes regen+diff method. +2. **Shepherd the remaining open draft PRs** to merge, one at a time, joint review before toggling ready. + +## Standing process rules (hard constraints) + +- **One task `in_progress` at a time.** Pause + report when each completes; wait for João's go before the next. He sequences. +- **Explain before side-effects** — full plan (commands, targets, order) before any state-changing/visible action. +- **Draft PRs only.** Never `gh pr ready` autonomously. Toggle ready only on joint satisfaction. +- **Rebase-merge only:** `gh pr merge --repo dsldevkit/dsl-devkit --rebase --delete-branch`. Never squash/merge-commit. +- **Review before merge** — read the diff, surface a counter-argument, then await go. +- **Amend on own active PR branches** (force-with-lease); don't add fixup commits. +- **Comments to Ruben** need an attribution line AND explicit per-comment approval. Never post autonomously. +- **Local builds use `-T 3C`** (CI uses `-T 2C`). +- **Branch from `upstream/master` explicitly** — master moves under us (concurrent agents share `.git`). `upstream` = canonical (dsldevkit/dsl-devkit); `origin` = João's fork (joaodinissf). +- Narrate worktree/branch switches; confirm work is safe. +- **Skill / process improvements ship in their OWN dedicated PRs** (separate from migration PRs) — persist them as we go. (e.g. PR #1415, skill ground-truth process.) + +## Migration method (four-eyes, per module) + +**THE SKILL:** in-repo at `.agents/skills/xtend-to-java/` (doc-driven; read+follow, NOT a Skill-tool entry). Start at `SKILL.md` → `workflow/overview.md` (Steps 0–7) → `workflow/one-file-conversion.md` → `rules/00-09` → `workflow/validation-checklist.md`. Hard rule: read BOTH the `.xtend` source AND a behavioral ground truth IN FULL before writing any Java. + +**Ground truth = a FRESH build, not #1274.** For each module, build off `upstream/master` with `-T 3C` to (re)generate `xtend-gen/` — that is the authoritative, current, Java-authoritative ground truth (and the first build gate). **PR #1274** `origin/feature/xtend-to-java-migration` (complete migration, 0 `.xtend`) is the **four-eyes CROSS-CHECK** — read it and reconcile every divergence, but it may be STALE vs current master and is NEVER a substitute for fresh `xtend-gen/`. (DO NOT merge #1274 directly.) Roadmap with full per-file checklist lives on that branch: `XTEND_MIGRATION_ROADMAP.md`. + +Per-module loop: +1. `git fetch upstream` → worktree/branch `migrate/xtend-to-java/` from `upstream/master`. +2. **Build for ground truth:** `mvn -f ./ddk-parent/pom.xml -pl : -am -DskipTests -T 3C compile --batch-mode` → read the fresh `xtend-gen/` Java (authoritative) IN FULL, and the `.xtend` source IN FULL. If the class extends a generated supertype, read it under `src-gen/`. +3. **Regenerate** the Java from scratch via the skill — idiomatic, matching `xtend-gen/` behavior exactly. +4. **Four-eyes cross-check:** diff regen vs #1274's `.java`; reconcile every divergence (either side may be wrong — #1274 may be stale OR skill-non-compliant, e.g. `String.format`). +5. Vet vs validation-checklist: no `val`/`var`; idiomatic strings (literal → text block → `.formatted()` → `StringBuilder` only in control flow; NEVER `String.format`); preserved stack traces; parameterized Log4j2 (`{}`); try-with-resources; no unnecessary boxing; `@Override` everywhere; no wildcard imports; behavior preserved on non-mechanical transforms. +6. Full CI-equivalent build: `xvfb-run mvn clean verify checkstyle:check pmd:check pmd:cpd-check spotbugs:check -f ./ddk-parent/pom.xml --batch-mode -T 3C` + +### Per-PR process (João's standing instruction — follow methodically EVERY slice) +1. Open PR **as draft, NO reviewers**. +2. `gh pr view --web` to open it in the browser. +3. **PAUSE — wait for João's explicit approval.** Never `gh pr ready` autonomously. +4. On his go: `gh pr ready ` + assign reviewer. +5. Move to the next slice. One slice in_progress at a time; pause+report between. + +`#1277` is UNRELATED (migrates `org.eclipse.xtend` runtime *type-system dependency*, not file conversion). Own track. + +## Phase 1 — migration sequence (easy → hard). 71 `.xtend` / 16 modules remain. + +Task list is now **per-module, 1:1 task↔PR**, labelled `P0-NN` where **NN = Seq #**. Each module = its own independent PR. + +| Seq | Module | Files | Risk | Task | Status | +|----|--------|-------|------|------|--------| +| 1 | `check.ui.test` | 1 | Low — **PILOT** | #26 (P0-01) | **#1416 READY + APPROVED + green — MERGE-READY** | +| 2 | `xtext.ui.test` | 1 | Low | #41 (P0-02) | **#1417 READY + APPROVED + green — MERGE-READY** | +| 3 | `xtext.generator.test` | 1 | Low | #42 (P0-03) | **#1418 READY + APPROVED + green — MERGE-READY** | +| 4 | `check.test.runtime` | 1 | Low | #32 (P0-04) | **#1419 DRAFT + green — awaiting flip** | +| 5 | `xtext.format.test` | 1 | Low | #27 (P0-05) | **#1420 DRAFT + green — awaiting flip** | +| 6 | `xtext.format.generator` | 1 | Low | #43 (P0-06) | **#1421 DRAFT + green + APPROVED — awaiting flip** | +| 7 | `xtext.export.generator` | 1 | Low | #44 (P0-07) | **#1422 DRAFT + green + APPROVED — awaiting flip** | +| 8 | `xtext.test.core` | 1 | Med | #31 (P0-08) | **#1423 DRAFT — BLOCKED** (active annotation; DDK CI can't validate) | +| 9 | `xtext.check.generator` | 2 | Med | #45 (P0-09) | Not started | +| 10 | `xtext.scope` | 4 | Low | #28 (P0-10) | Not started | +| 11 | `xtext.expression` | 5 | Low | #29 (P0-11) | Not started | +| 12 | `xtext.format` | 6 | Low | #46 (P0-12) | Not started | +| 13 | `xtext.export` | 9 | Med | #30 (P0-13) | Not started | +| 14 | `check.core` | 8 | **HIGH** (prod Check framework — line-by-line diff) | #33 (P0-14) | Not started | +| 15 | `check.core.test` | 11 | Low (test) | #34 (P0-15) | Not started | +| 16 | `xtext.generator` — parser group | ~8 | Med | #35 (P0-16) | Not started | +| 17 | `xtext.generator` — builder group | ~10 | Med | #36 (P0-17) | Not started | +| 18 | Cleanup — drop Xtend infra (POMs/MANIFESTs/feature.xml/.classpath/.project/xtend-gen) | 0 | Low | #37 (P0-18) | Not started | + +Already merged (gone from upstream/master): `helloworld.ui.test`, `format.ide`, `format.ui`, `scope.generator`, `checkcfg.core`, `checkcfg.core.test`, `check.test.runtime.tests`, `xtext.ui`. + +Generator split (seq 16/17) is approximate — confirm exact parser-vs-builder cut against #1274 when reached. Seq 8 (`xtext.test.core`) — only 1 of roadmap's 2 files remains; verify which. + +## Phase 2 — existing open PRs + +| Action | PR | Status | Trigger | +|--------|-----|--------|---------| +| Merge | #1399 cache restore-keys | ready, APPROVED pending | Ruben approves (monitor active) | +| Merge | #1308 stale-readme removal | ready | Ruben approves (monitor active) | +| Finish review | #1376 checkcfg dup-language | draft, validation-only, green | reviewer pass → joint → toggle | +| Rework | #1396 SARIF lint redesign | draft | sweep | +| Rework | #1397 CPD threshold | draft | sweep (de-stack from #1396) | +| Revisit | #1400 spotbugs-skip | draft | reconcile w/ task #25 | +| Gated | #1382 scoping fragment2 manifest | draft | behind Ruben's #1405 | +| Gated | #1281 export generatorX guard | draft | behind Ruben's #1405 | +| Standalone | #1364 check-docs maven PoC | draft | sweep | +| External | #1405 (Ruben's) scope/export migration | draft | his; unblocks #1382/#1281 | +| Reference | #1274 complete migration | keep open | four-eyes source; close after Phase 1 | +| Separate | #1277 xtend type-system dep | draft | own decision | + +## Merged today (2026-06-15) + +#1412 (style tidy-ups), #1394 (robustness sweep — added LOG.warn to ScopeResourceDescriptionStrategy per Ruben), #1410 (spotbugs 4.10.2 bump — full saga w/ #1411 IAOM-exclusion + #1414 USO-fix complete). + +## Monitors active + +#1399 + #1308 review/CI-failure/merge watchers (persistent). They fire on review change / CI failure / merge — NOT on CI-success-only. + +## Stale worktrees (prunable — branches merged) + +`/tmp/wt-sb410` (#1414), `/Users/joao/Git/Avaloq/dsl-devkit-fixes` (#1394), and the migrate/xtend-to-java/{format-ide,format-ui,scope-generator,xtext-ui} worktrees (their plugins already merged). `git worktree remove` when convenient. + +## Other backlog tasks + +#10 empty `{@inheritDoc}` sweep (P3); #15 KNOWN-ISSUES → tracker issues incl. language-scoped catalog/param uniqueness follow-up from #1376 (P3); #25 SpotBugs CI speedup — exclude generated code + pin effort=default (P3); #20 #1399 restore-keys post-merge check (P4); #17 src-gen Xtext 2.43 sync (P4 DO-LAST). + +## End-of-campaign tasks (strict order, after ALL file conversion) + +These are SEPARATE from the `.xtend`→`.java` file conversion. Keep the three concepts distinct: **Xtend1** (classic Xtend/Xpand, pre-Xbase) ≠ **Xbase** (modern Xtext expression framework) ≠ **`.xtend`→`.java` file conversion** (#1274, what Phase 1 does). + +- **#38 [FINAL] — Sort out the Xbase / Xtend1-retirement branches.** Two separate legacy tracks to reconcile/decide (revive vs abandon): + 1. Xbase: branch `claude/migrate-xtend-to-xbase-4OWuz` (local + origin); plan `~/.claude/plans/composed-yawning-lighthouse.md`. + 2. Xtend1 expression-engine retirement: branch `feat/retire-xtend1-step-1`; spec `~/.claude/jobs/0c9fde26/xtend1-migration-spec.md`; plan `~/.claude/plans/can-you-do-a-cheerful-matsumoto.md`. Drops `org.eclipse.xtend.typesystem.Type` + `.ext` → hand-written `DdkType`/`DdkScope`. = the `#1277` track. +- **#39 [VERY LAST] — Clear out all stale branches (local + remote).** Full branch+worktree hygiene sweep after #38 decides the legacy branches' fate. Leave only live/protected refs. + +## RESUME SNAPSHOT — 2026-06-17 (morning) + +**8 PRs MERGED** (#1415 skill + 7 migrations #1416–#1422 = P0-01→07). master @ `40034ebb4`. **5 migration drafts open, all CI-green.** + +| PR | P0 | State | Review | CI | Notes | +|----|----|-------|--------|----|-------| +| #1415–#1422 | skill + 01–07 | **MERGED** ✅ | APPROVED | — | done | +| **#1426** | 09 check.generator | OPEN (ready) | CHANGES_REQUESTED→addressed | 4/4 ✅ | Ruben wanted text blocks; converted (proven byte-identical), amended `d66599eee` + force-pushed → re-review | +| **#1427** | 15 check.core.test | OPEN draft | REVIEW_REQUIRED | 4/4 ✅ | xbase.lib-removal blocker did NOT materialize (CI green). +nits | +| **#1428** | 12 format | OPEN draft | REVIEW_REQUIRED | 4/4 ✅ | 766-line inferrer; 1 nit | +| **#1429** | 16 generator-parser | OPEN draft | REVIEW_REQUIRED | 4/4 ✅ | SHIP; split — infra deferred | +| **#1430** | 17 generator-builder | OPEN draft | REVIEW_REQUIRED | green | SHIP; split — infra deferred; rebase before merge | +| **#1423** | 08 Tag | OPEN draft | APPROVED | 4/4 ✅ | **BLOCKED** — gated on asmd's 6 non-test `@Tag` holders. DO NOT merge. | + +**Open threads:** (1) #1426 awaiting Ruben re-review after the text-block fix. (2) Drafts #1427/#1428/#1429/#1430 awaiting Ruben review (no reviewers assigned yet — flip/assign on João's go). (3) **Text-block sweep** (task #24): Ruben+João both want StringConcatenation→text-block where clean; #1426 done, rest pending. (4) Proposed but NOT dispatched: a **diff-vs-#1274 workflow** (four-eye audit of every split migration against the reference complete-migration). (5) Deferred behind Ruben's #1405: P0-10 scope, P0-11 expression, P0-13 export. (6) Held: P0-14 check.core (HIGH RISK). (7) End: P0-08 Tag, P0-18 cleanup. + +**Live worktrees:** dsl-devkit-{xtext-format, check-core-test, xtext-generator-parser, xtext-generator-builder, xtext-check-generator} (+ older stale ones for end-of-campaign sweep). + +**Equivalence-proof method (for text-block conversions):** compile a tiny comparator (`/tmp/CmpQf.java` pattern) linking the real `org.eclipse.xtext.xbase.lib` classes jar (under `~/.m2/.../p2/osgi/bundle/`), build both the StringConcatenation sequence and the candidate text block, `assertEquals`. For test files, running the test is itself the proof. + +## RESUME SNAPSHOT — 2026-06-21 (flaky-test fix + JUnit 4 purge sub-thread) + +Side-thread off **#1438** (dependabot checkstyle 13.6 bump, kept failing CI). Diagnosed a long-standing flaky SWTBot test and purged remaining JUnit 4 traces. Two of my draft PRs, both CI-green, awaiting flip: + +| PR | What | State | CI | +|----|------|-------|----| +| **#1440** | `fix(test)` de-flake `bulkApplyQuickfix` + remove dead `@Retry` | draft; row-count gate; ponytail-trimmed; full multi-agent review clean (1 nit fixed, 3 refuted) | green 4/4 | +| **#1441** | `chore` purge orphaned JUnit 4 declarations | draft; 5 commits (manifests, .classpath, devkit-run.launch sweep, dead PMD suppressions, stale comments); ultracode audit = **PASS** (no real JUnit 4 left repo-wide) | green 4/4 | +| **#1442** | `build(checkstyle)` require Javadoc on public/protected **methods** + document them | draft; `MissingJavadocMethod scope=protected tokens=METHOD_DEF` (**constructors excluded** per João); ~154 method docs across 64 files (144 boilerplate ctor docs stripped, 0 deletions vs master); checkstyle 0/65 green; 6 area-grouped commits | re-running | + +- **Flake root cause (task #26):** the Quick Fix dialog repopulates its Problems table async; the tick loop ran with no wait → 0 rows ticked → Finish a no-op → `expected:<0> but was:<4>`. Fixed by gating on `matchingRowCount == markers.length` (a *Set* of locations collapses — the 2 test files share line numbers). `@Retry` was a dead no-op since its JUnit 4 `ClassRunner` consumer was deleted (`d920b3bad`). The earlier 60s-timeout-bump attempt was **disproven** (still failed → nothing was applied, not slow refresh). +- **#1438 (task #28):** leave red; comment `@dependabot rebase` once #1440 merges → it picks up the de-flake → green. + +**Deferred follow-ups (own branches; harness tasks #29 / #30):** +- **#29 [JAVADOC]:** convert `@`/HTML entities in Javadoc `
` examples → `
{@code …}
` (readable source; never naively swap `@`→`@` outside `{@code}`). 2 files w/ `@` + 4 w/ `</>`. Own branch, deferred. +- **#30 [CHECKSTYLE]: DONE → PR #1442.** Backlog measured (~350 public/protected; the "598" was an inflated parallel-log count), documented via a 29-agent ultracode workflow, then **constructors excluded** (`tokens=METHOD_DEF`) and the 144 boilerplate ctor docs stripped → ~154 method docs. Reactor checkstyle 0/65 green. Draft, awaiting review/flip. + +New worktrees: `dsl-devkit-flaky-quickfix` (#1440), `dsl-devkit-junit4-purge` (#1441), `dsl-devkit-checkstyle-javadoc` (#30). + +## RESUME SNAPSHOT — 2026-06-22 (flaky PRs MERGED + Sonnet×Opus faithfulness experiment) + +**Flaky-test campaign COMPLETE — 3 merges since last snapshot:** +| PR | Now | Note | +|----|-----|------| +| **#1440** de-flake + drop `@Retry` | ✅ **MERGED** | shipped Option B: single retrying `checkMatchingRows` waited op; catch broadened to `IllegalArgumentException\|IndexOutOfBoundsException`; `!item.isChecked()` event-idempotency guard (verified `check()`=`setChecked(true)` in SWTBot bytecode). Tasks #26 ✅ | +| **#1441** JUnit4 purge | ✅ **MERGED** | task #27 ✅ | +| **#1438** checkstyle 13.6 bump | ✅ **MERGED** | went green once #1440 landed; task #28 moot ✅ | +| **#1442** Javadoc presence | OPEN/**draft**, tip `1b2bc5f64` on fork | FINAL design: `scope=protected`, `tokens=METHOD_DEF` (ctors excluded), `allowMissingPropertyJavadoc=true` (trivial getters/setters excluded), `@Override`/`@Inject` exempt. 51 method docs / 21 files, **0 deletions**, reactor green. Rewrote 7 boilerplate docs; stripped `setUserData` (lone missed trivial accessor). PR body updated. Task #30 — NOT flipped/merged. | + +**Sonnet×Opus migration-faithfulness experiment (DONE).** 8 workflows = 4 branches (#1426–1429) × 2 models, one model-pinned agent per file, comparing migrated `.java` vs original `.xtend` (ground truth = committed `xtend-gen`, found at `eclipse/ddk-master/git/dsl-devkit/**/xtend-gen/**`). All findings adjudicated against `xtend-gen`: +- **ONE material finding:** **#1428 `FormatJvmModelInferrer._getDirectiveName(GroupBlock)`** — migration **silently fixed a latent Xtend bug**. Xtend `newArrayList(Iterables.filter(...))` hit the `CollectionLiterals.newArrayList(T...)` varargs trap → `ArrayList>` → `indexOf` always −1 → **always `"Group0"`** (verified `xtend-gen` L922). Migrated builds real `List` → `"Group1/2/…"`; feeds generated formatter class/method names. Caught by BOTH models. **Decision pending:** reproduce `Group0` (strict) vs keep repair + document + check downstream. +- **Everything else:** benign idiomatic deviations (UTF-8 `getBytes`, dropped no-op `close()`, `orElseThrow`-vs-NPE), 2 trivial trailing-newline diffs (`generateSrc`, `IssueCodeValueTest`), and a pile of **whitespace FALSE POSITIVES**. +- **Scorecard:** Opus 1 real / **2 FP** (CheckModelUtil, #1429 Javadoc); Sonnet 1 real / **~10 FP** (all whitespace). **Model agreement ≠ correctness** — both wrong on `CheckModelUtil.modelWithSeverityRange` (verified byte-identical 0/4/4). `xtend-gen` required for every call. +- **Cost (real tokens):** Sonnet **$35.41** ($1.42/file) vs Opus **$117.99** ($4.72/file, ~3.3×). Sonnet churns on whitespace files (#1429-sonnet: $21.37, 285k out, 369 tool calls, 50 min) — advantage erodes where it's least accurate. +- **STAGED, NOT POSTED:** the #1428 `getDirectiveName` PR comment (inline, lines 623-628, commit_id `a0e4275e989fa26415ea4e0476383cc6cdcccb5e`, attribution line included). Full `gh api` command + body in conversation. **Needs explicit per-comment approval.** + +**Experiment infra gotchas (reusable):** (1) `args` is NOT delivered to workflow scripts here → **bake `const CFG={...}` into the script**. (2) 8-at-once → server-side rate limit → use ≤2-in-flight throttle + sequential retry + `incomplete:[]` reporting. (3) compute file lists in main thread (agent discovery truncated to 10). Scratch scripts under `.claude/wf-*.js` + `.claude/wf-filelists.json` (untracked; clean up later). + +**Migration PR review status:** #1426–1429 = Ruben's CHANGES_REQUESTED are STALE (his text-block/Iterables/`{@inheritDoc}` feedback already applied & pushed) → user to request re-review. **#1430** = genuinely unaddressed (static `StringConcatenation`→text-block across ~7 Fragment2 generators; his review is on current HEAD) — deferred sweep (tasks #24/#25). + +**Xtend whitespace rule (settled empirically):** Xtend strips the **min content indentation** (NOT the closing-`'''` delimiter indent); Java text blocks strip min content indent too, so a correctly-placed closing `"""` matches. Migration does this right everywhere; the only real whitespace divergence is a trailing newline when `"""` sits on its own line (`}\n` vs `}`). + +## RESUME SNAPSHOT — 2026-06-23 (append-run → text-block coalescing; verification harness) + +**PR state:** **#1426 MERGED 06-23** (xtend↔java reviewed by João = ✅ OK). #1427/#1428/#1429/#1430 still OPEN. Older batch #1416–1422 merged 06-16/17. + +**#1428 — CI fixed.** Audit-driven extra cleanup landed: `542eb5710` (7× single-line `StringConcatenation`→`.formatted`) + `e5c9dea0c`→ rebased to `00e2743f6` (`FormatGenerator` exception `.formatted`). The middle commit `2ec9f181f` (`_elementAccess`/`_locator` `StringConcatenation`→`java.lang.StringBuilder`) **broke PMD** (`InsufficientStringBufferDeclaration` + `AppendCharacterWithChar`) → **DROPPED** via `git rebase --onto 542eb5710 2ec9f181f`, force-pushed, **CI green**. Ruben's 8 #1428 threads: **all addressed** (6 applied in original migration + `generateJavaDoc` `:412` deliberately kept — loop + two-arg indented append). + +**#1429 — `kwBuilder` `.formatted` pushed** (`4df925f4d`), CI green. **Ruben added NEW comments 06-23**: `:567` (`ruleImpl`), `:175` (parser ctor), more on `:90` — all the **dynamic ANTLR generators** (the risky ones). + +**KEY DECISION — avoid risky translations.** Removing `StringConcatenation` from the big dynamic ANTLR generators (#1429 `AnnotationAwareAntlrGrammarGenerator` 1099 ln / `…ContentAssist…` 1302 ln; 86/90 `newLineIfNotEmpty`, 43/66 two-arg appends) is **too risky / low value** (Ruben himself: "a builder is justified") → **NOT doing it**. Dedicated stacked branch idea scrapped; scratch worktree `dsl-devkit-antlr-strconcat` removed. + +**CURRENT INITIATIVE — coalesce consecutive append-runs → text blocks**, as a **new dedicated commit per OPEN branch** (#1427–1430; NOT amend; fast-forward push). **Scope = TARGETED** (João's pick): Ruben's flagged methods + big pure-static/single-arg runs; skip heavily-fragmented small runs. + +**Verification (load-bearing): executable byte-equality harness** `.claude/coalesce-verify/Probe.java` — lifts the OLD append run verbatim onto real `new StringConcatenation("\n")` vs the candidate text block `.formatted(...)`, asserts byte-equality over a battery {empty, multi-line, spaces, quotes, `%`, unicode}. Compile/run: `javac -cp -d . Probe.java && java -cp :. Probe`. Jar = `~/.m2/repository/org/eclipse/xtext/org.eclipse.xtext.xbase.lib/2.43.0/org.eclipse.xtext.xbase.lib-2.43.0.jar`; Java 26. +- **Empirical rules (executed, not assumed):** single-arg `append(v)` does NOT re-indent multi-line → safe `%s`. Two-arg `append(v,indent)` DOES re-indent multi-line → **exclude/split**. `newLine()`/`newLineIfNotEmpty()`→`\n`; `newLineIfNotEmpty` safe after a line with literal content, **UNSAFE after a bare value** (empty→divergence). Import-managed `append(TypeReference/Class)` → keep as separate append (split text block around it). +- **CRITICAL:** `mvn verify`/CI do **NOT** regenerate generated artifacts (no `xtext-maven-plugin`/`mwe2` in poms; IDE-only) and there's **no CI freshness guard** → **CI cannot catch generator-output drift**. The harness is the ONLY real output gate. Committed `src-gen` is LF (line-ending check enforces) = matches `\n` modeling. + +**DONE so far:** **#1430 `LspBuilderIntegrationFragment2`** fully coalesced — 4 blocks (generateBuildService prologue+epilogue, generateBuildSetup imports + injector bodies), **all proven `IDENTICAL`**, **−70 net lines**, compiles (EXIT=0). **NOT committed yet** (more #1430 files under targeted scope before the single #1430 commit). + +**Review tracker (João confirms each xtend↔java):** #1426 ✅ OK. Pending: #1427–1430 (review AFTER coalescing lands) then older #1416–1422. **Sequence (João's order): update open PRs → review open ones → then older merged ones.** + +**Held comments (drafted, NOT posted — need per-comment approval + attribution; link to the coalescing commits once landed):** #1429 reply (static parts already text blocks; dynamic builders justified, kept), #1430 `LspBuilder` reply (API-bound wrapper, body now text blocks), #1428 `:412` `generateJavaDoc` note, and the staged **#1428 `getDirectiveName`** behavioural comment. + +**Memories added this session:** `feedback_generated_output_lf` (emit `\n` unconditionally; line-separator divergence not a blocker), `feedback_local_validation_full_checks` (pre-push must run `pmd:check`/`checkstyle:check`/`spotbugs:check`, not just `package`). + +**Scratch (untracked, clean up later):** `.claude/x2j-review.sh` (opens each PR's original `.xtend` vs new `.java` in `code --diff`; usage `bash .claude/x2j-review.sh `), `.claude/x2j-review//` (extracted originals), `.claude/coalesce-verify/Probe.java` (+ `.class`), earlier `.claude/wf-*.js` + `wf-filelists.json`. + +## RESUME SNAPSHOT — 2026-06-23 (late): APPROVALS IN → merge approved + coalescing-as-follow-ups + +**Ruben re-reviewed.** **#1427 APPROVED, #1428 APPROVED, #1430 APPROVED** (all `CLEAN`/`MERGEABLE`, CI 4/4 green). **#1429 = CHANGES_REQUESTED / BLOCKED** — his `:90/:112/:150/:175/:567` ANTLR multi-line-string comments; the only holdout. + +**Coalescing committed (harness-proven IDENTICAL, CI-gate green):** +- **#1430** (APPROVED, includes these): `e832f558b` (LspBuilder+StandaloneBuilder+FormatterFragment2) + `5f6feaa13` (LanguageConstants). Approval survived the pushes. +- **#1429**: `cd1d420bf` (AnnotationAwareAntlrGrammarGenerator). **ContentAssist batch-1** (imports + setters/getGrammar block) **built-green, UNCOMMITTED** in worktree `dsl-devkit-xtext-generator-parser`. +- #1427/#1428 nothing to coalesce; #1426 merged. + +**Refined rule (proven):** pure-static→text block; single-arg value→`%s`; two-arg `append(v,indent)` with provably single-line value→`%s` (no-op re-indent, verify single-line battery); EXCLUDE import-managed `append(Class/TypeReference)`, multi-line values, `appendImmediate`, loop bodies. **Remainder verdict:** leftover ContentAssist/Fragment2 runs are tiny fragments inside dynamic loops/typeRef appends → low/negative value → **skip (churn).** + +**THE PLAN (await João's go — merging is irreversible):** +- **Phase A — merge approved:** rebase-merge #1427 → #1428 → #1430 (`gh pr merge --repo dsldevkit/dsl-devkit --rebase --delete-branch`), re-check `CLEAN` before each. #1430 brings its 2 approved coalescing commits. *Caveat (review-before-merge): João has NOT done his own xtend↔java review of #1427/#1428/#1430 (only #1426); merging trusts Ruben+CI — `bash .claude/x2j-review.sh ` to review first.* +- **Phase B — #1429:** commit ContentAssist batch-1, post the held reply (static→text-blocks done; dynamic ANTLR generators kept as justified builders), request re-review → merge on approval. +- **Phase C — follow-ups:** skip marginal coalescing; genuine follow-ups = #29 Javadoc `{@code}` + staged #1428 `getDirectiveName` comment. Scratch cleanup: `.claude/coalesce-verify/`, `.claude/x2j-review/`, `.claude/wf-*.js`. + +**Harnesses:** `.claude/coalesce-verify/{Probe,SB,FF,AAGG1,AAGG2,LC,CA1}.java`; xbase jar `~/.m2/.../org.eclipse.xtext.xbase.lib/2.43.0/...jar`, Java 26. + +## RESUME SNAPSHOT — 2026-06-23 (latest): Phase 0 review DONE → A0 bugfix split-out → ordered merge + +**Phase 0 ultracode pre-merge review (workflow `w8mi77f4s`) DONE.** 29 files, 8 findings, **2 confirmed / 6 refuted** (whitespace/style FPs). **All 3 approved PRs CLEAR — 0 blockers.** Both confirmed are minor + in *original migration code* (my coalescing = 0 drift): +- #1428 `List.of(...)` in ~12 dispatcher fall-through throws (`FormatJvmModelInferrer:1434…`): NPE-on-null vs xtend-gen `Arrays.asList`→IAE; unreachable `infer(null,…)` path → optional follow-up. +- #1427 `modelWithContexts` trailing `"\n "`: test-helper, CI-green/harmless, whitespace-FP class → optional follow-up. + +**NEW DECISION (João): split the `getDirectiveName` repair into its own PR.** The migration silently fixed a latent Xtend bug (`newArrayList(filter(...))` varargs trap → `ArrayList>` → `indexOf` always −1 → every group `"Group0"`). Fix it **in the `.xtend` on a dedicated branch off `upstream/master`** so the migration stays a faithful 1:1. **This is now Phase A0, merged FIRST.** Fix = `FormatJvmModelInferrer.xtend:475` → `grammarRule.directives.filter(GroupBlock).toList` (→ `Group1/2/3`); changes generated formatter output (`Group0`→`GroupN`); open PR fork→upstream `master`, **reviewer = rubenporras**. Supersedes the old staged discussion comment. + +**Revised merge order: A0 → #1428 → #1427/#1430.** After A0 merges, **rebase #1428** onto bugfixed master (modify/delete on `FormatJvmModelInferrer.xtend` → keep #1428's deletion; force-push; may need Ruben re-approval) — its `.java` already emits `Group1/2/3` so it's faithful to the fixed `.xtend`. Then rebase-merge #1428/#1427/#1430 (`--rebase --delete-branch`, re-check CLEAN each). + +**A0 DONE (2026-06-23):** worktree `/Users/joao/Git/Avaloq/dsl-devkit-format-groupfix` (branch `fix/format-directive-group-numbering` off `upstream/master` @ `088854035`). Fix = 2 lines in `FormatJvmModelInferrer.xtend` (`.filter(GroupBlock).toList` + drop orphaned `Iterables` import). Verified: `xtend-gen` untracked + `git grep Group0` = 0 → no committed artifact affected (blast radius nil-to-cosmetic in-repo; latent bug). Full CI-gate build GREEN. Commit `c9d56d8ec`, pushed to fork. **PR [#1443](https://github.com/dsldevkit/dsl-devkit/pull/1443)** opened: draft ✓, `joaodinissf:fix/format-directive-group-numbering → dsldevkit:master`, reviewer **rubenporras** ✓. **Confirmed #1428's migrated `_getDirectiveName(GroupBlock)` already emits `Group1/2/3`** (via `Iterables.addAll(new ArrayList, filter)`) → faithful once A0 lands. + +## RESUME SNAPSHOT — 2026-06-25: A0+5 PRs MERGED → correctness-first faithfulness sweep running + +**📒 VERIFICATION LEDGER:** `.claude/migration-verification.md` — per-file byte-vet status (HARNESS / APPEND-DIFF / FAITHFULNESS / SWEEP) for every migrated file. Keep it updated as files are vetted. Bottom line: **every byte-output generator is byte-vetted**; logic/test files are faithfulness-vetted (byte-vet N/A); confirmed drifts all fixed (#1443/#1446) or benign-deferred. + +**#1446** (faithfulness follow-ups, draft, Ruben requested): 2 commits — `AnnotationAware` indent fix + faithful `FormatJvmModelInferrer` dispatcher throws. Ruben CHANGES_REQUESTED w/ 3 suggestions (empty-arg throws → `"Unhandled parameter types"`) → **applied + force-pushed (`e78494ffb`)**, build green. Pending: re-request Ruben / resolve 3 suggestion threads (await João). + +**#1429 DECISION (João):** keep `StringConcatenation` (do NOT remove per Ruben's `:90` — removal risks byte-drift), but **coalesce static append-runs into text blocks where byte-safe** (harness-gated). Sequence pending approval. + +**MERGED this campaign:** A0 `#1443` (getDirectiveName fix in `.xtend`), `#1428` (format), `#1427` (check-core-test), `#1444` (Ruben's CheckGenModelUtil log fix), `#1430` (generator-builder). master @ `839bfdde5`. **`#1429` (parser) is the only open migration PR** (CHANGES_REQUESTED). + +**Merge mechanics learned:** repo **dismisses approvals on push** (force-push re-opens review). `gh pr merge --repo dsldevkit/dsl-devkit --rebase --delete-branch`. Rebase locally onto `upstream/master` (git cherry-pick-detection drops duplicate stacked commits, e.g. A0's commit dropped from #1428). + +**Per-pair review done (read-only `OK`/`not OK`):** #1427 (11 pairs → 1 nit: `CheckModelUtil.modelWithContexts` trailing `"\n "`, harmless, disabled-caller). #1430 (12 pairs → `ResourceFactoryFragment2` verified clean; **`AnnotationAwareContentAssistFragment2` had a REAL byte-drift bug** — multi-line `Alternatives` leaf continuation-lines lost indentation: migrated single-arg `append` vs xtend-gen two-arg `append(v," ")`). **Fix done + harness-proven (AACA.java 7/10→10/10) + build-green; saved as `scratchpad/aaca-indent-fix.patch`** — NOT on #1430 (merged as-is); goes in the follow-up PR. + +### ★ GUIDING PRINCIPLE (João + Claude agreed 2026-06-25) — correctness over readability, in order +**Generated-output BYTE-IDENTITY is the non-negotiable gate. Readability (`StringConcatenation`→text-block) conversions are a genuine good (we AGREE with Ruben's goal) but are VERIFIED FOLLOW-UPS — never unverified blockers, sequenced AFTER correctness.** Never comply with "make it a multi-line string" on sight: convert only where **harness-proven byte-identical**; keep the builder (with byte-level justification to Ruben) where folding changes output (e.g. import-managed `append(TypeReference)` cannot fold to `%s`). Three real byte-drift bugs already came from *unverified* readability conversions (`AnnotationAware`, `modelWithContexts`, `FormatJvmModelInferrer`). The sweep exists to catch exactly this. + +### Ordered plan +1. **Sweep running** — `wqx01ugww` (`.claude/wf-sweep.js`): 51 Opus agents, one per merged migrated `.xtend` (excl. #1430's 12 = already harness-done), hunting byte-drift + faithfulness; adversarial verify. **WAIT for it.** +2. **Phase 2 — harness-confirm** every `NEEDS_HARNESS`/drift file (build `AACA`/`Probe`-style harness vs `xtend-gen`); pin/fix real drift. *(`xtend-gen` ground truth lives in sibling worktrees' `xtend-gen/` dirs; `git show ^:` for originals.)* +3. **Phase 3 — ONE follow-up PR** off master: `aaca-indent-fix.patch` + every sweep-confirmed fix, each harness-gated, for Ruben. +4. **#1429, same discipline** — harness-verify its existing text-block conversions; address Ruben's 6 comments by converting where provably safe (harness-gated) + keeping builders with byte evidence where not; commit ContentAssist batch-1; re-request review as *"correctness-verified readability."* +5. **Broader readability follow-ups** — LAST, only after correctness locked; separate harness-gated PRs. + +### #1429 — the 6 unresolved Ruben threads (all "use a multi-line string", correctness-gated) +- `AbstractAnnotationAwareAntlrGrammarGenerator.java:90` (current): keep builder but **drop `org.eclipse.xtend2.lib.StringConcatenation`**. +- `…Abstract…:112/:150/:180` (OUTDATED — code changed, likely already text-block'd by `b49eae6bc`): "clearly a multi-line string … and so on, I did not continue the review" (← he stopped partway, expects whole-file treatment). +- `AnnotationAwareAntlrContentAssistGrammarGenerator.java:567` (current): would benefit from a multi-line string (ContentAssist batch-1 coalescing still UNCOMMITTED in worktree). +- `AnnotationAwareAntlrGrammarGenerator.java:168` (current): "and this" (file coalesced in `cd1d420bf` — verify covers it). +His steering note: *"Claude constantly forgets … multi-line strings for readability … I interject it as steering."* + +## Next action + +**WAIT for sweep `wqx01ugww` to complete.** Then: report the per-file verdict table → **Phase 2** harness-confirm flagged files → fix → **Phase 3** single follow-up PR (incl. `aaca-indent-fix.patch`). Then **#1429** per the discipline above. Deferred: #29 Javadoc `{@code}`; scratch cleanup (`.claude/coalesce-verify`,`x2j-review`,`wf-*.js`,`sweep-cfg.json`,`pre-merge-cfg.json`); #1442 draft decision; Phase-0 nits (#1427 `modelWithContexts`, #1428 `List.of`→`Arrays.asList` dead-path) → fold into Phase-3 PR. Tasks: #11 sweep (in progress), #12 harness-confirm, #13 follow-up PR. diff --git a/XTEND_MIGRATION_ROADMAP.md b/XTEND_MIGRATION_ROADMAP.md new file mode 100644 index 0000000000..21bafe1abe --- /dev/null +++ b/XTEND_MIGRATION_ROADMAP.md @@ -0,0 +1,269 @@ +# Xtend → Java migration roadmap + +Living document. Lives on `feature/xtend-to-java-migration`. Updated each week as slices are cut and merged. + +## Scope + +94 `.xtend` files → 87 `.java` files on this branch, with 2122 supporting edits (POMs, MANIFEST.MFs, feature.xmls, `.project`, `.classpath`). The migration is complete on this branch; the remaining work is **merging it into master in reviewable slices**, one per week, with each slice manually vetted against the Java best-practices checklist below. + +## Process for each weekly slice + +1. **Cut a fresh branch from master**, not from this branch: + ```bash + SLICE=migrate/xtend-to-java/ + git fetch origin + git checkout -b "$SLICE" origin/master + ``` +2. **Grab just this slice's files from the migration branch**: + ```bash + git checkout feature/xtend-to-java-migration -- ... + ``` +3. **Manually vet each file** against the review checklist below. Do not trust that the migration branch is already clean — the goal of the slice review is to catch any residual issues. +4. **Build and test the affected plugins**: + ```bash + mvn -pl , -am verify -f ./ddk-parent/pom.xml + ``` + Then run the full CI-equivalent check before pushing: + ```bash + xvfb-run mvn clean verify checkstyle:check pmd:pmd pmd:cpd pmd:check pmd:cpd-check spotbugs:check -f ./ddk-parent/pom.xml --batch-mode --fail-at-end + ``` +5. **Commit, push, open PR** against master. +6. **Once merged**, update the status column in this file. + +## Review checklist (applied to every slice) + +For each migrated `.java` file, verify: + +- [ ] **No `val`** — Xtend's `val` must not leak via pretend-typed variables. Use an explicit type or `var`; prefer explicit type for fields, method returns, and any variable where the type is not obvious from the RHS. +- [ ] **String handling is idiomatic**: + - simple `+` concatenation for a single dynamic insertion, + - `String.format` for multi-value templates, + - text blocks for multi-line strings, + - `StringBuilder` only when building in a loop or branch, + - never `String.valueOf(x) + "..."` when `x + "..."` works. +- [ ] **Preserved stack traces in catch blocks** (PMD `PreserveStackTrace`): every `throw new WrapperException(...)` in a catch must pass the caught exception as cause. +- [ ] **Parameterized SLF4J logging**: `logger.info("x={}", x)` — never `logger.info("x=" + x)`, never `logger.info(String.format(...))`. +- [ ] **try-with-resources** for every `AutoCloseable` (streams, scanners, writers, JDBC). +- [ ] **No unnecessary boxing** — `Integer.valueOf(i)` only when the method signature demands it. +- [ ] **`@Override` on every override**, including interface methods. +- [ ] **No wildcard imports**. +- [ ] **PMD, Checkstyle, SpotBugs clean** — the slice must pass `verify.yml`'s full command without `--fail-at-end` tolerating anything. +- [ ] **Behavior preserved** — sanity-diff the Xtend source against the Java output for any non-mechanical transformation (lambda captures, operator overloading → method calls, extension methods, elvis `?:`, safe-nav `?.`, list/map literal syntax). +- [ ] **Each plugin still builds standalone**, and each plugin's tests still pass on their own. + +## Slice order + +Ordered leaves → trunk so that rollback of a single slice doesn't cascade. DSL families move as one unit (core + ide + ui + test + generator) so that each slice is independently shippable. + +| # | Week | Slice | Modules | Xtend files | Risk | Status | +|---|------|-------|---------|-------------|------|--------| +| 1 | TBD | Warmup — samples & leaf tests | `sample.helloworld.ui.test`, `check.ui.test`, `xtext.ui.test`, `xtext.generator.test` | 6 | Low | Not started | +| 2 | TBD | `xtext.format` DSL family | `xtext.format`, `.format.ide`, `.format.test`, `.format.ui`, `.format.generator` | 11 | Low | Not started | +| 3 | TBD | `xtext.scope` DSL family | `xtext.scope`, `.scope.generator` | 5 | Low | Not started | +| 4 | TBD | `xtext.expression` DSL | `xtext.expression` | 5 | Low | Not started | +| 5 | TBD | `xtext.export` DSL family | `xtext.export`, `.export.generator` | 10 | Medium | Not started | +| 6 | TBD | `checkcfg` DSL + tests | `checkcfg.core`, `checkcfg.core.test` | 11 | Medium | Not started | +| 7 | TBD | `check.core` (production DSL) | `check.core` | 8 | **High** — production check framework | Not started | +| 8 | TBD | Check tests & runtime | `check.core.test`, `check.test.runtime`, `check.test.runtime.tests` | 15 | Low (tests only) | Not started | +| 9 | TBD | Xtext test utilities + UI helpers | `xtext.test.core`, `xtext.ui`, `xtext.check.generator` | 5 | Medium | Not started | +| 10 | TBD | `xtext.generator` — parser group | `xtext.generator` (Antlr / annotation-aware fragments + `BundleVersionStripperFragment`, `DefaultFragmentWithOverride`) | 8 | Medium (build-time only) | Not started | +| 11 | TBD | `xtext.generator` — builder + misc | `xtext.generator` (builder/LSP fragments, formatter, language constants, model inference, project config, resource factory, compare, content-assist) | 10 | Medium (build-time only) | Not started | +| 12 | TBD | Cleanup — remove Xtend build infrastructure | POMs (`xtend-maven-plugin`), MANIFEST.MFs (`org.eclipse.xtend` imports), `feature.xml` (xtend bundles), `.classpath` / `.project` (Xtend nature), `xtend-gen/` directories, PMD config references | 0 (infrastructure only) | Low — final confirmation that nothing imports Xtend anymore | Not started | + +If a week is particularly quiet or a slice is particularly small, adjacent small slices (e.g., #3 + #4) can combine. Don't combine across risk tiers. + +## File inventory per slice + +
+Slice 1 — Warmup — samples & leaf tests (6 files) + +- `com.avaloq.tools.ddk.sample.helloworld.ui.test/src/.../CheckConfigurationIsAppliedTest.xtend` +- `com.avaloq.tools.ddk.sample.helloworld.ui.test/src/.../CheckExecutionEnvironmentProjectTest.xtend` +- `com.avaloq.tools.ddk.sample.helloworld.ui.test/src/.../IssueLabelTest.xtend` +- `com.avaloq.tools.ddk.check.ui.test/src/.../CheckQuickfixTest.xtend` +- `com.avaloq.tools.ddk.xtext.ui.test/src/.../TemplateProposalProviderHelperTest.xtend` +- `com.avaloq.tools.ddk.xtext.generator.test/src/.../XbaseGeneratorFragmentTest.xtend` +
+ +
+Slice 2 — xtext.format DSL family (11 files) + +- `xtext.format/src/.../FormatRuntimeModule.xtend` +- `xtext.format/src/.../FormatStandaloneSetup.xtend` +- `xtext.format/src/.../generator/FormatGenerator.xtend` +- `xtext.format/src/.../jvmmodel/FormatJvmModelInferrer.xtend` +- `xtext.format/src/.../scoping/FormatScopeProvider.xtend` +- `xtext.format/src/.../validation/FormatValidator.xtend` +- `xtext.format.ide/src/.../FormatIdeModule.xtend` +- `xtext.format.ide/src/.../FormatIdeSetup.xtend` +- `xtext.format.test/src/.../FormatParsingTest.xtend` +- `xtext.format.ui/src/.../FormatUiModule.xtend` +- `xtext.format.generator/src/.../FormatFragment2.xtend` +
+ +
+Slice 3 — xtext.scope DSL family (5 files) + +- `xtext.scope/src/.../generator/ScopeGenerator.xtend` +- `xtext.scope/src/.../generator/ScopeNameProviderGenerator.xtend` +- `xtext.scope/src/.../generator/ScopeProviderGenerator.xtend` +- `xtext.scope/src/.../generator/ScopeProviderX.xtend` +- `xtext.scope.generator/src/.../ScopingFragment2.xtend` +
+ +
+Slice 4 — xtext.expression DSL (5 files) + +- `xtext.expression/src/.../generator/CodeGenerationX.xtend` +- `xtext.expression/src/.../generator/ExpressionExtensionsX.xtend` +- `xtext.expression/src/.../generator/GeneratorUtilX.xtend` +- `xtext.expression/src/.../generator/GenModelUtilX.xtend` +- `xtext.expression/src/.../generator/Naming.xtend` +
+ +
+Slice 5 — xtext.export DSL family (10 files) + +- `xtext.export/src/.../generator/ExportedNamesProviderGenerator.xtend` +- `xtext.export/src/.../generator/ExportFeatureExtensionGenerator.xtend` +- `xtext.export/src/.../generator/ExportGenerator.xtend` +- `xtext.export/src/.../generator/ExportGeneratorX.xtend` +- `xtext.export/src/.../generator/FingerprintComputerGenerator.xtend` +- `xtext.export/src/.../generator/FragmentProviderGenerator.xtend` +- `xtext.export/src/.../generator/ResourceDescriptionConstantsGenerator.xtend` +- `xtext.export/src/.../generator/ResourceDescriptionManagerGenerator.xtend` +- `xtext.export/src/.../generator/ResourceDescriptionStrategyGenerator.xtend` +- `xtext.export.generator/src/.../ExportFragment2.xtend` +
+ +
+Slice 6 — checkcfg DSL + tests (11 files) + +- `checkcfg.core/src/.../generator/CheckCfgGenerator.xtend` +- `checkcfg.core/src/.../jvmmodel/CheckCfgJvmModelInferrer.xtend` +- `checkcfg.core/src/.../util/PropertiesInferenceHelper.xtend` +- `checkcfg.core/src/.../validation/ConfiguredParameterChecks.xtend` +- `checkcfg.core.test/src/.../contentassist/CheckCfgContentAssistTest.xtend` +- `checkcfg.core.test/src/.../scoping/CheckCfgScopeProviderTest.xtend` +- `checkcfg.core.test/src/.../syntax/CheckCfgSyntaxTest.xtend` +- `checkcfg.core.test/src/.../util/CheckCfgModelUtil.xtend` +- `checkcfg.core.test/src/.../util/CheckCfgTestUtil.xtend` +- `checkcfg.core.test/src/.../validation/CheckCfgConfiguredParameterValidationsTest.xtend` +- `checkcfg.core.test/src/.../validation/CheckCfgTest.xtend` +
+ +
+Slice 7 — check.core production DSL (8 files) + +- `check.core/src/.../compiler/CheckGeneratorConfig.xtend` +- `check.core/src/.../formatting2/CheckFormatter.xtend` +- `check.core/src/.../generator/CheckGenerator.xtend` +- `check.core/src/.../generator/CheckGeneratorExtensions.xtend` +- `check.core/src/.../generator/CheckGeneratorNaming.xtend` +- `check.core/src/.../jvmmodel/CheckJvmModelInferrer.xtend` +- `check.core/src/.../scoping/CheckScopeProvider.xtend` +- `check.core/src/.../typing/CheckTypeComputer.xtend` + +**Review extra-carefully:** this is the runtime Check framework used by downstream consumers. Diff every file against its Xtend original line-by-line, not just for style. +
+ +
+Slice 8 — Check tests & runtime (15 files) + +- `check.core.test/src/.../generator/IssueCodeValueTest.xtend` +- `check.core.test/src/.../test/BasicModelTest.xtend` +- `check.core.test/src/.../test/BugAig830.xtend` +- `check.core.test/src/.../test/CheckScopingTest.xtend` +- `check.core.test/src/.../test/IssueCodeToLabelMapGenerationTest.xtend` +- `check.core.test/src/.../test/ProjectBasedTests.xtend` +- `check.core.test/src/.../test/util/CheckModelUtil.xtend` +- `check.core.test/src/.../test/util/CheckTestUtil.xtend` +- `check.core.test/src/.../formatting/CheckFormattingTest.xtend` +- `check.core.test/src/.../validation/CheckApiAccessValidationsTest.xtend` +- `check.core.test/src/.../validation/CheckValidationTest.xtend` +- `check.test.runtime/src/.../generator/TestLanguageGenerator.xtend` +- `check.test.runtime.tests/src/.../CheckConfigurationIsAppliedTest.xtend` +- `check.test.runtime.tests/src/.../CheckExecutionEnvironmentProjectTest.xtend` +- `check.test.runtime.tests/src/.../label/IssueLabelTest.xtend` +
+ +
+Slice 9 — Xtext test utilities + UI helpers (5 files) + +- `xtext.test.core/src/.../resource/AbstractResourceDescriptionManagerTest.xtend` +- `xtext.test.core/src/.../Tag.xtend` +- `xtext.ui/src/.../templates/TemplateProposalProviderHelper.xtend` +- `xtext.check.generator/src/.../CheckValidatorFragment2.xtend` +- `xtext.check.generator/src/.../quickfix/CheckQuickfixProviderFragment2.xtend` +
+ +
+Slice 10 — xtext.generator parser group (8 files) + +- `xtext.generator/src/.../parser/antlr/AbstractAnnotationAwareAntlrGrammarGenerator.xtend` +- `xtext.generator/src/.../parser/antlr/AnnotationAwareAntlrContentAssistGrammarGenerator.xtend` +- `xtext.generator/src/.../parser/antlr/AnnotationAwareAntlrGrammarGenerator.xtend` +- `xtext.generator/src/.../parser/antlr/AnnotationAwareXtextAntlrGeneratorFragment2.xtend` +- `xtext.generator/src/.../parser/common/GrammarRuleAnnotations.xtend` +- `xtext.generator/src/.../parser/common/PredicatesNaming.xtend` +- `xtext.generator/src/.../BundleVersionStripperFragment.xtend` +- `xtext.generator/src/.../DefaultFragmentWithOverride.xtend` +
+ +
+Slice 11 — xtext.generator builder & misc (10 files) + +- `xtext.generator/src/.../builder/BuilderIntegrationFragment2.xtend` +- `xtext.generator/src/.../builder/LspBuilderIntegrationFragment2.xtend` +- `xtext.generator/src/.../builder/StandaloneBuilderIntegrationFragment2.xtend` +- `xtext.generator/src/.../formatting/FormatterFragment2.xtend` +- `xtext.generator/src/.../languageconstants/LanguageConstantsFragment2.xtend` +- `xtext.generator/src/.../modelinference/ModelInferenceFragment2.xtend` +- `xtext.generator/src/.../model/project/ProjectConfig.xtend` +- `xtext.generator/src/.../resourceFactory/ResourceFactoryFragment2.xtend` +- `xtext.generator/src/.../ui/compare/CompareFragment2.xtend` +- `xtext.generator/src/.../ui/contentAssist/AnnotationAwareContentAssistFragment2.xtend` +
+ +
+Slice 12 — Cleanup (infrastructure only, 0 xtend files) + +This slice removes every remaining trace of Xtend tooling now that no `.xtend` sources exist: + +- `ddk-parent/pom.xml` — remove `xtend-maven-plugin`, `xtend-gen` source directory config, Xtend dependency versions +- Per-plugin `pom.xml` — remove any residual Xtend plugin blocks +- Per-plugin `MANIFEST.MF` — remove `Import-Package: org.eclipse.xtend.*` / `org.eclipse.xtext.xbase.lib` entries that are no longer used +- Per-plugin `.classpath` — remove `xtend-gen` source folder entries +- Per-plugin `.project` — remove Xtend nature, Xtend incremental project builder +- `releng/**/feature.xml` — remove `org.eclipse.xtend.lib` and related bundles from the product features +- Delete every `xtend-gen/` directory +- `ddk-configuration/pmd/ruleset.xml` — remove `.*/xtend-gen/.*` exclude pattern (line 11 today) +- `ddk-configuration/checkstyle/**` — same cleanup if any xtend-gen exclusions exist +- Verify no remaining reference to `org.eclipse.xtend` or `xtend-gen` in the tree: + ```bash + rg -t xml -t java -t properties 'org\.eclipse\.xtend|xtend-gen' + ``` +
+ +## Known pitfalls from the migration work + +Documented here so each reviewer doesn't have to rediscover them: + +- **`CoreException` handling** — Xtend silently wraps checked exceptions; Java doesn't. Several files needed explicit `try`/`catch` added (see commit `a5deb4e3c`). +- **PMD `UseCollectionIsEmpty`** — Xtend's `.isEmpty` translates to `.isEmpty()` but chained differently in a few places. Watch for `.size() == 0` patterns that should be `.isEmpty()`. +- **PMD `UnnecessaryBoxing`** — Xtend auto-boxes aggressively. The converter sometimes leaves `Integer.valueOf(i)` where a primitive works. +- **PMD `StringToString`**, **`UnnecessaryCast`**, **`MissingOverride`**, **`LooseCoupling`** — all hit during migration cleanup. Make sure any new violations get caught in slice review, not after merge. +- **`BasicEList` compilation errors in test code** — `XbaseGeneratorFragmentTest` needed a type-parameter fix (commit `3517ba896`). Test code that does generic-heavy collection building needs extra attention. +- **Text blocks vs. `StringBuilder`** — the later commits in this branch converted `StringBuilder`-heavy generators to text blocks / `String.format` (`da8c8d91b`, `71afbe9db`, `a5cb80dec`). Make sure any `StringBuilder` that survives is actually necessary (loop/branch). +- **`val` leaks** — Xtend's `val` always converts to `final Type`, but the converter sometimes uses a pseudo-general type. Enforce explicit types or `var`. +- **Non-parameterized SLF4J logging** — mostly cleaned up project-wide, but migration-era Xtend files had `logger.info("msg" + x)` patterns that should now be `logger.info("msg {}", x)`. + +## Rollback plan + +If a merged slice turns out to break a downstream consumer, revert the slice's merge commit (`git revert -m 1 `) and reopen the slice branch for rework. Do **not** revert files individually — each slice is one coherent unit by module. + +## Branch hygiene + +- This file lives on `feature/xtend-to-java-migration`; update the **status** column on master-merge days by pushing a commit here. +- The slice branches themselves (`migrate/xtend-to-java/`) can be deleted after merge. +- Once slice 12 ships, this branch itself is redundant — keep it around until the roadmap says all slices are ✅ merged, then delete. diff --git a/com.avaloq.tools.ddk.check.ide/.classpath b/com.avaloq.tools.ddk.check.ide/.classpath index 0f2735e0da..657ce4953b 100644 --- a/com.avaloq.tools.ddk.check.ide/.classpath +++ b/com.avaloq.tools.ddk.check.ide/.classpath @@ -6,11 +6,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.check.ide/build.properties b/com.avaloq.tools.ddk.check.ide/build.properties index bef28d3d93..ec65859a4c 100644 --- a/com.avaloq.tools.ddk.check.ide/build.properties +++ b/com.avaloq.tools.ddk.check.ide/build.properties @@ -1,5 +1,4 @@ source.. = src/,\ - src-gen/,\ - xtend-gen/ + src-gen/ bin.includes = META-INF/,\ . diff --git a/com.avaloq.tools.ddk.check.ide/xtend-gen/.gitignore b/com.avaloq.tools.ddk.check.ide/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/com.avaloq.tools.ddk.check.ui/.classpath b/com.avaloq.tools.ddk.check.ui/.classpath index 0f2735e0da..657ce4953b 100644 --- a/com.avaloq.tools.ddk.check.ui/.classpath +++ b/com.avaloq.tools.ddk.check.ui/.classpath @@ -6,11 +6,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.check.ui/build.properties b/com.avaloq.tools.ddk.check.ui/build.properties index 8a4b3d3003..59c14ebec6 100644 --- a/com.avaloq.tools.ddk.check.ui/build.properties +++ b/com.avaloq.tools.ddk.check.ui/build.properties @@ -1,6 +1,5 @@ source.. = src/,\ - src-gen/,\ - xtend-gen/ + src-gen/ bin.includes = META-INF/,\ icons/,\ .,\ diff --git a/com.avaloq.tools.ddk.check.ui/xtend-gen/.gitignore b/com.avaloq.tools.ddk.check.ui/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/com.avaloq.tools.ddk.xtext.export.ide/.classpath b/com.avaloq.tools.ddk.xtext.export.ide/.classpath index fa7306227a..2e254f7cff 100644 --- a/com.avaloq.tools.ddk.xtext.export.ide/.classpath +++ b/com.avaloq.tools.ddk.xtext.export.ide/.classpath @@ -6,11 +6,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.xtext.export.ide/.project b/com.avaloq.tools.ddk.xtext.export.ide/.project index 92c9d23c37..1775943899 100644 --- a/com.avaloq.tools.ddk.xtext.export.ide/.project +++ b/com.avaloq.tools.ddk.xtext.export.ide/.project @@ -65,35 +65,5 @@ 1 PARENT-1-PROJECT_LOC/ddk-configuration/.pmd - - .settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - - - .settings/org.eclipse.core.resources.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.resources.prefs - - - .settings/org.eclipse.core.runtime.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.runtime.prefs - - - .settings/org.eclipse.jdt.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.core.prefs - - - .settings/org.eclipse.jdt.ui.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.ui.prefs - - - .settings/org.eclipse.pde.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.pde.core.prefs - diff --git a/com.avaloq.tools.ddk.xtext.export.ide/build.properties b/com.avaloq.tools.ddk.xtext.export.ide/build.properties index bef28d3d93..ec65859a4c 100644 --- a/com.avaloq.tools.ddk.xtext.export.ide/build.properties +++ b/com.avaloq.tools.ddk.xtext.export.ide/build.properties @@ -1,5 +1,4 @@ source.. = src/,\ - src-gen/,\ - xtend-gen/ + src-gen/ bin.includes = META-INF/,\ . diff --git a/com.avaloq.tools.ddk.xtext.export.ide/xtend-gen/.gitignore b/com.avaloq.tools.ddk.xtext.export.ide/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/com.avaloq.tools.ddk.xtext.expression.ide/.classpath b/com.avaloq.tools.ddk.xtext.expression.ide/.classpath index fa7306227a..2e254f7cff 100644 --- a/com.avaloq.tools.ddk.xtext.expression.ide/.classpath +++ b/com.avaloq.tools.ddk.xtext.expression.ide/.classpath @@ -6,11 +6,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.xtext.expression.ide/.project b/com.avaloq.tools.ddk.xtext.expression.ide/.project index 9714f39bf4..61badff6ee 100644 --- a/com.avaloq.tools.ddk.xtext.expression.ide/.project +++ b/com.avaloq.tools.ddk.xtext.expression.ide/.project @@ -65,35 +65,5 @@ 1 PARENT-1-PROJECT_LOC/ddk-configuration/.pmd - - .settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - - - .settings/org.eclipse.core.resources.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.resources.prefs - - - .settings/org.eclipse.core.runtime.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.runtime.prefs - - - .settings/org.eclipse.jdt.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.core.prefs - - - .settings/org.eclipse.jdt.ui.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.ui.prefs - - - .settings/org.eclipse.pde.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.pde.core.prefs - diff --git a/com.avaloq.tools.ddk.xtext.expression.ide/build.properties b/com.avaloq.tools.ddk.xtext.expression.ide/build.properties index bef28d3d93..ec65859a4c 100644 --- a/com.avaloq.tools.ddk.xtext.expression.ide/build.properties +++ b/com.avaloq.tools.ddk.xtext.expression.ide/build.properties @@ -1,5 +1,4 @@ source.. = src/,\ - src-gen/,\ - xtend-gen/ + src-gen/ bin.includes = META-INF/,\ . diff --git a/com.avaloq.tools.ddk.xtext.expression.ide/xtend-gen/.gitignore b/com.avaloq.tools.ddk.xtext.expression.ide/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/com.avaloq.tools.ddk.xtext.generator/.classpath b/com.avaloq.tools.ddk.xtext.generator/.classpath index 3315c338c4..0fbe7e871c 100644 --- a/com.avaloq.tools.ddk.xtext.generator/.classpath +++ b/com.avaloq.tools.ddk.xtext.generator/.classpath @@ -1,11 +1,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.xtext.generator/build.properties b/com.avaloq.tools.ddk.xtext.generator/build.properties index d8e2f0e92e..792f9b824e 100644 --- a/com.avaloq.tools.ddk.xtext.generator/build.properties +++ b/com.avaloq.tools.ddk.xtext.generator/build.properties @@ -1,5 +1,4 @@ source.. = src/,\ - xtend-gen/ output.. = bin/ bin.includes = META-INF/,\ . diff --git a/com.avaloq.tools.ddk.xtext.generator/xtend-gen/.gitignore b/com.avaloq.tools.ddk.xtext.generator/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/com.avaloq.tools.ddk.xtext.scope.ide/.classpath b/com.avaloq.tools.ddk.xtext.scope.ide/.classpath index fa7306227a..2e254f7cff 100644 --- a/com.avaloq.tools.ddk.xtext.scope.ide/.classpath +++ b/com.avaloq.tools.ddk.xtext.scope.ide/.classpath @@ -6,11 +6,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.xtext.scope.ide/.project b/com.avaloq.tools.ddk.xtext.scope.ide/.project index 71dd8ce3d0..48c6a75eb4 100644 --- a/com.avaloq.tools.ddk.xtext.scope.ide/.project +++ b/com.avaloq.tools.ddk.xtext.scope.ide/.project @@ -65,35 +65,5 @@ 1 PARENT-1-PROJECT_LOC/ddk-configuration/.pmd - - .settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - - - .settings/org.eclipse.core.resources.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.resources.prefs - - - .settings/org.eclipse.core.runtime.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.runtime.prefs - - - .settings/org.eclipse.jdt.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.core.prefs - - - .settings/org.eclipse.jdt.ui.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.ui.prefs - - - .settings/org.eclipse.pde.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.pde.core.prefs - diff --git a/com.avaloq.tools.ddk.xtext.scope.ide/build.properties b/com.avaloq.tools.ddk.xtext.scope.ide/build.properties index bef28d3d93..ec65859a4c 100644 --- a/com.avaloq.tools.ddk.xtext.scope.ide/build.properties +++ b/com.avaloq.tools.ddk.xtext.scope.ide/build.properties @@ -1,5 +1,4 @@ source.. = src/,\ - src-gen/,\ - xtend-gen/ + src-gen/ bin.includes = META-INF/,\ . diff --git a/com.avaloq.tools.ddk.xtext.scope.ide/xtend-gen/.gitignore b/com.avaloq.tools.ddk.xtext.scope.ide/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/com.avaloq.tools.ddk.xtext.test.core/.classpath b/com.avaloq.tools.ddk.xtext.test.core/.classpath index 907a12b9cb..71aee59a71 100644 --- a/com.avaloq.tools.ddk.xtext.test.core/.classpath +++ b/com.avaloq.tools.ddk.xtext.test.core/.classpath @@ -1,11 +1,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.xtext.test.core/META-INF/MANIFEST.MF b/com.avaloq.tools.ddk.xtext.test.core/META-INF/MANIFEST.MF index 8d850705a8..54db6588e8 100644 --- a/com.avaloq.tools.ddk.xtext.test.core/META-INF/MANIFEST.MF +++ b/com.avaloq.tools.ddk.xtext.test.core/META-INF/MANIFEST.MF @@ -13,7 +13,6 @@ Require-Bundle: com.avaloq.tools.ddk.xtext, org.eclipse.jdt.core, org.eclipse.pde.core, org.eclipse.ui.ide, - org.eclipse.xtend.lib, org.eclipse.xtext.testing;visibility:=reexport, org.eclipse.xtext.ui.testing;visibility:=reexport, org.eclipse.xtext.ui, diff --git a/com.avaloq.tools.ddk.xtext.test.core/build.properties b/com.avaloq.tools.ddk.xtext.test.core/build.properties index ea832598c9..b107977f4e 100644 --- a/com.avaloq.tools.ddk.xtext.test.core/build.properties +++ b/com.avaloq.tools.ddk.xtext.test.core/build.properties @@ -1,4 +1,3 @@ -source.. = src/,\ - xtend-gen/ +source.. = src/ bin.includes = META-INF/,\ . diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/Tag.xtend b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/Tag.java similarity index 75% rename from com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/Tag.xtend rename to com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/Tag.java index 892e50810b..5cc4a24878 100644 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/Tag.xtend +++ b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/Tag.java @@ -8,19 +8,20 @@ * Contributors: * Avaloq Group AG - initial API and implementation *******************************************************************************/ -package com.avaloq.tools.ddk.xtext.test +package com.avaloq.tools.ddk.xtext.test; -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy -import org.eclipse.xtend.lib.macro.Active +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.eclipse.xtend.lib.macro.Active; /** * Initializes global tags in linking tests. * The annotated field must be of integer type. * Usage example: @Tag int MEM_DOC */ -@Active(typeof(TagCompilationParticipant)) +@Active(TagCompilationParticipant.class) @Retention(RetentionPolicy.RUNTIME) -annotation Tag { +public @interface Tag { } diff --git a/com.avaloq.tools.ddk.xtext.test.core/xtend-gen/.gitignore b/com.avaloq.tools.ddk.xtext.test.core/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/com.avaloq.tools.ddk.xtext.valid.ide/.classpath b/com.avaloq.tools.ddk.xtext.valid.ide/.classpath index fa7306227a..2e254f7cff 100644 --- a/com.avaloq.tools.ddk.xtext.valid.ide/.classpath +++ b/com.avaloq.tools.ddk.xtext.valid.ide/.classpath @@ -6,11 +6,6 @@ - - - - - diff --git a/com.avaloq.tools.ddk.xtext.valid.ide/.project b/com.avaloq.tools.ddk.xtext.valid.ide/.project index acb4841b6c..9778b1bec4 100644 --- a/com.avaloq.tools.ddk.xtext.valid.ide/.project +++ b/com.avaloq.tools.ddk.xtext.valid.ide/.project @@ -65,35 +65,5 @@ 1 PARENT-1-PROJECT_LOC/ddk-configuration/.pmd - - .settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/edu.umd.cs.findbugs.plugin.eclipse.prefs - - - .settings/org.eclipse.core.resources.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.resources.prefs - - - .settings/org.eclipse.core.runtime.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.core.runtime.prefs - - - .settings/org.eclipse.jdt.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.core.prefs - - - .settings/org.eclipse.jdt.ui.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.jdt.ui.prefs - - - .settings/org.eclipse.pde.core.prefs - 1 - PARENT-1-PROJECT_LOC/ddk-configuration/.settings/org.eclipse.pde.core.prefs - diff --git a/com.avaloq.tools.ddk.xtext.valid.ide/build.properties b/com.avaloq.tools.ddk.xtext.valid.ide/build.properties index bef28d3d93..ec65859a4c 100644 --- a/com.avaloq.tools.ddk.xtext.valid.ide/build.properties +++ b/com.avaloq.tools.ddk.xtext.valid.ide/build.properties @@ -1,5 +1,4 @@ source.. = src/,\ - src-gen/,\ - xtend-gen/ + src-gen/ bin.includes = META-INF/,\ . diff --git a/com.avaloq.tools.ddk.xtext.valid.ide/xtend-gen/.gitignore b/com.avaloq.tools.ddk.xtext.valid.ide/xtend-gen/.gitignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ddk-configuration/.checkstyle b/ddk-configuration/.checkstyle index 8248743687..ab5c6246d0 100644 --- a/ddk-configuration/.checkstyle +++ b/ddk-configuration/.checkstyle @@ -10,7 +10,6 @@ - diff --git a/ddk-configuration/pmd/ruleset.xml b/ddk-configuration/pmd/ruleset.xml index 8277c89135..c2bbd02f5f 100644 --- a/ddk-configuration/pmd/ruleset.xml +++ b/ddk-configuration/pmd/ruleset.xml @@ -9,7 +9,6 @@ .*/src-gen/.* .*/src-model/.* - .*/xtend-gen/.* .*/ddk/xtext/test/ui/quickfix/AbstractQuickFixTest.* diff --git a/docs/xtend-migration.md b/docs/xtend-migration.md new file mode 100644 index 0000000000..70d20e1438 --- /dev/null +++ b/docs/xtend-migration.md @@ -0,0 +1,337 @@ +# Xtend-to-Java Migration Tracker + +## Decisions + +- **Target**: Java 21 (pattern matching, switch expressions, text blocks, records) +- **Template strategy**: `StringBuilder` (pure Java, no Xtend runtime dependency) +- **No `var` keyword**: Always use explicit types (`final ExplicitType`, not `final var`) +- **Conversion prompt**: [`docs/xtend-to-java-conversion-prompt.md`](xtend-to-java-conversion-prompt.md) + +## Summary + +| Metric | Value | +|--------|-------| +| Total Xtend source files | 94 | +| Already migrated (Batch 1–9) | 94 | +| Remaining | 0 | +| Total remaining lines | 0 | +| Modules with remaining Xtend | 0 | + +--- + +## Module Overview + +| Module | Files | Lines | Status | +|--------|-------|-------|--------| +| `check.core` | 8 | ~1,848 | **DONE** (Batch 1) | +| `check.core.test` | 11 | ~1,760 | **DONE** (Batch 2–4, 9) | +| `check.test.runtime` | 1 | 22 | **DONE** (Batch 2) | +| `check.test.runtime.tests` | 3 | 202 | **DONE** (Batch 3–4) | +| `check.ui` | 2 | 113 | **DONE** (Batch 3) | +| `check.ui.test` | 1 | 200 | **DONE** (Batch 9) | +| `checkcfg.core` | 4 | 303 | **DONE** (Batch 2–4) | +| `checkcfg.core.test` | 7 | 460 | **DONE** (Batch 2–4) | +| `sample.helloworld.ui.test` | 3 | 203 | **DONE** (Batch 3–4) | +| `xtext.check.generator` | 2 | 113 | **DONE** (Batch 2–3) | +| `xtext.export` | 9 | 1,027 | **DONE** (Batch 5) | +| `xtext.export.generator` | 1 | 86 | **DONE** (Batch 4) | +| `xtext.expression` | 5 | 679 | **DONE** (Batch 2, 6) | +| `xtext.format` | 6 | 1,623 | **DONE** (Batch 2, 7) | +| `xtext.format.generator` | 1 | 239 | **DONE** (Batch 7) | +| `xtext.format.ide` | 2 | 31 | **DONE** (Batch 2) | +| `xtext.format.test` | 1 | 40 | **DONE** (Batch 3) | +| `xtext.format.ui` | 1 | 47 | **DONE** (Batch 2) | +| `xtext.generator` | 18 | 3,450 | **DONE** (Batch 2, 8) | +| `xtext.generator.test` | 1 | 200 | **DONE** (Batch 8) | +| `xtext.scope` | 4 | 852 | **DONE** (Batch 6) | +| `xtext.scope.generator` | 1 | 47 | **DONE** (Batch 4) | +| `xtext.test.core` | 2 | 221 | **DONE** (Batch 4, 6) | +| `xtext.ui` | 1 | 82 | **DONE** (Batch 6) | +| `xtext.ui.test` | 1 | 265 | **DONE** (Batch 6) | + +All module names are prefixed with `com.avaloq.tools.ddk.` (omitted for brevity). + +--- + +## Batch 1 — `check.core` (8 files) — DONE + +- [x] `CheckGeneratorConfig.xtend` (20 lines) — Trivial +- [x] `CheckGeneratorNaming.xtend` (~80 lines) — Easy +- [x] `CheckTypeComputer.xtend` (~80 lines) — Easy +- [x] `CheckScopeProvider.xtend` (~100 lines) — Easy +- [x] `CheckGenerator.xtend` (216 lines) — Hard — templates, `@Inject extension` +- [x] `CheckGeneratorExtensions.xtend` (268 lines) — Hard — dispatch, templates +- [x] `CheckFormatter.xtend` (302 lines) — Hard — dispatch +- [x] `CheckJvmModelInferrer.xtend` (647 lines) — Very Hard — dispatch, templates, extensions + +--- + +## Batch 2 — Trivial files ≤50 lines (~14 files) — DONE + +Small setup classes, empty modules, simple overrides. + +### `check.test.runtime` (1 file) +- [x] `TestLanguageGenerator.xtend` (22 lines) — Trivial — override + +### `xtext.format.ide` (2 files) +- [x] `FormatIdeModule.xtend` (11 lines) — Trivial — no complex features +- [x] `FormatIdeSetup.xtend` (20 lines) — Trivial — override + +### `xtext.format` (1 file) +- [x] `FormatStandaloneSetup.xtend` (15 lines) — Trivial — extension + +### `xtext.expression` (2 files) +- [x] `GeneratorUtilX.xtend` (29 lines) — Trivial — no complex features +- [x] `Naming.xtend` (30 lines) — Trivial — no complex features + +### `xtext.check.generator` (1 file) +- [x] `CheckValidatorFragment2.xtend` (31 lines) — Trivial — extension, !==, override + +### `checkcfg.core.test` (2 files) +- [x] `CheckCfgTestUtil.xtend` (32 lines) — Trivial — override +- [x] `CheckCfgModelUtil.xtend` (42 lines) — Trivial — templates + +### `checkcfg.core` (1 file) +- [x] `CheckCfgJvmModelInferrer.xtend` (45 lines) — Trivial — templates, extension, @Inject + +### `xtext.format.test` (1 file) +- [x] `FormatParsingTest.xtend` (40 lines) — Trivial — templates, @Inject + +### `xtext.format.ui` (1 file) +- [x] `FormatUiModule.xtend` (47 lines) — Trivial — override + +### `xtext.generator` (2 files) +- [x] `BundleVersionStripperFragment.xtend` (47 lines) — Trivial — typeof, @Accessors +- [x] `ProjectConfig.xtend` (48 lines) — Trivial — templates, @Accessors, switch + +--- + +## Batch 3 — Easy files 50–100 lines (~14 files) — DONE + +Simple test files, utilities, small production code. + +### `check.core.test` (3 files) +- [x] `BugAig830.xtend` (56 lines) — Easy — templates, @Inject +- [x] `CheckTestUtil.xtend` (72 lines) — Easy — ===, !== +- [x] `CheckScopingTest.xtend` (81 lines) — Easy — extension, typeof, @Inject + +### `check.test.runtime.tests` (2 files) +- [x] `IssueLabelTest.xtend` (56 lines) — Easy — #{, override +- [x] `CheckConfigurationIsAppliedTest.xtend` (64 lines) — Easy — extension, typeof, @Inject, override + +### `check.ui` (2 files) +- [x] `CheckNewProject.xtend` (50 lines) — Easy — templates, !== +- [x] `CheckQuickfixProvider.xtend` (63 lines) — Easy — templates + +### `checkcfg.core` (2 files) +- [x] `CheckCfgGenerator.xtend` (53 lines) — Easy — templates, typeof, @Inject, override +- [x] `ConfiguredParameterChecks.xtend` (66 lines) — Easy — templates, ===, !==, ?. + +### `checkcfg.core.test` (2 files) +- [x] `CheckCfgConfiguredParameterValidationsTest.xtend` (63 lines) — Easy — templates, extension, override +- [x] `CheckCfgTest.xtend` (63 lines) — Easy — templates, typeof, @Inject + +### `sample.helloworld.ui.test` (2 files) +- [x] `IssueLabelTest.xtend` (56 lines) — Easy — #{, override +- [x] `CheckConfigurationIsAppliedTest.xtend` (64 lines) — Easy — extension, typeof, @Inject, override + +### `xtext.check.generator` (1 file) +- [x] `CheckQuickfixProviderFragment2.xtend` (82 lines) — Easy — templates, extension, @Inject + +--- + +## Batch 4 — Medium prod + test files 80–140 lines (13 files) — DONE + +### `check.core.test` (4 files) +- [x] `ProjectBasedTests.xtend` (88 lines) — Easy — extension, typeof, @Inject, override +- [x] `IssueCodeValueTest.xtend` (104 lines) — Medium — templates, #{, switch +- [x] `CheckApiAccessValidationsTest.xtend` (61 lines) — Easy — templates, @Inject +- [x] `BasicModelTest.xtend` (116 lines) — Medium — extension, typeof, @Inject + +### `check.test.runtime.tests` (1 file) +- [x] `CheckExecutionEnvironmentProjectTest.xtend` (82 lines) — Easy — extension, typeof, @Inject, override + +### `checkcfg.core` (1 file) +- [x] `PropertiesInferenceHelper.xtend` (139 lines) — Medium — typeof, ===, !==, switch, create + +### `checkcfg.core.test` (3 files) +- [x] `CheckCfgContentAssistTest.xtend` (84 lines) — Easy — templates, extension, @Inject, override +- [x] `CheckCfgScopeProviderTest.xtend` (77 lines) — Easy — templates, ===, #[, override +- [x] `CheckCfgSyntaxTest.xtend` (99 lines) — Easy — templates, #[, override + +### `sample.helloworld.ui.test` (1 file) +- [x] `CheckExecutionEnvironmentProjectTest.xtend` (83 lines) — Easy — extension, typeof, @Inject, override + +### `xtext.export.generator` (1 file) +- [x] `ExportFragment2.xtend` (86 lines) — Medium — templates, extension, !==, @Inject, override + +### `xtext.scope.generator` (1 file) +- [x] `ScopingFragment2.xtend` (47 lines) — Trivial — extension, !==, override + +### `xtext.test.core` (1 file) +- [x] `Tag.xtend` (23 lines) — Trivial — typeof + +--- + +## Batch 5 — `xtext.export` module (9 files, 1,027 lines) — DONE + +Code generators with templates and some dispatch methods. + +- [x] `ResourceDescriptionConstantsGenerator.xtend` (54 lines) — Easy — templates, extension, @Inject +- [x] `ExportFeatureExtensionGenerator.xtend` (77 lines) — Easy — templates, extension, @Inject +- [x] `ResourceDescriptionManagerGenerator.xtend` (59 lines) — Easy — templates, extension, !==, @Inject +- [x] `ExportedNamesProviderGenerator.xtend` (96 lines) — Medium — templates, extension, !==, ?., switch +- [x] `FragmentProviderGenerator.xtend` (94 lines) — Medium — templates, extension, !==, switch +- [x] `FingerprintComputerGenerator.xtend` (134 lines) — Medium — **dispatch**, templates, extension, @Inject +- [x] `ExportGenerator.xtend` (136 lines) — Medium — templates, extension, ===, !==, @Inject, override +- [x] `ExportGeneratorX.xtend` (187 lines) — Hard — **dispatch**, extension, !==, ?., @Inject +- [x] `ResourceDescriptionStrategyGenerator.xtend` (190 lines) — Hard — templates, extension, ===, !==, @Inject + +--- + +## Batch 6 — `xtext.expression` + `xtext.scope` + remaining small files (10 files) — DONE + +### `xtext.expression` (3 files) +- [x] `ExpressionExtensionsX.xtend` (87 lines) — Medium — **dispatch**, === +- [x] `GenModelUtilX.xtend` (160 lines) — Hard — **dispatch**, extension, !==, create +- [x] `CodeGenerationX.xtend` (373 lines) — Hard — **dispatch**, extension, ===, !==, #[ + +### `xtext.scope` (4 files) +- [x] `ScopeGenerator.xtend` (83 lines) — Medium — extension, ===, !==, @Inject, override +- [x] `ScopeNameProviderGenerator.xtend` (136 lines) — Medium — **dispatch**, templates, extension, switch +- [x] `ScopeProviderX.xtend` (247 lines) — Hard — **dispatch**, extension, ===, !==, @Inject +- [x] `ScopeProviderGenerator.xtend` (386 lines) — Hard — **dispatch**, templates, extension, ===, !==, #[, switch + +### `xtext.test.core` (1 file) +- [x] `AbstractResourceDescriptionManagerTest.xtend` (198 lines) — Medium — ===, override, create + +### `xtext.ui` (1 file) +- [x] `TemplateProposalProviderHelper.xtend` (82 lines) — Medium — templates, #[ + +### `xtext.ui.test` (1 file) +- [x] `TemplateProposalProviderHelperTest.xtend` (265 lines) — Medium — templates, #[ + +--- + +## Batch 7 — `xtext.format` module (6 files, 1,847 lines) — DONE + +Includes the largest file in the project. Heavy use of dispatch, templates, create methods. + +### `xtext.format` (5 files) +- [x] `FormatRuntimeModule.xtend` (115 lines) — Medium — extension, override +- [x] `FormatGenerator.xtend` (93 lines) — Medium — **dispatch**, templates, extension, typeof, @Inject, override +- [x] `FormatScopeProvider.xtend` (258 lines) — Hard — **dispatch**, typeof, ===, !==, create +- [x] `FormatValidator.xtend` (376 lines) — Hard — ===, !==, override +- [x] **`FormatJvmModelInferrer.xtend` (766 lines) — Very Hard** — dispatch, templates, extension, typeof, ===, !==, ?., #[, switch, create + +### `xtext.format.generator` (1 file) +- [x] `FormatFragment2.xtend` (239 lines) — Hard — templates, extension, typeof, !==, ?., @Inject, override + +--- + +## Batch 8 — `xtext.generator` module (17 files, 3,555 lines) — DONE + +The largest module. Includes ANTLR grammar generators — the hardest files in the project. + +### Simple (4 files) +- [x] `PredicatesNaming.xtend` (35 lines) — Trivial — extension, @Inject +- [x] `ModelInferenceFragment2.xtend` (49 lines) — Trivial — extension, !==, override +- [x] `DefaultFragmentWithOverride.xtend` (54 lines) — Easy — ?., override, @Accessors +- [x] `BuilderIntegrationFragment2.xtend` (60 lines) — Easy — templates, extension, !==, override + +### Medium (5 files) +- [x] `ResourceFactoryFragment2.xtend` (76 lines) — Medium — templates, extension, !==, ?., @Accessors +- [x] `CompareFragment2.xtend` (99 lines) — Medium — templates, extension, !==, @Inject +- [x] `LanguageConstantsFragment2.xtend` (144 lines) — Medium — templates, extension, !==, ?., @Accessors +- [x] `FormatterFragment2.xtend` (147 lines) — Medium — templates, extension, typeof, !==, ?., @Inject +- [x] `XbaseGeneratorFragmentTest.xtend` (200 lines) — Medium — extension + +### Hard - Builder fragments (2 files) +- [x] `StandaloneBuilderIntegrationFragment2.xtend` (165 lines) — Hard — templates, extension, @Inject +- [x] `LspBuilderIntegrationFragment2.xtend` (174 lines) — Hard — templates, extension, @Inject + +### Hard - Content assist (1 file) +- [x] `AnnotationAwareContentAssistFragment2.xtend` (226 lines) — Hard — **dispatch**, templates, extension, !==, ?., @Accessors + +### Very Hard - ANTLR generators (4 files) +- [x] `AbstractAnnotationAwareAntlrGrammarGenerator.xtend` (159 lines) — Hard — templates, extension, @Inject +- [x] `GrammarRuleAnnotations.xtend` (406 lines) — Very Hard — templates, ===, !==, ?., @Data +- [x] `AnnotationAwareAntlrContentAssistGrammarGenerator.xtend` (489 lines) — Very Hard — **dispatch**, templates, extension, === +- [x] `AnnotationAwareAntlrGrammarGenerator.xtend` (543 lines) — Very Hard — **dispatch**, templates, extension, !==, @Accessors, switch, create +- [x] `AnnotationAwareXtextAntlrGeneratorFragment2.xtend` (529 lines) — Very Hard — templates, extension, ===, !==, #[, @Accessors, create + +--- + +## Batch 9 — Remaining test files (5 files) — DONE + +### `check.core.test` (4 files) +- [x] `CheckModelUtil.xtend` (113 lines) — Medium — templates +- [x] `IssueCodeToLabelMapGenerationTest.xtend` (130 lines) — Medium — templates, #[, switch +- [x] `CheckValidationTest.xtend` (342 lines) — Hard — extension, typeof, create +- [x] `CheckFormattingTest.xtend` (554 lines) — Very Hard — templates, extension, typeof, !==, ?. + +### `check.ui.test` (1 file) +- [x] `CheckQuickfixTest.xtend` (200 lines) — Medium — templates, #[, override + +--- + +## Build Config Cleanup (after all files migrated) + +- [ ] Remove `xtend-maven-plugin` from `ddk-parent/pom.xml` +- [ ] Remove `xtend.version` property from `ddk-parent/pom.xml` +- [ ] Remove PMD `excludeRoot` for xtend-gen from `ddk-parent/pom.xml` +- [ ] Remove clean plugin xtend-gen fileset from `ddk-parent/pom.xml` +- [ ] Remove `org.eclipse.xtend.lib` from ~10 MANIFEST.MF files +- [ ] Remove `xtend-gen` source entries from ~31 `.classpath` files +- [ ] Remove `xtend-gen` from ~31 `build.properties` files +- [ ] Update `.gitignore` to remove xtend-gen patterns +- [ ] Delete all `xtend-gen/` directories +- [ ] Final full build + test verification + +--- + +## Verification Protocol + +After each batch: + +1. **Compile**: `mvn clean compile -f ./ddk-parent/pom.xml --batch-mode` — must pass +2. **Test**: `mvn clean verify -f ./ddk-parent/pom.xml --batch-mode --fail-at-end` — must pass (except known UI test on macOS) +3. **Update** this checklist — mark converted files as done +4. **Commit** the batch + +--- + +## Conversion Rules Quick Reference + +| Xtend | Java | +|-------|------| +| `val x = expr` | `final ExplicitType x = expr;` | +| `var x = expr` | `ExplicitType x = expr;` | +| `def method()` | `public ReturnType method()` | +| `override method()` | `@Override public ReturnType method()` | +| `typeof(X)` | `X.class` | +| `===` / `!==` | `==` / `!=` | +| `obj?.method()` | null check or ternary | +| `[x \| body]` | `(x) -> body` | +| `dispatch method(T x)` | `_method(T x)` + dispatcher with `instanceof` | +| `@Inject extension Foo` | `@Inject private Foo foo;` + rewrite call sites | +| `'''template «expr»'''` | `StringBuilder` with `.append()` | +| `#[]` / `#{}` | `List.of()` / `Set.of()` | +| `obj.name` (property) | `obj.getName()` | +| `list += x` | `list.add(x)` | +| `a ?: b` | `a != null ? a : b` | +| `expr as Type` | `(Type) expr` or pattern matching | + +Full conversion prompt: [`docs/xtend-to-java-conversion-prompt.md`](xtend-to-java-conversion-prompt.md) + +--- + +## Complexity Legend + +| Rating | Criteria | +|--------|----------| +| **Trivial** | ≤30 lines, no complex Xtend features | +| **Easy** | ≤100 lines, basic features (val, override, @Inject, simple templates) | +| **Medium** | 100–200 lines, or uses templates + extension methods + switch | +| **Hard** | 200–400 lines with dispatch/complex templates/extension methods | +| **Very Hard** | 400+ lines with dispatch + templates + extensions + create methods | diff --git a/docs/xtend-to-java-conversion-prompt.md b/docs/xtend-to-java-conversion-prompt.md new file mode 100644 index 0000000000..c68e1bab74 --- /dev/null +++ b/docs/xtend-to-java-conversion-prompt.md @@ -0,0 +1,631 @@ +# Xtend-to-Java Conversion Prompt + +You are an expert Java and Xtend developer. Your task is to convert a single Xtend (.xtend) file into idiomatic Java (.java) targeting Java 21. The file belongs to the dsl-devkit project, an Eclipse/Xtext-based DSL development kit that uses Google Guice for dependency injection. + +## INPUT + +You will receive the complete contents of one `.xtend` file. + +## OUTPUT + +Return the complete, compilable `.java` file. The output must: +- Be valid Java 21 code +- Compile without errors in the context of the dsl-devkit project +- Preserve all original functionality exactly +- Be idiomatic Java (NOT a mechanical translation) +- Include all necessary imports + +--- + +## DECISIONS + +- **Target: Java 21** (pattern matching, switch expressions, text blocks, records) +- **Template strategy: `StringBuilder`** (pure Java, no Xtend runtime dependency) +- **No `var` keyword**: Always use explicit types (no Java 10 `var` / `final var`) + - `val catalog = ...` -> `final CheckCatalog catalog = ...;` (NOT `final var catalog = ...;`) + - `var skip = ...` -> `int skip = ...;` (NOT `var skip = ...;`) + - All local variables, fields, and parameters must have explicit type declarations. + +--- + +## CONVERSION RULES + +Apply these rules systematically, in order. Every rule is mandatory. + +### 1. FILE STRUCTURE AND BASICS + +1.1. **Package declaration**: Keep identical. Add semicolon if missing. + +1.2. **Imports**: Convert all Xtend imports to Java imports. +- `import com.foo.Bar` stays as `import com.foo.Bar;` +- `import static com.foo.Bar.*` stays as `import static com.foo.Bar.*;` +- `import static extension com.foo.Bar.*` becomes `import static com.foo.Bar.*;` (the `extension` keyword is dropped; see Rule 8 for call-site conversion) +- Remove imports for Xtend-specific types that are no longer needed (e.g., `org.eclipse.xtend2.lib.StringConcatenation` if you use StringBuilder instead) +- Add any new imports needed by the Java code (e.g., `java.util.List`, `java.util.ArrayList`, `java.util.stream.*`, `java.util.Objects`) +- Do NOT add wildcard imports. Use explicit imports. +- Remove unused imports. + +1.3. **Class declaration**: +- Xtend classes are `public` by default. Add `public` explicitly. +- `class Foo extends Bar` becomes `public class Foo extends Bar` +- Xtend `interface` stays as `interface` (already public by default in Java too). +- Add `{` and `}` braces as normal Java. + +1.4. **Semicolons**: Add semicolons to all statements. Xtend allows omitting them; Java requires them. + +### 2. VARIABLE DECLARATIONS + +2.1. **`val` (final local variable)**: +- Always use explicit types: `final ExplicitType name = expr;` +- Example: `val catalog = EcoreUtil2.getContainerOfType(context, CheckCatalog.class)` becomes `final CheckCatalog catalog = EcoreUtil2.getContainerOfType(context, CheckCatalog.class);` + +2.2. **`var` (mutable local variable)**: +- Always use explicit types: `ExplicitType name = expr;` +- Example: `var skip = instance - 1` becomes `int skip = instance - 1;` + +2.3. **`val` fields (class-level final fields)**: +- `val String FOO = "bar"` becomes `private final String FOO = "bar";` +- `val static Logger LOGGER = ...` becomes `private static final Logger LOGGER = ...;` +- Always add explicit visibility (`private` unless a different visibility is needed). + +2.4. **`var` fields (class-level mutable fields)**: +- `var String name` becomes `private String name;` +- Add explicit visibility. + +### 3. METHOD DECLARATIONS + +3.1. **`def` methods**: +- `def` means `public` by default. Add explicit `public`. +- `def private`, `def protected`, `def package` keep their visibility. +- Add explicit return type. If the Xtend method omits the return type, infer it from the method body. +- Example: `def outputPath()` with body `'.settings'` becomes `public String outputPath()` + +3.2. **`override` keyword**: +- Replace `override` with `@Override` annotation plus the appropriate visibility modifier. +- `override void doGenerate(...)` becomes: + ```java + @Override + public void doGenerate(...) { + ``` +- `override protected doGenerate()` becomes: + ```java + @Override + protected void doGenerate() { + ``` + +3.3. **Return types and implicit returns**: +- Xtend methods return the value of the last expression. In Java, add explicit `return` statements. +- If a method's last expression is a value, wrap it in `return`. +- For `void` methods, no return is needed. +- Example: Xtend `def foo() { bar }` becomes Java `public SomeType foo() { return bar; }` + +3.4. **Method parameters**: +- Add `final` to parameters in methods that do not reassign them (follow the convention of the existing Java code in the project). +- `extension` parameters: see Rule 8. + +### 4. TYPE REFERENCES + +4.1. **`typeof(X)` to `X.class`**: +- Replace all `typeof(ClassName)` with `ClassName.class`. +- Example: `typeof(CheckCatalog)` becomes `CheckCatalog.class` + +4.2. **Generic type syntax**: Xtend and Java use the same generic syntax. Keep as-is. + +4.3. **Type casting**: `expr as Type` becomes `(Type) expr` or use pattern matching with `instanceof` (Java 21). + +### 5. OPERATORS AND EXPRESSIONS + +5.1. **Identity comparison**: +- `===` (identity equals) becomes `==` in Java. +- `!==` (identity not-equals) becomes `!=` in Java. +- `==` in Xtend is `.equals()`. Convert to `.equals()` or `Objects.equals()` in Java (use `Objects.equals()` when either operand could be null). + +5.2. **Null-safe navigation `?.`**: +- `obj?.method()` becomes a null check. Use one of: + - Ternary: `obj != null ? obj.method() : null` + - If-statement for complex cases + - For chained null-safe calls, nest the ternaries or use local variables: + ```java + // Xtend: resource?.URI + // Java: + final URI uri = resource != null ? resource.getURI() : null; + ``` + - IMPORTANT: If the result of `?.` is used in a comparison against null (e.g., `resource?.URI !== null`), then use: `resource != null && resource.getURI() != null` + +5.3. **Elvis operator `?:`**: +- `a ?: b` becomes `a != null ? a : b` (or `Objects.requireNonNullElse(a, b)` if appropriate). + +5.4. **`=>` operator (with/apply)**: +- `obj => [ body ]` executes `body` with `obj` as `it`, then returns `obj`. +- Convert to: + ```java + { // inline block or extract to a method + ExplicitType temp = obj; + // body, replacing `it` references with `temp` + // return temp; // if the result is used + } + ``` +- For simple cases like `new Foo() => [bar = "baz"]`, convert to: + ```java + Foo foo = new Foo(); + foo.setBar("baz"); + // use foo + ``` + +5.5. **String concatenation `+`**: Same in Java. + +5.6. **Range operator `..`**: `0..n` becomes `IntStream.rangeClosed(0, n)` or a for-loop. + +5.7. **Power operator `**`**: Use `Math.pow()`. + +### 6. LAMBDA EXPRESSIONS + +6.1. **Xtend `[...]` lambda to Java `(...) -> {...}`**: +- `[x | x.name]` becomes `(x) -> x.getName()` or `x -> x.getName()` +- `[it | name]` becomes `(it) -> it.getName()` or simply use a method reference +- `[ body ]` (no parameters, implicit `it`) becomes `(it) -> { body }` where `it` is used, or `() -> { body }` if `it` is not used. +- Single-expression lambdas do not need braces: `x -> x.getName()` +- Multi-statement lambdas need braces and explicit `return`: `(x) -> { doSomething(); return x.getName(); }` + +6.2. **Lambda with `it` as implicit parameter**: +- When a lambda uses properties/methods without a receiver, these reference the implicit `it` parameter. +- `[name]` on a `Function1` becomes `(Foo it) -> it.getName()` or `Foo::getName` +- Xtend: `checks.filter[name !== null]` becomes Java: `checks.stream().filter(c -> c.getName() != null).toList()` + +6.3. **Procedure (void lambda) vs Function (returning lambda)**: +- Xtend uses the same `[...]` syntax for both. +- In Java, determine from context whether it is `Consumer`, `Predicate`, `Function`, etc. +- For Xtext-specific cases: `Procedure1` lambdas like `[noSpace]` become `(IHiddenRegionFormatter it) -> { it.noSpace(); }` or `IHiddenRegionFormatter::noSpace`. + +### 7. DISPATCH METHODS + +This is one of the most complex Xtend features. Dispatch methods implement multiple dispatch (method selection based on runtime type of arguments). + +7.1. **Pattern**: A set of `def dispatch` methods with the same name but different parameter types: +```xtend +def dispatch void format(CheckCatalog c, IFormattableDocument doc) { ... } +def dispatch void format(Category c, IFormattableDocument doc) { ... } +def dispatch void format(EObject obj, IFormattableDocument doc) { ... } +``` + +7.2. **Conversion strategy**: Convert to individual `protected` methods prefixed with `_` (underscore) plus a public dispatcher method: + +```java +protected void _format(CheckCatalog c, IFormattableDocument doc) { ... } +protected void _format(Category c, IFormattableDocument doc) { ... } +protected void _format(EObject obj, IFormattableDocument doc) { ... } + +public void format(Object obj, IFormattableDocument doc) { + if (obj instanceof CheckCatalog c) { + _format(c, doc); + return; + } else if (obj instanceof Category c) { + _format(c, doc); + return; + } else if (obj instanceof EObject e) { + _format(e, doc); + return; + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + obj); + } +} +``` + +7.3. **Important dispatch rules**: +- Order type checks from most specific to least specific. +- If a dispatch method has `override` keyword, add `@Override` to the dispatcher method, not the individual `_` methods. +- The dispatcher parameter type should be the common supertype (usually `Object` or `EObject`). +- If the parent class also has dispatch methods with the same name, the dispatcher must call `super._methodName()` for types not handled locally. +- Use Java 21 pattern matching for instanceof (`if (obj instanceof Foo f)`) in the dispatcher. + +### 8. EXTENSION METHODS + +8.1. **`@Inject extension ClassName fieldName`**: +- Convert to: `@Inject private ClassName fieldName;` +- At every call site where the extension's methods were called as `obj.extensionMethod(args)`, convert to `fieldName.extensionMethod(obj, args)`. +- If the extension field was used without a name (e.g., `@Inject extension CheckGeneratorNaming`), generate a field name following the convention: `_checkGeneratorNaming` (underscore + camelCase class name starting lowercase). + +8.2. **`extension` method parameters** (e.g., `def foo(extension IFormattableDocument document)`): +- Drop the `extension` keyword from the parameter. +- At call sites within the method body, calls that were dispatched to the extension parameter need to be converted to explicit calls: + - `prepend(checkcatalog)[noSpace]` becomes `document.prepend(checkcatalog, (IHiddenRegionFormatter it) -> it.noSpace())` + +8.3. **`static extension` imports** (e.g., `import static extension com.foo.Bar.*`): +- Convert to `import static com.foo.Bar.*;` +- At call sites: `obj.staticExtensionMethod(args)` becomes `Bar.staticExtensionMethod(obj, args)`. +- Example: `import static extension org.eclipse.xtext.GrammarUtil.*` then `grammar.simpleName` becomes `GrammarUtil.getSimpleName(grammar)`. + +8.4. **`extension` keyword on `val`/`var`** (e.g., `val extension naming = contentAssistNaming`): +- Drop `extension` keyword. Keep as a local variable. +- Convert call sites within scope to explicit calls on the variable. + +### 9. TEMPLATE EXPRESSIONS (GUILLEMETS) + +This is the MOST COMPLEX feature. Template expressions use triple single quotes and guillemets (French quotes). + +9.1. **Simple templates** (no control flow, just interpolation): +- If the template is a single line or very short, use string concatenation or `String.format()`: + ```xtend + '''{predicates.«predicate.name»(parserContext)}?=>''' + ``` + becomes: + ```java + "{predicates." + predicate.getName() + "(parserContext)}?=>" + ``` + +9.2. **Multi-line templates generating code/text**: Use `StringBuilder`: +```xtend +def compile(CheckConfiguration config) { + val properties = propertiesGenerator.convertToProperties(config); + ''' + «FOR k:properties.keySet» + «k»=«properties.get(k)» + «ENDFOR» + ''' +} +``` +becomes: +```java +public CharSequence compile(CheckConfiguration config) { + final Properties properties = propertiesGenerator.convertToProperties(config); + final StringBuilder builder = new StringBuilder(); + for (final String k : properties.keySet()) { + builder.append(k).append("=").append(properties.get(k)).append("\n"); + } + return builder; +} +``` + +9.3. **Template control flow**: +- `«IF condition»...«ENDIF»` becomes `if (condition) { builder.append(...); }` +- `«IF condition»...«ELSE»...«ENDIF»` becomes `if-else` +- `«ELSEIF condition»` becomes `else if (condition)` +- `«FOR item : collection»...«ENDFOR»` becomes `for (Type item : collection) { builder.append(...); }` +- `«FOR item : collection SEPARATOR sep»...«ENDFOR»` -- use a boolean flag or `String.join()` or `Collectors.joining()`: + ```java + builder.append(collection.stream() + .map(item -> /* expression */) + .collect(Collectors.joining(sep))); + ``` +- `«val x = expr»` inside a template is a local variable declaration. Declare it before use. + +9.4. **Template indentation**: +- Xtend templates preserve indentation relative to the insertion point. In the Java conversion, you do NOT need to replicate this Xtend-specific whitespace behavior exactly. Instead: + - For code generators producing source code: use explicit `\n` and string indentation as appropriate. + - For simple cases, inline `\n` in the StringBuilder appends. + - For complex generators, consider creating a helper method or using a `StringJoiner`. + +9.5. **Return type**: Methods returning template expressions should return `CharSequence` (to match Xtend's `StringConcatenation` return type, which implements `CharSequence`). Alternatively, return `String` if all callers use it as `String` (add `.toString()` call on the `StringBuilder`). + +9.6. **`«expression»` interpolation**: Convert `«expr»` to the corresponding Java expression inside a `.append()` call. +- `«catalog.name»` becomes `.append(catalog.getName())` +- `«IF grammar !== null»GRAMMAR_NAME,«ENDIF»` becomes: + ```java + if (grammar != null) { + builder.append("GRAMMAR_NAME,"); + } + ``` + +### 10. COLLECTION LITERALS AND OPERATIONS + +10.1. **`#[]` (list literal)**: +- `#["a", "b", "c"]` becomes `List.of("a", "b", "c")` (immutable) or `new ArrayList<>(List.of("a", "b", "c"))` (mutable). +- Empty: `#[]` becomes `List.of()` or `new ArrayList<>()`. +- `newArrayList` becomes `new ArrayList<>()` or `new ArrayList<>(...)`. +- `newArrayList("a", "b")` becomes `new ArrayList<>(List.of("a", "b"))` or `Lists.newArrayList("a", "b")` (if Google Guava is available, which it is in this project). + +10.2. **`#{}` (set literal)**: +- `#{"a", "b"}` becomes `Set.of("a", "b")` or `new HashSet<>(Set.of("a", "b"))`. +- `newHashSet` becomes `new HashSet<>()` or `Sets.newHashSet(...)`. + +10.3. **Collection operations** (Xtend extension methods on Iterable/Collection): +- `.map[expr]` becomes `.stream().map(x -> expr).toList()` or `.stream().map(x -> expr).collect(Collectors.toList())` +- `.filter[expr]` becomes `.stream().filter(x -> expr).toList()` +- `.filter(Type)` becomes `.stream().filter(Type.class::isInstance).map(Type.class::cast).toList()` OR use Guava `Iterables.filter(collection, Type.class)` +- `.exists[expr]` becomes `.stream().anyMatch(x -> expr)` +- `.forall[expr]` becomes `.stream().allMatch(x -> expr)` +- `.findFirst[expr]` becomes `.stream().filter(x -> expr).findFirst().orElse(null)` +- `.head` becomes `.get(0)` (for List) or `.iterator().next()` (for Iterable), with null safety if needed +- `.tail` becomes `.subList(1, list.size())` or `.stream().skip(1).toList()` +- `.toList` becomes `.stream().toList()` or `new ArrayList<>(iterable)` or `IterableExtensions.toList(iterable)` +- `.toSet` becomes `new HashSet<>(collection)` or `.stream().collect(Collectors.toSet())` +- `.flatten` becomes `.stream().flatMap(Collection::stream).toList()` +- `.sortBy[expr]` becomes `.stream().sorted(Comparator.comparing(x -> expr)).toList()` +- `.sort` becomes `.stream().sorted().toList()` or `Collections.sort(list)` for in-place +- `.join(',')` becomes `String.join(",", collection)` or `.stream().collect(Collectors.joining(","))` +- `.indexed` becomes use `IntStream.range(0, list.size())` with index access, or keep Guava if present +- `.reverse` becomes `Collections.reverse(new ArrayList<>(list))` or use `Lists.reverse(list)` (Guava) +- `.isEmpty` / `.empty` becomes `.isEmpty()` +- `.size` becomes `.size()` +- `.forEach[action]` becomes `.forEach(x -> action)` (Java Iterable.forEach or Stream.forEach) +- `.filterNull` becomes `.stream().filter(Objects::nonNull).toList()` + +10.4. **`isNullOrEmpty`**: +- `StringExtensions.isNullOrEmpty(s)` or `s.isNullOrEmpty` becomes `s == null || s.isEmpty()` +- Or use a utility method if the project has one. + +10.5. **`toIterable(iterator)`**: `IteratorExtensions.toIterable(resource.getAllContents())` -- keep this call as-is since it is an Xtext utility, OR convert to: `() -> resource.getAllContents()` (creating an Iterable from Iterator). + +10.6. **`Iterables.filter(iterable, Class)`**: Keep this Guava call as-is -- it is idiomatic in Eclipse/Xtext projects. + +10.7. **Operator overloading on collections**: +- `list += element` becomes `list.add(element)` +- `list += otherList` becomes `list.addAll(otherList)` +- `list -= element` becomes `list.remove(element)` +- `map.get(key)` -- same in Java (Xtend allows `map[key]` syntax which becomes `map.get(key)`) + +### 11. PROPERTY ACCESS SYNTAX + +11.1. Xtend allows property-style access for getters/setters: +- `obj.name` may mean `obj.getName()` -- convert to explicit getter call +- `obj.name = value` may mean `obj.setName(value)` -- convert to explicit setter call +- `obj.isActive` may mean `obj.isActive()` or `obj.getIsActive()` -- determine from context + +11.2. Boolean property access: +- `field.final` means `field.isFinal()` +- `field.static` means `field.isStatic()` + +11.3. **IMPORTANT**: Not all dot-access is property access. If the object actually has a public field, keep field access. Determine from the types involved. + +### 12. SWITCH EXPRESSIONS + +12.1. **Basic switch**: +```xtend +switch(x) { + case "a": doA() + case "b": doB() + default: doDefault() +} +``` +becomes Java switch expression or statement depending on context. + +12.2. **Switch with type guards**: +```xtend +switch obj { + CheckCatalog: obj.name + Category case obj.name !== null: obj.label + default: "unknown" +} +``` +becomes: +```java +if (obj instanceof CheckCatalog checkCatalog) { + return checkCatalog.getName(); +} else if (obj instanceof Category category && category.getName() != null) { + return category.getLabel(); +} else { + return "unknown"; +} +``` + +12.3. Use Java 21 pattern matching for instanceof where applicable. + +### 13. ACTIVE ANNOTATIONS + +13.1. **`@Data`**: This generates `equals()`, `hashCode()`, `toString()`, and getters for all fields (which are final). Convert to a Java `record` if the class has no mutable state and no superclass. Otherwise, manually add: +- All-args constructor +- Getter methods for each field +- `equals()`, `hashCode()`, `toString()` +- Or use `@Override` of these methods if the class extends something. + +For inner static classes annotated with `@Data` that have `val` fields and no superclass (like this project's `NoBacktrack`, `SemanticPredicate`, `GrammarAnnotations`), prefer Java records: +```java +public record SemanticPredicate(String name, String message, String grammar, List keywords) {} +``` + +13.2. **`@Accessors`**: Generates getters (and setters for `var` fields). +- `@Accessors boolean foo` generates `getFoo()` and `setFoo(boolean)`. +- `@Accessors(PUBLIC_SETTER) String bar` generates only a public setter. +- `@Accessors(PROTECTED_GETTER) Foo baz` generates only a protected getter. +- Convert by manually writing the getter/setter methods with the specified visibility. + +13.3. **`@FinalFieldsConstructor`**: Generates a constructor taking all final fields as parameters. Manually write the constructor. + +### 14. SPECIAL XTEND PATTERNS + +14.1. **`it` implicit parameter**: +- When a method declares `Type it` as its first parameter (e.g., `def generate(ExportModel it, ...)`), all unqualified method/property calls in the body refer to `it`. +- Convert: Add the parameter with a proper name (e.g., `exportModel`) and qualify all calls: + - `exports` becomes `exportModel.getExports()` + - `grammar` becomes `exportModel.getGrammar()` (if it's a property of ExportModel) + - `extension` becomes `exportModel.isExtension()` + +14.2. **`this` vs receiver**: In Xtend, method calls without a receiver may go to `this`, an extension, or `it`. You must determine which based on the type hierarchy. Check: + 1. Is it a method on the current class or its superclass? -> `this.method()` or just `method()` + 2. Is it an extension method from an `@Inject extension` field? -> `field.method(obj)` + 3. Is it a static extension method? -> `ExtClass.method(obj)` + 4. Is it on the `it` implicit receiver? -> `it.method()` using the renamed parameter + +14.3. **Multiple return statements**: Xtend methods implicitly return the last expression. You MUST add explicit `return` for ALL non-void return paths. + +14.4. **`class` keyword access**: In Xtend, `SomeClass` by itself in certain contexts refers to the class literal. In Java, use `SomeClass.class`. + +14.5. **Static method access with `::`**: `ClassName::methodName` or `ClassName::FIELD` becomes `ClassName.methodName()` or `ClassName.FIELD` in Java. + +14.6. **Pairs**: `key -> value` becomes `Pair.of(key, value)` or `Map.entry(key, value)` depending on context. + +### 15. GUICE DEPENDENCY INJECTION + +15.1. **`@Inject` fields**: Keep as-is. These are standard Guice annotations. +- `@Inject ClassName fieldName` becomes `@Inject private ClassName fieldName;` +- Add `private` visibility if not already present. +- If the field was an `extension`, see Rule 8. + +15.2. **`@Inject extension`**: See Rule 8.1. + +### 16. COMMENTS AND DOCUMENTATION + +16.1. **Preserve ALL comments**: Copy Javadoc (`/** */`), block comments (`/* */`), and line comments (`//`) exactly as they appear. + +16.2. **Copyright headers**: Keep the exact copyright header from the original file. + +16.3. **`@SuppressWarnings("all")`**: The Xtend compiler adds this. Do NOT add it to the converted Java file unless it was explicitly in the Xtend source. + +### 17. XTEND LIBRARY REPLACEMENTS + +Replace Xtend runtime library calls with Java standard library or Guava equivalents: + +| Xtend Library Call | Java Replacement | +|---|---| +| `IterableExtensions.map(iter, fn)` | `iter.stream().map(fn).toList()` | +| `IterableExtensions.filter(iter, fn)` | `iter.stream().filter(fn).toList()` | +| `IterableExtensions.toList(iter)` | `Lists.newArrayList(iter)` or stream | +| `IterableExtensions.toSet(iter)` | `Sets.newHashSet(iter)` or stream | +| `IterableExtensions.head(iter)` | `iter.iterator().next()` with null check, or `Iterables.getFirst(iter, null)` | +| `IterableExtensions.join(iter, sep)` | `String.join(sep, iter)` or `Joiner.on(sep).join(iter)` | +| `IterableExtensions.exists(iter, fn)` | `iter.stream().anyMatch(fn)` | +| `IterableExtensions.forall(iter, fn)` | `iter.stream().allMatch(fn)` | +| `IterableExtensions.findFirst(iter, fn)` | `iter.stream().filter(fn).findFirst().orElse(null)` | +| `IterableExtensions.sortBy(iter, fn)` | `iter.stream().sorted(Comparator.comparing(fn)).toList()` | +| `IterableExtensions.sort(iter)` | `iter.stream().sorted().toList()` | +| `IterableExtensions.isEmpty(iter)` | `!iter.iterator().hasNext()` or `Iterables.isEmpty(iter)` | +| `IterableExtensions.toMap(iter, keyFn, valFn)` | `iter.stream().collect(Collectors.toMap(keyFn, valFn))` | +| `IteratorExtensions.toIterable(iter)` | Keep as utility or wrap: `(Iterable) () -> iter` | +| `StringExtensions.isNullOrEmpty(s)` | `s == null \|\| s.isEmpty()` | +| `CollectionLiterals.newArrayList(...)` | `new ArrayList<>(List.of(...))` or `Lists.newArrayList(...)` | +| `CollectionLiterals.newHashSet(...)` | `new HashSet<>(Set.of(...))` or `Sets.newHashSet(...)` | +| `CollectionLiterals.newHashMap(...)` | `new HashMap<>(Map.of(...))` or `Maps.newHashMap()` | +| `ObjectExtensions.operator_doubleArrow(obj, fn)` | inline (see Rule 5.4) | +| `Functions.Function1` | `java.util.function.Function` | +| `Procedures.Procedure1` | `java.util.function.Consumer` | + +**IMPORTANT**: If the existing Java code in the project uses Guava (which this project does extensively), prefer Guava utilities over Java streams for consistency. For example, prefer `Iterables.filter(iter, Type.class)` over `iter.stream().filter(...)`. + +### 18. FORMATTING AND STYLE + +18.1. Use standard Java formatting: +- 2-space indentation (to match this project's convention) +- Opening brace on same line +- Spaces around operators +- Blank line between methods + +18.2. Keep the original method ordering from the Xtend file. + +18.3. Use `this.` qualifier only when needed for disambiguation (e.g., with injected fields sharing names with parameters). + +### 19. CHECKED EXCEPTIONS + +19.1. Xtend does not enforce checked exceptions. When converting to Java, methods that call APIs that throw checked exceptions need proper handling: +- Add `throws` declarations to the method signature, OR +- Wrap in try-catch blocks +- Check the actual APIs being called to determine which checked exceptions need handling +- Example: `CoreException` from Eclipse APIs, `IOException` from I/O operations + +--- + +## CHECKLIST + +Before returning the converted file, verify: + +- [ ] All `val` converted to `final ExplicitType` (no `var` keyword) +- [ ] All `var` converted to `ExplicitType` (no `var` keyword) +- [ ] All `def` converted to proper Java method with visibility, return type, and `return` statements +- [ ] All `override` converted to `@Override` annotation +- [ ] All `typeof(X)` converted to `X.class` +- [ ] All `===` / `!==` converted to `==` / `!=` +- [ ] All `?.` null-safe navigation converted to null checks +- [ ] All `[...]` lambdas converted to `(...) -> {...}` +- [ ] All `dispatch` methods converted to dispatcher pattern +- [ ] All `extension` methods converted to explicit calls +- [ ] All `static extension` imports converted to static calls +- [ ] All template expressions `'''...«»...'''` converted to StringBuilder +- [ ] All `«IF»/«FOR»/«SEPARATOR»` converted to Java control flow +- [ ] All `#[]` / `#{}` collection literals converted +- [ ] All `=>` operator usages converted +- [ ] All `@Data` / `@Accessors` active annotations expanded +- [ ] All property access (`.name`) converted to getter/setter calls (`.getName()`) +- [ ] All `isNullOrEmpty` and Xtend library calls replaced +- [ ] All `+=` on collections converted to `.add()` / `.addAll()` +- [ ] All `::` static access converted to `.` +- [ ] Semicolons added to all statements +- [ ] Explicit visibility modifiers on all classes, methods, fields +- [ ] All imports updated (Xtend-specific removed, Java ones added) +- [ ] All comments and Javadoc preserved +- [ ] Copyright header preserved exactly +- [ ] No `@SuppressWarnings("all")` added (unless in original source) +- [ ] Checked exceptions properly handled (throws or try-catch) +- [ ] File compiles as valid Java 21 + +--- + +## EXAMPLE CONVERSION + +### Xtend Input: +```xtend +package com.example + +import com.google.inject.Inject +import org.eclipse.emf.ecore.resource.Resource +import static org.eclipse.xtext.xbase.lib.IteratorExtensions.* +import static extension com.example.NamingExtensions.* + +class MyGenerator { + @Inject extension MyHelper helper + + override void doGenerate(Resource resource) { + val config = getConfig(resource?.URI) + for (model : toIterable(resource.allContents).filter(typeof(MyModel))) { + model.compile + } + } + + def compile(MyModel it) ''' + package «packageName»; + «IF !imports.isNullOrEmpty» + + «FOR imp : imports» + import «imp»; + «ENDFOR» + «ENDIF» + + public class «name» { + } + ''' +} +``` + +### Java Output: +```java +package com.example; + +import com.google.inject.Inject; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.xbase.lib.IteratorExtensions; + +import com.google.common.collect.Iterables; + +public class MyGenerator { + + @Inject + private MyHelper helper; + + @Override + public void doGenerate(final Resource resource) { + final URI uri = resource != null ? resource.getURI() : null; + final MyConfig config = getConfig(uri); + for (final MyModel model : Iterables.filter(IteratorExtensions.toIterable(resource.getAllContents()), MyModel.class)) { + compile(model); + } + } + + public CharSequence compile(final MyModel model) { + final StringBuilder builder = new StringBuilder(); + builder.append("package ").append(model.getPackageName()).append(";\n"); + if (!(model.getImports() == null || model.getImports().isEmpty())) { + builder.append("\n"); + for (final String imp : model.getImports()) { + builder.append("import ").append(imp).append(";\n"); + } + } + builder.append("\n"); + builder.append("public class ").append(NamingExtensions.getName(model)).append(" {\n"); + builder.append("}\n"); + return builder; + } +} +``` + +--- + +Now convert the following Xtend file to idiomatic Java following ALL the rules above: