Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9d0465b
add conformance check for the new evaluation example
Apr 22, 2026
5abb3f2
add new evaluation test case (WIP)
Jun 15, 2026
22ae0df
Initial commit
PresiProdanov Jun 16, 2026
c26a34e
Initial commit
PresiProdanov Jun 16, 2026
484bee7
Refactor updater factory and add combined evaluation assertions
PresiProdanov Jun 18, 2026
e5bcdb3
Merge branch 'dev' into PPr/initial
PresiProdanov Jun 22, 2026
4bc5683
Changes to handling of imports, requirements updated, new TODOs
PresiProdanov Jun 23, 2026
b79772b
Fix todos for import handling
PresiProdanov Jun 24, 2026
61c0d25
Add more tests and checks for associations
PresiProdanov Jun 29, 2026
9188a98
Refactor CodeAdapter.java
PresiProdanov Jun 30, 2026
191aaad
Refactor AdaptationConflictDetector and SpoonUpdater with helpers. Ad…
PresiProdanov Jul 1, 2026
3a043ce
CodeUpdaterMill, CodeAdapter refactoring, small changes and possible …
PresiProdanov Jul 10, 2026
55f64f6
Enhance AdaptedCodeMerger to handle relocations and imports for adapt…
PresiProdanov Jul 11, 2026
b901aca
Refactor IncarnationContext and related classes to use StableElementK…
PresiProdanov Jul 11, 2026
30997fd
Refactor BasicUpdateHandler to incorporate stable key selection for i…
PresiProdanov Jul 11, 2026
3eed1ef
Refactor CodeUpdaterMill to simplify updater lifecycle management and…
PresiProdanov Jul 11, 2026
65dd134
Refactor CodeUpdater and related services to streamline method and fi…
PresiProdanov Jul 11, 2026
c747290
Refactor AdaptationConflictDetector and related classes to simplify c…
PresiProdanov Jul 11, 2026
c6b7bda
Refactor Spoon transformation services to integrate executable repair…
PresiProdanov Jul 12, 2026
1b85b17
Refactor adaptation context handling to utilize CDModelIndex as it wa…
PresiProdanov Jul 13, 2026
8856eb7
Refactor constants and update method signatures for clarity; enhance …
PresiProdanov Jul 16, 2026
e0327c2
Refactor AdapterUtils and related classes to utilize AdaptReference f…
PresiProdanov Jul 16, 2026
fbff494
Add a few more examples to explain the functionalities better; more m…
PresiProdanov Jul 16, 2026
e3d7fc3
Missing StaticDelegator for one test case and refining member type im…
PresiProdanov Jul 17, 2026
57baf3b
Update method signatures for clarity; enhance type handling and impor…
PresiProdanov Jul 20, 2026
1699e83
Merge test case from paper; add more javadocs to all files
PresiProdanov Jul 20, 2026
0ff95ea
Merge master; implement R024 and R026; small bug fixes
PresiProdanov Aug 1, 2026
0715a67
Add large fulfillment platform evaluation case
PresiProdanov Aug 2, 2026
ea696ee
Refactor output path handling in tests to use temporary directories; …
PresiProdanov Aug 24, 2026
540ddd9
Merge remote-tracking branch 'origin/dev' into PPr/2-iteration
PresiProdanov Sep 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
405 changes: 405 additions & 0 deletions ref-code-adaptation/README.md

Large diffs are not rendered by default.

650 changes: 650 additions & 0 deletions ref-code-adaptation/architecture.md

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions ref-code-adaptation/evaluation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Ref-Code-Adaptation Evaluation Cases

This document describes the numbered evaluation resources under
`src/test/resources/de/monticore/codeAdaption/evaluation`. Each case contains a
reference CD, a concrete CD, and either adapter handwritten code, concrete
handwritten code, or both.

## Case Overview

