refactor: migrate Xtend to Java - com.avaloq.tools.ddk.check.core - #1452
Conversation
0f48915 to
72b635e
Compare
72b635e to
2fa945b
Compare
Pure git mv of the 8 remaining .xtend sources to .java, content unchanged, so the rename edge has 100% similarity and git log --follow and git blame permanently traverse the migration boundary. This intermediate commit intentionally does not compile; the translation follows in the next commit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2fa945b to
f43aba0
Compare
In-place translation of the 8 renamed sources, faithful to the Xtend compiler's own xtend-gen output (fresh ground-truth build). The three byte-critical emitters (CheckGeneratorExtensions, CheckGenerator, CheckJvmModelInferrer) keep the exact StringConcatenation call sequences incl. two-arg append(value, indent) and newLineIfNotEmpty. Notable per-file points: - CheckJvmModelInferrer: Xtend's JvmTypesBuilder '+=' silently skips null elements; the plain Iterables.addAll translation let null JvmMembers reach the type resolver (IllegalArgumentException: element: null, reproduced by the CheckValidationTest severity-range stubs). The two affected call sites are wrapped in IterableExtensions.filterNull like every sibling site. - CheckGeneratorExtensions.getContents: two deliberate output-neutral hardenings (explicit UTF-8; IllegalStateException wrapping of checked exceptions), documented in the PR. - splitCamelCase uses .formatted() per repo convention (behavior-identical to the String.format xtend-gen emitted). - Comments preserve the originals' exact state: block comments stay block comments, and no Javadoc is invented (checkstyle only validates Javadoc that exists; it does not require any). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The module is fully off Xtend: remove the xtend-gen source folder from build.properties and .classpath, the xtextBuilder/xtextNature from .project, and the xtend-gen directory marker - matching the other fully migrated modules. No Require-Bundle change needed: org.eclipse.xtext.xbase.lib stays as a direct dependency because the migrated sources still use IterableExtensions/ListExtensions/ StringExtensions and StringConcatenation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Post-verification style pass, kept separate from the faithful translation commit for clean revertability: - adopt Java 21 pattern matching in the hand-written dispatchers (CheckTypeComputer, CheckScopeProvider, CheckGeneratorExtensions, CheckFormatter — 62 branches) - CheckFormatter: rename the generic dispatcher parameter xlistliteral to element, collapse the tail's impossible final else (provably unreachable: requires x == null and x != null both false), drop the dead Arrays import, restore final on loop variables - CheckGenerator: fix misspelled local formattedCateogryDescription - CheckGeneratorExtensions: build the splitCamelCase regex from named compile-time String constants folded by the compiler instead of a runtime .formatted() call Behavior is identical by construction; verified against the fresh xtend-gen ground truth as part of the migration verification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f43aba0 to
74444ba
Compare
| } | ||
|
|
||
| @Override | ||
| @XbaseGenerated |
There was a problem hiding this comment.
We should remove @XbaseGenerated
| @Override | ||
| @XbaseGenerated | ||
| public void format(final Object element, final IFormattableDocument document) { | ||
| if (element instanceof JvmTypeParameter jvmTypeParameter) { |
There was a problem hiding this comment.
I think we can do better for this number of elements and be more concise. What about
switch (element) {
case JvmTypeParameter j -> _format(j, document);
case JvmFormalParameter p -> _format(p, document);
case XtextResource r -> _format(r, document);
case XAssignment a -> _format(a, document);
case XBinaryOperation b -> _format(b, document);
case XDoWhileExpression dw -> _format(dw, document);
case XFeatureCall f -> _format(f, document);
case XListLiteral ll -> _format(ll, document);
case XMemberFeatureCall m -> _format(m, document);
case XPostfixOperation pf -> _format(pf, document);
case XUnaryOperation u -> _format(u, document);
case XWhileExpression w -> _format(w, document);
case XFunctionTypeRef ft -> _format(ft, document);
case Category c -> _format(c, document);
case Check ch -> _format(ch, document);
case CheckCatalog cc -> _format(cc, document);
case Context ctx -> _format(ctx, document);
case Implementation impl -> _format(impl, document);
case Member mem -> _format(mem, document);
case XGuardExpression ge -> _format(ge, document);
case XIssueExpression ie -> _format(ie, document);
case JvmGenericArrayTypeReference gar -> _format(gar, document);
case JvmParameterizedTypeReference ptr -> _format(ptr, document);
case JvmWildcardTypeReference wtr -> _format(wtr, document);
case XBasicForLoopExpression bf -> _format(bf, document);
case XBlockExpression bl -> _format(bl, document);
case XCastedExpression ce -> _format(ce, document);
case XClosure cl -> _format(cl, document);
case XCollectionLiteral col -> _format(col, document);
case XConstructorCall cc -> _format(cc, document);
case XForLoopExpression fl -> _format(fl, document);
case XIfExpression iff -> _format(iff, document);
case XInstanceOfExpression io -> _format(io, document);
case XReturnExpression ret -> _format(ret, document);
case XSwitchExpression sx -> _format(sx, document);
case XSynchronizedExpression sxn -> _format(sxn, document);
case XThrowExpression tx -> _format(tx, document);
case XTryCatchFinallyExpression tcf -> _format(tcf, document);
case XTypeLiteral tl -> _format(tl, document);
case XVariableDeclaration vd -> _format(vd, document);
case XAnnotation xa -> _format(xa, document);
case ContextVariable cv -> _format(cv, document);
case FormalParameter fp -> _format(fp, document);
case SeverityRange sr -> _format(sr, document);
case JvmTypeConstraint tc -> _format(tc, document);
case XExpression xe -> _format(xe, document);
case XImportDeclaration id -> _format(id, document);
case XImportSection is -> _format(is, document);
case EObject eo -> _format(eo, document);
case null -> _format((Void) null, document);
default -> _format(element, document);
}
?
This is the rewrite Web CoPilot gave me, so do not paste it, it might be incomplete, it is more for you to get an idea.
There was a problem hiding this comment.
It should also execute faster
| final EClass eClass = classForJvmType(context, jvmTypeRef.getType()); | ||
| if (eClass != null) { | ||
| final EList<EStructuralFeature> features = eClass.getEAllStructuralFeatures(); | ||
| final Collection<IEObjectDescription> descriptions = Collections2.transform(features, (final EStructuralFeature f) -> EObjectDescription.create(QualifiedName.create(f.getName()), f)); |
There was a problem hiding this comment.
It looks like https://bugs.eclipse.org/bugs/show_bug.cgi?id=368263 mentioned above is fixed, so this code can be simplified, would you like to ask your friend to simplify it?
rubenporras
left a comment
There was a problem hiding this comment.
At least remove the @XbaseGenerated annotation. I think also the switch would be nice.
Replace the 50-branch if/else instanceof chain in format(Object, IFormattableDocument) with a Java 21 pattern switch, and drop the @XbaseGenerated marker (plus its now-unused import) left over from the Xtend dispatch scaffolding. Branch order is unchanged, and javac's dominance analysis proves it: a pattern switch rejects any case dominated by an earlier one, so the chain's order compiling as-is also establishes that no branch was unreachable. The null branch moves to an explicit `case null` -- without it the switch would throw NPE where the chain routed null to _format((Void) null, document). The _format method names stay: they override and are called into XbaseFormatter (see super._format in _format(XMemberFeatureCall, ...)), so the class-level checkstyle:MethodName suppression stays too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CheckScopeProvider carried a 2012 workaround: "Use dispatch definitions instead of a switch statement since bug 368263 will otherwise cause the builder to fail during linking." That bug is an Xtend-compiler type-inference AssertionError (switch expression containing a closure that calls a static method) -- the _scope bodies match the trigger profile exactly, which is why the Xtend original used def dispatch. The source is Java now; a Java switch shares no compilation path with an Xtend switch expression, so the constraint is not merely fixed upstream but inapplicable. Fold the scope()/_scope() dispatch layer into getScope as a pattern switch, keeping the observable contract: handled types delegate to their scope method, unhandled types fall through to super.getScope, and a null context still throws IllegalArgumentException. Rename the two surviving overloads to scopeCatalog/scopeIssueExpression -- the underscore existed only for Xtend dispatch, and deliberately NOT to the scope_* shape that AbstractDeclarativeScopeProvider resolves reflectively. Nothing outside the class references the old methods; only CheckRuntimeModule binds it, and there are no subclasses. The checkstyle:MethodName and PMD.UnusedFormalParameter suppressions existed solely for the dispatch scaffolding; both are gone and the gates stay clean, confirming the cleanup was real. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| if (context.getMarkerObject() != null) { | ||
| jvmTypeRef = typeResolver.resolveTypes(context.getMarkerObject()).getActualType(context.getMarkerObject()).toTypeReference(); | ||
| } else { | ||
| jvmTypeRef = EcoreUtil2.<Context> getContainerOfType(context, Context.class).getContextVariable().getType(); |
There was a problem hiding this comment.
| jvmTypeRef = EcoreUtil2.<Context> getContainerOfType(context, Context.class).getContextVariable().getType(); | |
| jvmTypeRef = EcoreUtil2.getContainerOfType(context, Context.class).getContextVariable().getType(); |
| case CheckCatalog checkCatalog -> scopeCatalog(checkCatalog, reference); | ||
| case XIssueExpression xIssueExpression -> scopeIssueExpression(xIssueExpression, reference); | ||
| case null -> throw new IllegalArgumentException("Unhandled parameter types: " | ||
| + Arrays.<Object> asList(context, reference).toString()); |
There was a problem hiding this comment.
Do we need <Object>? I would expect we can remove it
| // Make sure that only Checks of the current model can be referenced, and if the CheckCatalog includes | ||
| // another CheckCatalog, then use that parent as parent scope | ||
|
|
||
| final CheckCatalog catalog = EcoreUtil2.<CheckCatalog> getContainerOfType(context, CheckCatalog.class); |
There was a problem hiding this comment.
| final CheckCatalog catalog = EcoreUtil2.<CheckCatalog> getContainerOfType(context, CheckCatalog.class); | |
| final CheckCatalog catalog = EcoreUtil2.getContainerOfType(context, CheckCatalog.class); |
| // another CheckCatalog, then use that parent as parent scope | ||
|
|
||
| final CheckCatalog catalog = EcoreUtil2.<CheckCatalog> getContainerOfType(context, CheckCatalog.class); | ||
| final List<Check> checks = IterableExtensions.<Check> toList(IterableExtensions.<Check> filter(catalog.getAllChecks(), c -> c.getName() != null)); |
There was a problem hiding this comment.
| final List<Check> checks = IterableExtensions.<Check> toList(IterableExtensions.<Check> filter(catalog.getAllChecks(), c -> c.getName() != null)); | |
| final List<Check> checks = IterableExtensions.toList(IterableExtensions.filter(catalog.getAllChecks(), c -> c.getName() != null)); |
| // We look first in the workspace for a grammar and then in the registry for a registered grammar | ||
| return MapBasedScope.createScope(IScope.NULLSCOPE, Iterables.filter(descriptions, Predicates.notNull())); | ||
| } else if (Objects.equals(reference, CheckPackage.Literals.XISSUE_EXPRESSION__CHECK)) { | ||
| final List<IEObjectDescription> descriptions = ListExtensions.map(context.getAllChecks(), (final Check c) -> EObjectDescription.create(checkQualifiedNameProvider.getFullyQualifiedName(c), c)); |
There was a problem hiding this comment.
| final List<IEObjectDescription> descriptions = ListExtensions.map(context.getAllChecks(), (final Check c) -> EObjectDescription.create(checkQualifiedNameProvider.getFullyQualifiedName(c), c)); | |
| final List<IEObjectDescription> descriptions = ListExtensions.map(context.getAllChecks(), c -> EObjectDescription.create(checkQualifiedNameProvider.getFullyQualifiedName(c), c)); |
rubenporras
left a comment
There was a problem hiding this comment.
some boilerplate to remove
Apply the review suggestions on dsldevkit#1452: remove the explicit generic type witnesses the Xtend compiler always emitted (EcoreUtil2 getContainerOfType x2, IterableExtensions toList/filter, Arrays.asList) and the explicit lambda parameter type in the ListExtensions.map call. Java's inference resolves every site to the same types the witnesses pinned; the compiler is the proof. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…vider Finish the boilerplate sweep from the review round: the three Collections2.transform lambdas kept their explicit parameter types after the previous commit removed the type witnesses. Same species, same fix -- inference resolves the element type from the collection argument in all three. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apply the review suggestions on #1452: remove the explicit generic type witnesses the Xtend compiler always emitted (EcoreUtil2 getContainerOfType x2, IterableExtensions toList/filter, Arrays.asList) and the explicit lambda parameter type in the ListExtensions.map call. Java's inference resolves every site to the same types the witnesses pinned; the compiler is the proof. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ore files Extend the boilerplate cleanup from the dsldevkit#1452 review to the remaining migrated files: remove explicit generic type witnesses (57) and explicit lambda parameter types (109) that the Xtend compiler always emits, plus the five imports orphaned by the removals. Java's inference resolves every site to the same types; the compiler and the untouched gates are the proof. CheckFormatter, CheckJvmModelInferrer, CheckGenerator and CheckGeneratorExtensions; no semantic change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ore files Extend the boilerplate cleanup from the dsldevkit#1452 review to the remaining migrated files: remove explicit generic type witnesses (57) and explicit lambda parameter types (109) that the Xtend compiler always emits, plus the five imports orphaned by the removals. Java's inference resolves every site to the same types; the compiler and the untouched gates are the proof. CheckFormatter, CheckJvmModelInferrer, CheckGenerator and CheckGeneratorExtensions; no semantic change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
Migrates the remaining 8
.xtendfiles ofcom.avaloq.tools.ddk.check.coreto Java 21 (generator, generator-extensions, generator-naming, JVM model inferrer, formatter, scope provider, type computer, generator-config), and drops the module's Xtend build infrastructure. Commit structure: puregit mvrename → in-place translate → infrastructure cleanup → separate style pass (Java 21 pattern matching in the hand-written dispatchers, kept out of the faithful translation commit for clean revertability).How it was validated
Two independent lines of evidence, both against the authoritative ground truth — a freshly built
xtend-gen/(what the Xtend compiler itself produced from these sources, built off current master):.xtendsources without sight of this branch, then reconciled file-by-file against this branch withxtend-genas arbiter. The two migrations converged on identical semantics at every contested site (dispatcher contracts,filterNullnull-skip preservation, issue-code sort order, template whitespace, sentinel returns).xtend-gen(every one-arg vs two-argappend,newLineIfNotEmptyoccurrence, null-handling quirk, iteration order, and exception surface), with each candidate finding subjected to a refutation pass. Two real defects were found and fixed on this branch: fabricated Javadoc in the generator files (now restored to the originals' exact comment state) and a malformed.classpathfrom the infra cleanup (now xmllint-valid).Deliberate, output-neutral divergences from
xtend-gen, kept and documented: explicit UTF-8 ingetContents(PMDRelianceOnDefaultCharset), checked-exception wrapping inIllegalStateException, and theCPD-OFFmarker around the faithfultoMethodduplication inCheckJvmModelInferrer(master's stricterpmd.cpd.min=100).Verification
Full local gate from
ddk-parent(verify checkstyle:check pmd:check spotbugs:check, check.core + test bundle + upstream deps): BUILD SUCCESS, test suite green, all static analysis clean. CI green on the pushed stack.🤖 Generated with Claude Code