Skip to content

processing: generalize processor machinery into a reusable codegen driver (#378) - #379

Merged
AndreasIgel merged 170 commits into
java-helpers:mainfrom
igel-devin-ai:devin/1792924834-generic-processor-driver
Nov 1, 2026
Merged

AndreasIgel merged 170 commits into
java-helpers:mainfrom
igel-devin-ai:devin/1792924834-generic-processor-driver

Conversation

@igel-devin-ai

@igel-devin-ai igel-devin-ai commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #378 — generalizes BuilderProcessor's machinery into a reusable annotation-processing framework before the module move (#349). A new org.javahelpers.codegen.processing package holds the generic pipeline; BuilderProcessor shrinks to the builder domain plugged into it via hooks. Generated output is byte-identical.

The generic machinery (new package codegen.processing)

  • AbstractCodegenProcessor<C, K, D> — the driver. Owns the processing lifecycle so a new processor supplies only its domain hooks:

    ctx      = createContext(env, reader, logger)                   [implement]
    per round:
      onRoundStart(ctx)                                           [no-op hook]
      elements = findElements(annotations, roundEnv)              [default: config's elementCollector]
      skipsRound(elements, ctx)  → empty collection or isFinished(ctx)  [framework gate]
      tasks    = plan(elements, ctx)  → planTasks(elements, ctx)  [implement]
      onTasksPlanned(tasks, ctx)                                  [no-op hook]
      registerPlannedTaskResults(tasks, ctx)                      [framework; per-task names via predictTaskResultTypeNames() — default: empty]
      loop 1 — specification (guarded, strict-mode reporting):
        defs   = analyse(task, ctx)                               [implement]
        target = mapToTargetSpecification(def, cfg, ctx)          [implement — one def, one target]
      loop 2 — generation:
        existsGuard → generateTarget(target)                      [engine + formatterResolver.resolve(target.mode)]
        onTaskGenerated(PlannedGeneration, ctx)                   [no-op hook]
      endRound(ctx)                                               [no-op hook — also after an empty plan]
    last round: onProcessingOver(ctx)                             [no-op hook — domain generates last-round outputs via generateTarget]
    
  • CodegenProcessorConfiguration — static declarations in one record handed to super(...): elementCollector (required), optionRegistry, requiredSourceVersion, loggerPrefix (default: processor class name).

  • ElementCollector/ElementSelector/ElementGroups — declarative element selection keyed by group name (annotation types, meta-marker metaMarked, opt-out); collection only finds, value reading stays in planTasks.

  • ProcessorOptionRegistry — supported options derived as a Set from the declared CompilerOptions, including the wired verbose and formatter-profile options.

  • CodegenTask<K> — the planned unit (element + resolved configuration + output package); PlannedGeneration (protected record) carries task + definition + target into onTaskGenerated.

  • CodegenConfiguration/CodegenDefinition — the domain contracts (targetTypeName() = the generated type's predicted name).

  • SourceFormatterResolver — formatter profile resolved from the option registry.

  • GenerationEnvironment wiring (generationEnvironment(...)), target-exists guard, JDK gate (verifySupportedJdk), and the generated-type registry all live in the driver.

BuilderProcessor after the collapse

Only domain code remains: ELEMENT_COLLECTOR/OPTION_REGISTRY/PROCESSOR_CONFIGURATION constants, createContext, createGenerator (RoasterCodeGenerator over generationEnvironment(...)), planTasks (config resolution, @SimpleBuilderFor expansion, scope/conflict rules), predictTaskResultTypeNames + onTaskResultRegistered (builder-name registration + SPI publication), analyse/mapToTargetSpecification, onTaskGenerated (Jackson accumulation + metrics), onProcessingOver (SPI flush, Jackson modules via generateTarget, perf report), isFinished/endRound (once-round marker + SPI finalization). ~600 lines of driver code left the class.

Verification

  • mvn clean install — 567/567 tests green (processor 535); pinned diagnostic strings unchanged (domain keeps its "builder"/"Jackson module" nouns, framework messages use "target")
  • mvn -pl example clean compile after removing generated-example-builder — regenerated sources byte-identical (git status clean)
  • CodeQL: genuinely-dead java/unused-parameter params removed; the remaining ones are intentional extension-point parameters, dismissed as "won't fix" in the Security tab (CodeQL suppression comments are not honored by code scanning without an extra dismiss Action)
  • SonarCloud quality gate: passed

This lands before the #368 module move: the move PR then carries a third artifact (annotation-processing) holding this package plus the JSR-269-bound helpers, per the plan discussion.

Lifecycle parity with main: a round with collected elements but an empty plan (e.g. every @SimpleBuilder outside builderGenerationPackages) still runs endRound → finishCompilation(), so MapStruct maps such beans via setters instead of deferring until "No implementation was created" — pinned by MapStructSpiIntegrationTest.mapStruct_markedBeanOutsideGenerationScope_shouldMapViaSetters.

@devin-ai-integration
devin-ai-integration Bot force-pushed the devin/1792924834-generic-processor-driver branch from 75e1134 to 25021b3 Compare October 25, 2026 12:46
…eLoader — driver defaults for logger/generator/formatter creation
Comment thread docs/CONTRIBUTING.md Resolved
Comment thread docs/CONTRIBUTING.md Resolved
Comment thread docs/DEBUG_LOGGING.md Resolved
Comment thread docs/DEBUG_LOGGING.md Resolved
@sonarqubecloud

sonarqubecloud Bot commented Nov 1, 2026

Copy link
Copy Markdown

@AndreasIgel
AndreasIgel merged commit 9fb8e11 into java-helpers:main Nov 1, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generalize annotation-processor machinery into a reusable processing framework

3 participants