| Case | Purpose | Inputs | Mappings | Expected behavior | Test coverage |
| --- | --- | --- | --- | --- | --- |
| `design_patterns` | Composite and Adapter examples imported from the evaluation-paper branch. | `Composition.cd` or `Adapter.cd`, `DesignPatterns.cd`, matching `hwc/` directory | `ci` and `re`, or `npg` | Adapts the paper's Composite example for two mappings and its Adapter example. | `DesignPatternTest.adaptsCompositePatternForBothMappings`, `DesignPatternTest.adaptsAdapterPattern` |
| `testcase_1` | University/user-role case with two mappings over shared concrete types. | `Reference.cd`, `Concrete.cd`, `reference/` | `stud`, `prof` | Adapts both mappings successfully. The former association-role conflict is resolved by treating roles as generated fields owned by the opposite association side. | `CodeAdapterTestCase1` |
| `testcase_2_cd4code` | CD4Code user-role case with generated follow-up code. | `Reference.cd`, `Concrete.cd`, `hwc/` | `stud` | Adapts the reference handwritten code to `Student`/`HiwiRole` and then runs Java generation for the concrete CD. | `CodeAdapterTestCase2a` |
| `testcase_6_builder_pattern` | Builder pattern expansion for multiple concrete target types. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `buildPat` | Creates `PersonBuilder` and `TaskBuilder`, adapts setters/build methods, and keeps existing concrete classes. | `BuilderPatternAdapterTest` |
| `testcase_7_controller_worker_observer` | Combined builder and observer adaptation where workers are observers. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `buildPat`, `observer` | Generates/adapts controller and worker builder/observer code while preserving concrete worker classes. | `ControllerWorkerAdapterTest` |
| `testcase_8_controller_worker_observer_reversed` | Variant of controller/worker observer adaptation with reversed observer modelling. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `buildPat`, `observer` | Adapts the same pattern combination when the observer interface/class relation is represented differently. | `ControllerWorkerAdapterReversedTest` |
| `testcase_9_strategy_pattern` | Strategy pattern adaptation. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `strategy` | Adapts context and strategy types/methods to payment processing classes and strategies. | `StrategyAdapterTest` |
| `testcase_10_singleton_pattern` | Singleton pattern adaptation for multiple concrete singleton classes. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `singleton` | Produces singleton-style code for `DatabaseConnection` and `Logger` and preserves the application code. | `SingletonAdapterTest` |
| `testcase_11_template_method_pattern` | Template method adaptation with several concrete processors. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `template` | Adapts the abstract template and concrete processor methods for CSV, JSON, and XML processors. | `TemplateMethodAdapterTest` |
| `testcase_12_strategy_singleton_pattern` | Combined singleton and strategy adaptation. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `singleton`, `strategy` | Adapts configuration manager singleton behavior and storage strategy variants. | `StrategySingletonAdapterTest` |
| `testcase_13_template_observer_pattern` | Combined template method and observer adaptation. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `template`, `observer` | Adapts data pipeline template methods and observer notification/update code. | `TemplateObserverAdapterTest` |
| `testcase_14_adapter_factory_combined` | Combined adapter and factory pattern resource case. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `adapter`, `factory` | Adapts a shipping port/carrier adapter with factory creation. | `CombinedPatternEvaluationTest` |
| `testcase_15_composite_decorator_combined` | Combined composite and decorator pattern resource case. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `composite`, `decorator` | Adapts renderable components, groups, and decorators. | `CombinedPatternEvaluationTest` |
| `testcase_16_observer_command_combined` | Combined observer and command pattern resource case. | `Reference.cd`, `Concrete.cd`, `adapter/`, `concrete/` | `observer`, `command` | Adapts event bus observers that are also workflow commands and a command queue invoker. | `CombinedPatternEvaluationTest` |
| `testcase_17_fulfillment_platform` | Large fulfilment-platform evaluation required by R-011. | `Reference.cd`, `Concrete.cd`, `adapter/`, deliberately incomplete `concrete/` | `observer`, `command`, `strategy`, `adapter` | Derives all mappings manually, supplies missing pattern members through adapted reference HWC, compiles the 27-type combined system, and executes its end-to-end and queue failure/reuse scenarios. | `LargeFulfillmentSystemEvaluationTest` |

