fix(refactoring): bootstrap JDT code template store headless (#98) #99

Merged
hauschel.fred merged 1 commit from fix/98-encapsulate-template-store into main 2026-09-07 21:01:01 +00:00
Collaborator

Basis: PR #96 (auf #95) — Branch fix/61-51-refactoring-status, nicht main.

Befund

jdt_encapsulate_field scheiterte headless bei jedem Aufruf mit
Cannot invoke "TemplateStoreCore.getTemplateData(boolean)" because "this.fInstanceStore" is null.

SelfEncapsulateFieldRefactoring rendert Getter-/Setter-Body aus
JavaManipulation.getCodeTemplateStore() — ein statisches Feld, das nur das
org.eclipse.jdt.ui-JavaPlugin beim Workbench-Start füllt. Headless startet dieses
Plugin nie, das Feld bleibt null, und die NPE fliegt vor jeder Status-Prüfung.
Gleiche Bug-Klasse wie #77/#95 (jdt.ui-Bundle-Zustand headless).

Lösungsweg

HeadlessCodeTemplates baut den Store aus derselben Quelle auf, aus der auch die
Workbench liest: den Default-Code-Templates, die org.eclipse.jdt.ui per
templates/default-codetemplates.xml am Extension Point
org.eclipse.ui.editors.templates beiträgt. Die Extension Registry ist headless
verfügbar, es wird kein UI-Bundle aktiviert. Die Workbench-Implementierung
(ContributionTemplateStore) war nicht nutzbar: sie braucht einen jface
IPreferenceStore und loggt über EditorsPlugin — genau der UI-Stack, aus dem
sich dieser Server heraushalten muss.

Installiert wird der Store in HeadlessApplication.initHeadlessPreferences(), neben
dem schon vorhandenen Bootstrap für Preference-Node-Id und Import-Order. Damit
profitiert jedes manipulation-Refactoring, das ein Template rendert, nicht nur dieses
eine Tool. Fehlt der Store trotzdem, meldet das Tool das jetzt selbsterklärend unter
Nennung der Template-Id, statt eine NPE durchzureichen.

Zweiter Defekt, der dadurch sichtbar wurde

Mit renderndem Template zeigte sich: das Tool übergab Flags.AccPrivate an
setVisibility(). Das setzt die Sichtbarkeit der generierten Accessoren, nicht die
des Feldes — das Feld macht das Refactoring ohnehin immer privat. Ergebnis war ein
privater Getter und Setter, den keine andere Klasse aufrufen kann; die zugesagte
Aktualisierung aller Feldzugriffe konnte so gar nicht funktionieren. Accessoren sind
jetzt public.

Andere Tools mit demselben Template-Store

Keine. CodeGenerationTools.generateGettersSetters / generateConstructor /
generateEqualsHashCode / generateToString bauen ihren Quelltext per
StringBuilder und fassen den Template-Store nicht an — geprüft, in diesem PR
bewusst nicht angefasst. Der Bootstrap wirkt prozessweit, käme also auch künftigen
corext-Refactorings zugute.

Verifikation

  • mvn clean package (Tycho, Maven 3.9.14) grün
  • tests/refactoring-test.sh: 3/3 — Test 3 vorher rot mit exakt der NPE aus dem
    Issue, danach grün. Assertions lesen die Datei auf Platte: Feld private, Getter und
    Setter vorhanden, beide Bodies gerendert (return name; / this.name = name;),
    und die Antwort darf weder NullPointerException noch ProjectTemplateStore
    enthalten.
  • tests/smoke-test.sh: 9/9, 52 Tools
  • Der neue Test-Fall hängt bewusst am Ende des Skripts, damit er konfliktfrei
    neben den Tests 0/0b aus PR #97 mergt.

Closes #98

🤖 Generated with Claude Code

https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL

Basis: PR #96 (auf #95) — Branch `fix/61-51-refactoring-status`, nicht `main`. ## Befund `jdt_encapsulate_field` scheiterte headless bei **jedem** Aufruf mit `Cannot invoke "TemplateStoreCore.getTemplateData(boolean)" because "this.fInstanceStore" is null`. `SelfEncapsulateFieldRefactoring` rendert Getter-/Setter-Body aus `JavaManipulation.getCodeTemplateStore()` — ein statisches Feld, das nur das `org.eclipse.jdt.ui`-JavaPlugin beim Workbench-Start füllt. Headless startet dieses Plugin nie, das Feld bleibt null, und die NPE fliegt **vor** jeder Status-Prüfung. Gleiche Bug-Klasse wie #77/#95 (jdt.ui-Bundle-Zustand headless). ## Lösungsweg `HeadlessCodeTemplates` baut den Store aus derselben Quelle auf, aus der auch die Workbench liest: den Default-Code-Templates, die `org.eclipse.jdt.ui` per `templates/default-codetemplates.xml` am Extension Point `org.eclipse.ui.editors.templates` beiträgt. Die Extension Registry ist headless verfügbar, es wird **kein UI-Bundle aktiviert**. Die Workbench-Implementierung (`ContributionTemplateStore`) war nicht nutzbar: sie braucht einen jface `IPreferenceStore` und loggt über `EditorsPlugin` — genau der UI-Stack, aus dem sich dieser Server heraushalten muss. Installiert wird der Store in `HeadlessApplication.initHeadlessPreferences()`, neben dem schon vorhandenen Bootstrap für Preference-Node-Id und Import-Order. Damit profitiert jedes manipulation-Refactoring, das ein Template rendert, nicht nur dieses eine Tool. Fehlt der Store trotzdem, meldet das Tool das jetzt selbsterklärend unter Nennung der Template-Id, statt eine NPE durchzureichen. ## Zweiter Defekt, der dadurch sichtbar wurde Mit renderndem Template zeigte sich: das Tool übergab `Flags.AccPrivate` an `setVisibility()`. Das setzt die Sichtbarkeit der **generierten Accessoren**, nicht die des Feldes — das Feld macht das Refactoring ohnehin immer privat. Ergebnis war ein privater Getter und Setter, den keine andere Klasse aufrufen kann; die zugesagte Aktualisierung aller Feldzugriffe konnte so gar nicht funktionieren. Accessoren sind jetzt `public`. ## Andere Tools mit demselben Template-Store Keine. `CodeGenerationTools.generateGettersSetters` / `generateConstructor` / `generateEqualsHashCode` / `generateToString` bauen ihren Quelltext per `StringBuilder` und fassen den Template-Store nicht an — geprüft, in diesem PR bewusst nicht angefasst. Der Bootstrap wirkt prozessweit, käme also auch künftigen corext-Refactorings zugute. ## Verifikation - `mvn clean package` (Tycho, Maven 3.9.14) grün - `tests/refactoring-test.sh`: **3/3** — Test 3 vorher rot mit exakt der NPE aus dem Issue, danach grün. Assertions lesen die Datei auf Platte: Feld `private`, Getter und Setter vorhanden, beide Bodies gerendert (`return name;` / `this.name = name;`), und die Antwort darf weder `NullPointerException` noch `ProjectTemplateStore` enthalten. - `tests/smoke-test.sh`: 9/9, 52 Tools - Der neue Test-Fall hängt bewusst am **Ende** des Skripts, damit er konfliktfrei neben den Tests 0/0b aus PR #97 mergt. Closes #98 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL
jdt_encapsulate_field failed on every call with "Cannot invoke
TemplateStoreCore.getTemplateData(boolean) because this.fInstanceStore
is null". SelfEncapsulateFieldRefactoring renders the accessor bodies
from JavaManipulation.getCodeTemplateStore(), a static field that only
the org.eclipse.jdt.ui JavaPlugin fills on workbench startup. Headless
that plugin never starts, so the field stays null and the NPE escapes
before any status check can run.

HeadlessCodeTemplates rebuilds the store from the same source the
workbench uses: the default code templates that org.eclipse.jdt.ui
contributes to the org.eclipse.ui.editors.templates extension point.
The extension registry is available headless, so no UI bundle is
activated - the workbench's own ContributionTemplateStore was not
usable here because it needs a jface IPreferenceStore and logs through
EditorsPlugin. Installed from initHeadlessPreferences() next to the
existing preference-node and import-order bootstrap, so every
manipulation refactoring that renders a template benefits, not just
this one tool. A missing store is now reported as a self-explaining
tool error naming the template id instead of an NPE.

With the templates rendering, a second defect surfaced: the tool passed
Flags.AccPrivate to setVisibility(), which sets the visibility of the
GENERATED ACCESSORS, not of the field - the refactoring always makes
the field private. The result was a private getter and setter that no
other class could call, so "updates all direct field accesses to use
the accessors" could not work. Accessors are public now.

tests/refactoring-test.sh gets an end-to-end case that asserts on disk:
field private, getter and setter present, both bodies rendered.

Closes #98

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018q6miiQHwQYFgZBy71aUhL
hauschel.fred changed target branch from fix/61-51-refactoring-status to main 2026-09-07 20:57:06 +00:00
fred force-pushed fix/98-encapsulate-template-store from 4189f3a54f to b37b82da5a 2026-09-07 20:59:49 +00:00 Compare
Commenting is not possible because the repository is archived.
No description provided.