## Notes

- Cases 6 through 13 are covered by pattern-focused JUnit tests in the main
`de.monticore.codeAdaption` test package.
- Cases 14 through 16 are covered by executable wrappers in
`CombinedPatternEvaluationTest`.
- Case 17 is the large multi-pattern R-011 evaluation. Its dedicated test
verifies that adaptation, rather than concrete-source copying, supplies the
mapped behavior.

## TOP Composition Cases

These cases are decided independently for every adapted type. In the examples,
the reference `Builder` pattern is adapted to `PersonBuilder`; the concrete CD
containing `Person` remains mandatory, while concrete handwritten Java is
optional.

| Case | Expected composition | Tests that assert it |
|-------------------------------------------------------------------------| --- | --- |
| 1. Concrete HWC exists without a TOP superclass. | The copied HWC becomes `PersonBuilder extends PersonBuilderTOP`; the adapted implementation becomes `PersonBuilderTOP`. | `TopCodeComposerTest.createsTopCompanionWithoutMergingMembersOrRewritingPublicTypeReferences` |
| 2. No matching concrete HWC exists. | The adapted implementation is emitted directly as `PersonBuilder`; no TOP layer is introduced by composition. | `TopCodeComposerTest.emitsAdaptedTypeDirectlyWhenConcreteHwcIsMissing` |
| 3. Concrete HWC already declares `extends PersonBuilderTOP`. | The existing inheritance is retained and the adapted implementation supplies `PersonBuilderTOP`. Fluent public self types continue to compile. | `TopCodeComposerTest.preservesFluentPublicSelfTypeInGeneratedTopCode`, `BuilderPatternAdapterTest.topSeparationCompilesFluentBuilderWithConcreteHwc` |
| 4a. Reference code uses TOP and no matching concrete HWC exists. | The adapted `PersonBuilder extends PersonBuilderTOP` is emitted unchanged; no TOPTOP layer is needed. | `TopCodeComposerTest.keepsExistingReferenceTopWhenNoConcreteHwcExists`, `CodeAdapterTestCase2a.adaptsAssociationRoleUsageWithoutRunningARegularGenerator` |
| 4b. Reference code uses TOP and concrete HWC also owns `PersonBuilder`. | Concrete HWC extends `PersonBuilderTOP`; adapted reference HWC becomes `PersonBuilderTOP extends PersonBuilderTOPTOP`. The external generator supplies the actual TOPTOP base. | `TopCodeComposerTest.shiftsReferenceTopToTopTopWhenConcreteHwcOwnsThePublicType`, `CodeAdapterTestCase2a.topApiAddsInheritanceAndEmitsAdaptedImplementationAsTop` |

Case 1 and case 3 have the same final two-class shape. They differ in whether
the adapter adds the HWC `extends PersonBuilderTOP` clause or merely retains the
clause already written by the developer. In case 4, the adapter creates the
adapted `PersonBuilderTOP` middle layer and only references—not generates—the
external `PersonBuilderTOPTOP` base.
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package de.monticore.codeAdaption;

import de.monticore.codeAdaption.utils.JavaLoader;
import java.io.IOException;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.Optional;

/** Owns normalized paths, temporary directories, and transactional output publication. */
final class AdaptationWorkspace {

private final Path referenceSource;
private final Optional<Path> concreteSource;
private final Path output;

AdaptationWorkspace(Path referenceSource, Path concreteSource, Path output) {
this(
referenceSource,
Optional.of(Objects.requireNonNull(concreteSource, "concreteSource")),
output);
}

AdaptationWorkspace(Path referenceSource, Optional<Path> concreteSource, Path output) {
this.referenceSource = canonicalPath(referenceSource);
this.concreteSource =
Objects.requireNonNull(concreteSource, "concreteSource")
.map(AdaptationWorkspace::canonicalPath);
this.output = canonicalPath(output);
validatePaths(this.referenceSource, this.concreteSource, this.output);
}

Path referenceSource() {
return referenceSource;
}

Optional<Path> concreteSourceOptional() {
return concreteSource;
}

Path output() {
return output;
}

void validateReadOnlyInput(Path input, String label) {
rejectOverlap(label, canonicalPath(input), output);
}

Path createStagingDirectory() {
try {
Files.createDirectories(output.getParent());
return Files.createTempDirectory(output.getParent(), ".code-adaptation-staging-");
} catch (IOException exception) {
throw new CodeAdaptationException("Could not create output staging directory", exception);
}
}

Path createMappingDirectory(Path stagingDirectory) {
try {
Path directory = Files.createTempDirectory(stagingDirectory, ".mapping-");
requireContained(stagingDirectory, directory);
return directory;
} catch (IOException exception) {
throw new CodeAdaptationException("Could not create mapping workspace", exception);
}
}

void discard(Path directory) {
if (directory != null && Files.exists(directory)) {
JavaLoader.removeDirectory(directory);
}
}

void publish(Path stagingDirectory) {
requireContained(output.getParent(), stagingDirectory);
Path backup = null;
try {
if (Files.exists(output)) {
backup = Files.createTempDirectory(output.getParent(), ".code-adaptation-backup-");
Files.delete(backup);
moveDirectory(output, backup);
}
moveDirectory(stagingDirectory, output);
if (backup != null) {
JavaLoader.removeDirectory(backup);
}
} catch (IOException | RuntimeException exception) {
if (backup != null && Files.exists(backup)) {
try {
if (Files.exists(output)) {
moveDirectory(output, stagingDirectory);
}
if (!Files.exists(output)) {
moveDirectory(backup, output);
}
} catch (IOException restoreFailure) {
exception.addSuppressed(restoreFailure);
}
}
throw new CodeAdaptationException("Could not publish adapted code to " + output, exception);
}
}

static void validatePaths(Path referenceSource, Path concreteSource, Path output) {
validatePaths(
referenceSource,
Optional.of(Objects.requireNonNull(concreteSource, "concreteSource")),
output);
}

static void validatePaths(Path referenceSource, Optional<Path> concreteSource, Path output) {
Path normalizedReference = normalizedPath(referenceSource);
Optional<Path> normalizedConcrete =
Objects.requireNonNull(concreteSource, "concreteSource")
.map(AdaptationWorkspace::normalizedPath);
Path normalizedOutput = normalizedPath(output);
if (normalizedOutput.getParent() == null) {
throw new IllegalArgumentException(
"Output path must not be a filesystem root: " + normalizedOutput);
}
rejectOverlap("reference handwritten code", normalizedReference, normalizedOutput);
normalizedConcrete.ifPresent(
concrete -> rejectOverlap("concrete handwritten code", concrete, normalizedOutput));
}

private static void rejectOverlap(String label, Path input, Path output) {
if (input.equals(output) || input.startsWith(output) || output.startsWith(input)) {
throw new IllegalArgumentException(
"Output path must not overlap " + label + ": " + output + " and " + input);
}
}

private static Path normalizedPath(Path path) {
return Objects.requireNonNull(path, "path").toAbsolutePath().normalize();
}

private static Path canonicalPath(Path path) {
Path normalized = normalizedPath(path);
Path existing = normalized;
while (existing != null && !Files.exists(existing)) {
existing = existing.getParent();
}
if (existing == null) {
return normalized;
}
try {
return existing.toRealPath().resolve(existing.relativize(normalized)).normalize();
} catch (IOException exception) {
throw new CodeAdaptationException("Could not resolve path " + normalized, exception);
}
}

private static void requireContained(Path parent, Path child) {
Path normalizedParent = normalizedPath(parent);
Path normalizedChild = normalizedPath(child);
if (!normalizedChild.startsWith(normalizedParent) || normalizedChild.equals(normalizedParent)) {
throw new IllegalArgumentException(
"Temporary path escapes its workspace: " + normalizedChild);
}
}

private static void moveDirectory(Path source, Path target) throws IOException {
try {
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException exception) {
Files.move(source, target);
}
}
}
Loading
Loading