diff --git a/ref-code-adaptation/README.md b/ref-code-adaptation/README.md new file mode 100644 index 00000000..1a072027 --- /dev/null +++ b/ref-code-adaptation/README.md @@ -0,0 +1,405 @@ +# ref-code-adaptation + +`ref-code-adaptation` adapts concrete Java code from reference Java code and class-diagram mappings. +## Inputs + +The main entry point is `de.monticore.codeAdaption.CodeAdapter`. + +Required inputs: + +- reference class diagram +- concrete class diagram +- mapping names, such as `buildPat`, `observer`, or `ref` +- adapter/reference Java source directory +- output directory + +The API can run without a concrete Java source directory. In that case it emits +only Java obtained by adapting the reference implementation; it does not run a +regular model-to-Java generator. `adapt(...)` retains the legacy member-merge +behavior, while `adaptWithTopSeparation(...)` keeps matching concrete HWC and +adapted implementations in separate TOP-related classes. The CLI still exposes +only its existing merge workflow and requires a concrete-code argument. + +The class diagrams define which reference classes, fields, and methods incarnate as concrete Java elements. Java code is parsed and transformed with MontiCore JavaDSL and Spoon. + +## Adaptation Flow + +1. Load reference and concrete class diagrams with `JavaLoader.parseCD`. +2. Build mapping-specific incarnation contexts. +3. Validate inputs and detect unresolved or ambiguous mappings before creating + a staging workspace or changing existing output. +4. Copy and filter reference adapter code for the active mapping. +5. Adapt types, fields, methods, parameters, constructor calls, and pattern-derived members. +6. Apply the final API-selected composition: + - merge adapted members into concrete HWC for `adapt(...)`, or + - keep HWC separate through `Concrete extends ConcreteTOP` for + `adaptWithTopSeparation(...)`. +7. Copy remaining concrete files and clean staged Java: + - remove `@Adapt` annotations with Spoon + - remove invalid generated imports through JavaDSL import declarations + - preserve valid existing imports without inventing imports for unresolved + simple names + - keep Spoon as the only whole-file formatter; import cleanup edits only + import declaration source ranges +8. Publish the completed staging directory transactionally, restoring the + previous output if publication fails. +9. Compile and structurally verify generated Java in tests where all external + regular-generator dependencies are supplied. + +The context-building step depends on the `useConcretization` argument of +`CodeAdapter.adapt(...)`. + +### `useConcretization=true` + +This mode delegates model repair to cdconcretization before code adaptation: + +1. `ConcretizationCompleter` completes the concrete CD by adding safe missing + model elements, repairing deterministic inheritance gaps, adapting + multi-incarnation names, and removing redundant inherited attributes. +2. `CDConformanceChecker` validates the completed CD and provides the + mapping-specific incarnation mapping. +3. `JavaTypeUpdateService` projects Java-expressible elements that were added + to the completed concrete CD, such as missing fields, methods, types, enum + constants, inheritance, and interfaces. +4. `ReferenceCodeDependencySelector` retains the transitive source-local + helper closure of mapped reference types. Required Java-only helper members + and helper classes do not need ignore annotations; unrelated source units + are excluded. Reference-CD type uses inside retained helpers are adapted by + the normal transformation passes. + +Use this mode when the concrete CD may need deterministic model-level repair +before Java adaptation. + +#### Persisting the concretized CD + +The full adapter APIs accept `persistConcretizedCD` as their final boolean +argument: + +```java +adapter.adapt( + referenceCD, + concreteCD, + mappings, + referenceCodePath, + concreteCodePath, + outputPath, + true, // run cdconcretization + true, // allow exact common-parent grouping + false); // do not persist the concretized CD +``` + +When `persistConcretizedCD` is `true`, the working concrete CD is written to the +output directory under the input concrete CD's filename. It is also written +when concretization or a later adaptation step fails, so the partial model can +help diagnose the failure. Set the argument to `false` to suppress both the +successful-output file and this failure diagnostic. The argument is ignored +when `useConcretization` is `false`. + +Existing overloads without this argument remain available and default to +persisting the CD. The option applies to `adapt(...)`, +`adaptWithoutConcreteCode(...)`, and both `adaptWithTopSeparation(...)` forms. +For the CLI, persistence is enabled with `--concretize` unless +`--no-persist-concretized-cd` is supplied. + +### `useConcretization=false` + +This mode does not mutate or complete the concrete CD. It builds a manual +incarnation context from: + +- explicit stereotypes on concrete-CD elements for the active mapping name, +- deterministic same-name rules enabled by `CDConfParameter.NAME_MAPPING`, +- manual `<>` mappings derived from already-known + incarnations. + +The manual branch does not call `ConcretizationCompleter` or +`CDConformanceChecker`, and it does not clone or create CD elements. Conflicts +are reported together as a `CodeAdaptationException` before staging is created +or existing output is touched. + +Supported manual `forEach` targets: + +- Type target: `<> class DataClassBuilder` +- Attribute target: `<> any attribute` +- Owner-implied attribute target: `<> any getAttribute()` + +For method `forEach` over attributes, concrete method incarnations are derived +from already-mapped concrete attributes and deterministic method names, for +example `getAttribute()` to `getFirstName()` and `getAge()`. The manual mode +does not invent cdconcretization suffix rules; concrete stereotypes or enabled +name rules must make the target deterministic. + +#### Manual mappings without concrete Java (R-024) + +The no-concrete-code API and manual mapping derivation are independent choices. +This call combines them explicitly: + +```java +adapter.adaptWithoutConcreteCode( + referenceCD, + concreteCD, + mappings, + referenceCodePath, + outputPath, + false, // do not run cdconcretization + true); // allow exact common-parent grouping +``` + +The implementation then follows one transactional pipeline: + +1. `AdaptationWorkspace` represents the concrete Java input as absent, while + both CDs and the reference Java directory remain read-only inputs. +2. Because `useConcretization` is false, the parsed concrete CD is neither + cloned nor completed. `AdaptationContextFactory` delegates every mapping to + `ManualIncarnationContextBuilder`. +3. The builder scans concrete types first, then members within their mapped + owners. An explicit `<>` wins; enabled + same-name/signature rules are the deterministic fallback. Reference-side + `forEach` stereotypes copy or expand mappings already found in those passes. +4. `AdaptationConflictDetector` and strict reference-Java validation run before + staging. Manual mode does not enable R-026's unannotated-helper policy. +5. Normal isolated mapping passes transform the reference Java. With no + concrete Java base, this adapted result becomes the staged authoritative + handwritten code. +6. The adapted Java is cleaned and published directly. No model-only types, + association fields, or generated TOP bases are synthesized. An empty + reference Java directory therefore produces no Java output. + +R-026 is intentionally a concretization-mode feature. Its transitive +Java-helper selection is not used by the manual path above; unmatched manual +helpers must still satisfy the normal manual validation policy. + +#### TOP separation + +```java +adapter.adaptWithTopSeparation( + referenceCD, + concreteCD, + mappings, + referenceCodePath, + concreteCodePath, // omit this argument when no concrete HWC exists + outputPath); +``` + +TOP decisions are made per adapted concrete type. Without matching HWC, the +adapted `Concrete` declaration is emitted directly. With matching HWC, the +adapter emits the implementation as `ConcreteTOP` and adds or retains +`extends ConcreteTOP` on handwritten `Concrete`. If ordinary adaptation had +already produced `Concrete extends ConcreteTOP`, the separated implementation +becomes `ConcreteTOP extends ConcreteTOPTOP`. + +The four supported cases can be illustrated by adapting the reference +`Builder` pattern to the concrete `Person` entity. The adapted pattern type is +called `PersonBuilder`. The concrete CD containing `Person` is mandatory in +every case; "concrete HWC" below means optional handwritten Java. + +1. **Concrete HWC exists but does not use TOP yet.** The adapted and + handwritten declarations would both be named `PersonBuilder`. The adapter + moves the adapted implementation to `PersonBuilderTOP` and adds the + inheritance relationship to the copied HWC: + + ```java + class PersonBuilder extends PersonBuilderTOP { /* concrete HWC */ } + class PersonBuilderTOP { /* adapted Builder pattern */ } + ``` + +2. **No concrete HWC exists.** There is no Java-name collision, so the adapted + implementation keeps its ordinary name: + + ```java + class PersonBuilder { /* adapted Builder pattern */ } + ``` + +3. **Concrete HWC already explicitly uses TOP.** The handwritten Java already + contains `extends PersonBuilderTOP`, but normally no corresponding TOP + source file exists yet. The adapter retains that relationship and produces + the expected class from the adapted pattern implementation: + + ```java + class PersonBuilder extends PersonBuilderTOP { /* concrete HWC */ } + class PersonBuilderTOP { /* adapted Builder pattern */ } + ``` + + Unlike case 1, the adapter does not need to add the `extends` clause. If a + concrete `PersonBuilderTOP.java` already exists, producing the adapted class + with the same identity is rejected as a collision rather than merged or + overwritten. + +4. **The reference code already uses TOP.** For example, reference HWC may + declare `Builder extends BuilderTOP`. Without concrete HWC, adaptation emits + `PersonBuilder extends PersonBuilderTOP` unchanged. If concrete HWC also + owns `PersonBuilder`, the adapted reference HWC is moved into the middle + layer: + + ```java + class PersonBuilder extends PersonBuilderTOP { /* concrete HWC */ } + class PersonBuilderTOP extends PersonBuilderTOPTOP { + /* adapted reference HWC */ + } + ``` + + Renaming the adapted declaration to `PersonBuilderTOP` would otherwise + create the invalid self-inheritance `PersonBuilderTOP extends + PersonBuilderTOP`. The adapter therefore changes that parent reference to + `PersonBuilderTOPTOP`. It does not generate the actual + `PersonBuilderTOPTOP` class; an external regular generator must supply it. + +The adapter renames only the implementation declaration and its constructors; +ordinary references remain references to public `Concrete`. Fields and methods +are not merged or compared across the two classes. Consequently, Java's normal +override, hiding, and type-checking rules apply to the resulting hierarchy. +Explicit `this` values in an implementation moved to `ConcreteTOP` are emitted +as `(Concrete) this`. This preserves public self-return types and fluent APIs; +targets such as `this.field` remain unchanged so they still select the adapted +TOP member. Exact package-qualified HWC matches also work for generated pattern +types, such as `PersonBuilder`, that are not declarations in the concrete CD. +`ConcreteTOPTOP` and other regular-generator artifacts must be supplied +externally when the selected reference-code structure requires them. + +## Association Adaptation + +Associations affect Java through navigable role fields. For example, + +```cd +association [1] User -> (roles) Role [*]; +``` + +can be generated by an external CD4Code configuration as a collection-valued +field comparable to `Set roles` on `User`. `CodeAdapter` and CD4Code have +separate responsibilities: + +- `CodeAdapter` adapts handwritten Java that uses an association. It updates + endpoint type references and owner-scoped direct role-field reads or writes, + such as `this.roles`. +- An external CD4Code invocation can create the role-derived fields with + `--fieldfromrole navigable`. + Association fields are not projected as ordinary CD attributes by + `CodeAdapter`. + +Given `User -> (roles) Role` and the concrete association +`Student -> (roles) HiwiRole`, code such as +`for (Role role : this.roles)` is adapted to use `HiwiRole` while retaining the +role `roles`. If the concrete role is renamed, direct field accesses are renamed +as well. Bidirectional associations are handled per role: a role written on one +association side produces a field on the opposite endpoint, and reads and +assignments are rewritten only inside that owning Java type. + +## Important Implementation Points + +- `JavaLoader.parseCD` must use the same symbol-table setup as `loadCD`, including built-in types. CDs must declare imports explicitly for Java library types such as `Object`, `String`, `List`, or `Optional`. +- `BasicUpdateHandler` handles ordinary and multi-incarnation adaptation through one optional, + immutable stable-key selection. An empty selection represents the ordinary case. +- `MappingAdaptationRunner` owns the isolated per-pass updater lifecycle, output filtering, merge, + cleanup, and error wrapping. +- `ReferenceCodeDependencySelector` builds a package-qualified dependency graph + once from the reference source snapshot and selects mapping-specific helper + closures in concretization mode. Ambiguous source-local type resolution is + rejected deterministically. +- `TopCodeComposer` analyzes all per-type TOP decisions before modifying cloned + ASTs. It adds or deduplicates HWC inheritance, renames only adapted + declarations and their constructors, and leaves ordinary references pointing + to the public concrete type. +- `CDTypeRelations` centralizes direct generated-AST access for interfaces, + superclasses, modifiers, and type-reference printing. Runtime Java reflection + is not used by the adapter. +- `ManualIncarnationContextBuilder` is the non-mutating context builder for `useConcretization=false`. +- `IncarnationContext` owns one immutable stable-key mapping. MontiCore symbols + are retained only as mapped-element payload for AST operations; symbol object + identity is not used for incarnation lookup. +- `AdaptationConflictDetector` validates manual mappings before Java output is written. +- `JavaTypeUpdateService` fills Java-expressible gaps only after + cdconcretization, such as missing fields, methods, types, enum constants, + inheritance, and interfaces. +- `JavaSourcePostProcessor` is the final source cleanup step. It parses JavaDSL + compilation units for import declarations and removes only known invalid or + malformed generated imports. It does not infer missing JDK imports from + unresolved simple names. +- `JavaSourceNames` centralizes Java/CD type naming, generic rendering, arrays, + primitives, `void`, and `any` normalization. `JavaMethodSignatures` validates + method names and parameter lists through the JavaDSL grammar rather than + splitting them manually; invalid signatures remain unmatched. Callers should + use these utilities instead of open-coded name or signature parsing. +- `SpoonUpdater` is the supported code updater. `RegexUpdater` is preserved + unchanged as deprecated legacy source for compatibility and is outside the + supported adaptation path. +- `CodeUpdaterMill` owns updater initialization and isolation. Use `init()` for + Spoon, `init(Supplier)` for an alternative updater, `getUpdater()` for the + current pass, and `reset()` between isolated passes. +- Generated builder bodies are represented as structured `MethodBodySpec` + values, so setter-return and constructor-return methods are built through + Spoon statements instead of parsed string snippets. +- Concretization compatibility tests inspect and compile the adapter's real output; they do not + substitute a synthetic CD-to-Java projection. +- Concrete handwritten Java packages are authoritative when an adapted top-level type has one + unique same-name concrete declaration. The adapted declaration is merged into that package, and + other adapted units receive imports for types that no longer share their original package. + Multiple concrete package candidates or conflicting imports are rejected instead of guessed. +- The concretization compilation oracle may stub a missing type only when it is declared by the + original concrete CD and one concrete Java package is unambiguous. Types introduced only from the + reference CD are not stubbed, so stale reference names and missing adapter output fail compilation. +- Final generated output must not contain adapter metadata. +- Generated Java cleanup intentionally avoids broad source-text regex formatting. + Comments, literals, generics, operators, and method bodies should be left to + Spoon, JavaDSL, or AST-level code. + + + +## Manual Conflict Detection + +When `useConcretization=false`, the adapter should detect risks that +cdconcretization would otherwise repair or reject on the model level. Reported +conflict categories include: + +- missing used type or `any` type incarnation +- type-kind mismatches that cannot be handled by the selected parent/interface mode +- illegal or ambiguous inheritance situations +- field name/type conflicts and duplicate generated field targets +- duplicate Java method signatures with incompatible return types +- ambiguous overloaded stereotypes without a signature +- enum constant order conflicts +- association-derived Java field conflicts, including cardinality and direction ambiguity +- missing manual `forEach` target incarnations + +`CodeAdapterTestCase1` exercises the `stud` and `prof` mappings together and is +expected to adapt successfully. Association-role conflicts that remain +ambiguous are reported before output generation; the testcase itself is no +longer an error-only scenario. + +## Verification + +The cdconcretization-derived tests use the adapter's generated output as their +oracle input. A correct case means: + +- final Java files exist +- no final Java file contains `@Adapt` or the `Adapt` import +- generated Java compiles +- generated Java structure matches the Java-expressible `*Conc.cd` to `*Out.cd` + completion delta for every type materialized in adapter output, except for the + explicitly registered model boundaries described below + +Exact Java source text is not the main oracle, but generated Java should still be readable and consistently spaced. + +## Current Limitations + +No fixture is hidden in a generic disabled-case bucket. Three underspecified +elements without an incarnation are explicit rollback/error tests: one attribute, +one method parameter, and one method return type. They are invalid because the +placeholder type `any` cannot be emitted into a concrete CD. + +Three fixtures exercise completion semantics that upstream `cdconcretization` +explicitly does not implement. They remain executable rejection/rollback tests: + +- attribute `forEach` across inherited declaring owners +- attribute `forEach` without a target incarnation (requires optional-member or + `matchStructure` semantics) +- method-target `forEach` + +Five fixtures still compile and run, but do not use their complete model as a +structural oracle. Each is registered by path and reason in +`CDConcretizationTestCases`: four mirror tests explicitly disabled upstream +because of unresolved association, binding, cross-incarnation, or bidirectional +`forEach` semantics; the fifth is an inherited attribute-type mismatch that the +current concretizer incorrectly accepts. These cases are not skipped locally; +metadata cleanup and strict Java compilation remain mandatory. + +Fixtures for which upstream provides no separate `*Out.cd` use their reference +CD as the structural oracle, matching the assertions in the upstream tests. diff --git a/ref-code-adaptation/architecture.md b/ref-code-adaptation/architecture.md new file mode 100644 index 00000000..aa7a07f5 --- /dev/null +++ b/ref-code-adaptation/architecture.md @@ -0,0 +1,650 @@ +# ref-code-adaptation Architecture + +This document describes the intended architecture of `ref-code-adaptation`. +It records the module boundaries that keep adapter orchestration, matching, +validation, model analysis, and code rewriting independent from each other. + +The code adapter has one central responsibility: transform handwritten +reference Java code into concrete Java code according to class-diagram +incarnation mappings. The transformation combines two different models: + +- class diagrams, parsed as MontiCore CD ASTs +- Java source code, parsed as JavaDSL ASTs and rewritten through a `CodeUpdater` + +The architecture separates those models from the orchestration flow. A class +that decides which CD element maps to which concrete element does not know how +Spoon renames Java fields. A class that renames Java fields does not know how +manual incarnation conflicts are detected. This separation keeps the adapter +usable with different updater implementations and keeps model-level reasoning +testable without source-rewriting side effects. + +## Layer Overview + +The module is organized into these layers: + +1. **Entry and orchestration** + Coordinates one adaptation run from input files to output files. + +2. **Model loading and indexing** + Parses CD and Java sources and exposes precomputed CD relationships. + +3. **Mapping and matching** + Interprets adapter annotations, stereotypes, name rules, and incarnation + contexts. + +4. **Validation and conflict detection** + Checks whether an adaptation run is deterministic before a staging + workspace is created or existing output is changed. + +5. **Adaptation handlers** + Convert mapping decisions into updater operations for Java AST elements. + +6. **Updater facade and updater implementations** + Apply source-code changes through Spoon or another implementation. + +7. **Post-processing and verification helpers** + Clean generated Java and support tests with structural oracles. + +The allowed dependency direction is top-down: + +```text +CodeAdapter + -> context builders / validators / handlers / output merger + -> matchers / CD indexes / Java collectors / updater interface + -> concrete updater implementation +``` + +Concrete updater code stays behind the `CodeUpdater` interface. Handler and +orchestration code talks to `CodeUpdater`, not to Spoon. + +## Packages + +### `de.monticore.codeAdaption` + +This package contains the public entry point and CLI wrapper. + +- `CodeAdapter` is the API entry point for Java adaptation. +- `CodeAdapterTool` is the CLI integration. +- `CodeAdaptationException` is the user-facing failure type for deterministic + adaptation conflicts. +- `AdaptedCodeMerger` owns Java AST filtering, splitting, deduplication, and + merging after isolated mapping runs. It also exposes package relocation + without member merging for TOP composition. +- `TopCodeComposer` performs a read-only per-type analysis and then creates + separate HWC/TOP ASTs. It never globally renames public concrete type + references. +- `OutputCodeService` owns final source cleanup, TOP self-type repair, and + concrete handwritten-file and resource inclusion. +- `AdaptationWorkspace` owns normalized path validation, safe temporary + directories, rollback, and transactional output publication. The concrete + source is optional at this boundary. + +The entry package coordinates the workflow. It owns high-level sequencing: + +- parse reference and concrete CDs +- delegate optional concrete-CD completion to the context layer +- delegate incarnation context construction to the context layer +- validate mappings before destructive output changes +- run one or more mapping adaptation passes +- compose final Java through either the legacy merger or `TopCodeComposer` +- invoke final cleanup and concrete file/resource copying through + `OutputCodeService` +- repair explicit self values in generated TOP implementations against their + public HWC subtype before transactional publication + +The entry package does not own low-level rewriting. It delegates Java changes to +`CodeUpdater`, mapping lookups to context builders and validators, and CD +relationship lookups to `CDModelIndex`. + +`CodeAdapter` plans mapping passes with immutable stable-key selections and accumulates their +results. `MappingAdaptationRunner` owns the complete isolated pass operation: cloning reference +Java ASTs, temporary mapping directories, updater initialization, handler execution, output +filtering, merge, cleanup, and error wrapping. + +#### `AdaptedCodeMerger` + +`AdaptedCodeMerger` is package-private because it is part of the orchestration +implementation, not the public API. It owns all Java compilation-unit decisions +that happen after a handler has produced adapted code: + +- `filterCodeForMapping(...)` keeps only Java units relevant for the current + mapping or explicitly ignored by adapter metadata. +- `splitCompilationUnitsByType(...)` turns one JavaDSL compilation unit with + multiple top-level types into one unit per top-level type, preserving package + declarations and assigning a source file name per generated type. +- `mergeAdaptedCode(...)` accumulates generated units from several mappings and + merges same-file units through `AdapterUtils.mergeAsts(...)`. +- `mergeAdaptedCodeIntoConcreteBase(...)` overlays adapted pattern code on top + of concrete handwritten Java and filters out reference-only template artifacts. When a unique + same-name concrete declaration moves an adapted type into another package, the merger reconciles + imports in dependent adapted units before changing package identity. Ambiguous concrete packages + and conflicting simple-name imports are rejected. +- `relocateAdaptedCodeToConcretePackages(...)` applies those package/import + decisions without merging members, allowing `TopCodeComposer` to keep the + adapted implementation separate. +- `deduplicateBySimpleName(...)` keeps one unit per simple file name, preferring + the unit with the deeper package-relative path. + +This class depends on JavaDSL AST traversal, `CodeValidator`, +`IncarnationContext`, `AdapterUtils`, `CDModelIndex`, and `JavaSourceNames`. +It does not create updaters, run conformance checks, or write files. + +#### `OutputCodeService` + +`OutputCodeService` is package-private and owns the final filesystem-facing +operations after Java AST generation: + +- `cleanCode(...)` creates a fresh updater and delegates implementation-specific + cleanup to `CodeUpdater.cleanCode(...)`. +- `copyConcreteFiles(...)` includes concrete handwritten files that were not + already produced by adaptation. +- `concreteCopyTarget(...)` preserves package-based Java output paths when a + concrete Java file parses successfully. Non-Java files preserve their original + relative paths. Unparsable concrete Java is rejected because its authoritative + package identity cannot be determined safely. + +This class keeps file-copying and cleanup out of `CodeAdapter`. It does not +inspect adaptation mappings and does not merge ASTs. + +### `de.monticore.codeAdaption.dependency` + +This package owns concretization-mode reference-source dependency selection. + +- `ReferenceCodeDependencySelector` is the parser-independent facade used by + orchestration. +- `ReferenceCodeSelection` is an immutable mapping-specific result containing + package-qualified roots and helpers, source-local dependency edges, and the + reference-CD type keys used by retained units. + +The complete graph is built once from the same reference Java snapshot used by +validation. Mapping-specific selection starts at mapped top-level types and +follows transitive source-local type dependencies. Nested, local, and anonymous +classes remain with their top-level owner. Ambiguous source-local identities +fail deterministically; unrelated Java-only units are not retained. The +Spoon-backed analyzer is implemented behind this facade in the Spoon updater +package, so Spoon model types do not escape into orchestration. + +### `de.monticore.codeAdaption.context` + +The context package contains services that prepare model-level adaptation +context before Java rewriting starts. + +- `MappingConformanceService` creates `CDConformanceChecker` instances and runs + conformance checks with the adapter's fail-quick handling. +- `ConcretizationService` wraps `ConcretizationCompleter` and owns the + concretization-mode fail-quick policy. +- `AdaptationContextFactory` builds one `IncarnationContext` per mapping. It + uses manual contexts directly in manual mode and chooses conformance-derived + or stereotype-derived contexts in concretization mode. +- `GroupingMappingService` computes concrete-type-simple-name to + grouping-type-simple-name replacements for one mapping context, preventing + grouping decisions from leaking between mappings. + +This package operates on CD ASTs, conformance checkers, and incarnation +contexts. It does not parse Java code, create updater instances, or mutate the +output directory. + +The context package is the boundary between model-level preparation and +Java-level adaptation. Manual mode and concretization mode meet here: both +produce the same `Map` shape for later workflow +phases. + +### `de.monticore.codeAdaption.matcher` + +Matchers translate annotation and naming information into `CodeMatching` +objects. They answer the question: + +```text +Which reference CD element(s) does this Java AST element represent? +``` + +Important matcher groups: + +- annotation matchers read explicit `@Adapt` metadata +- name matchers use same-name relationships +- infix matchers support generated names that contain the reference name +- composition matchers combine multiple matching strategies +- ignore and error matchers encode non-adapted and invalid cases + +Matchers do not mutate Java code. They produce mapping information consumed by +validators and handlers. + +### `de.monticore.codeAdaption.validator` + +`CodeValidator` owns Java-side validation and exposes matched elements to the +handlers. It initializes matchers for the current Java AST set and provides +queries such as: + +- matched type for a Java type declaration +- matched field for a Java field declaration +- matched method for a Java method declaration +- matched local variable or method parameter +- matched supertype usage + +Validator classes and CoCos check adapter annotation syntax and semantic +validity. They do not write output files and do not use Spoon. + +### `de.monticore.codeAdaption.handler` + +Handlers convert validated mapping information into updater operations. + +`BasicUpdateHandler` handles both ordinary and multi-incarnation passes. Its optional immutable +selection maps stable reference keys to mapped concrete elements; the empty selection is the +ordinary case. It coordinates three coarse-grained collaborators: `ConcreteSymbolResolver` +resolves stable pass selections, concrete symbols, and method signatures, +`JavaMemberUpdateService` owns variables, members, supertypes, and association-role rewrites, and +`JavaTypeUpdateService` owns type renaming, generation, and completed-CD member projection. The +handler is final and exposes no subclass variation points; its package-local forwarding methods +exist only for those collaborators. + +Handlers are the bridge between model reasoning and Java rewriting. They know +both CD concepts and Java AST concepts, but they still depend only on the +updater interface. + +### `de.monticore.codeAdaption.handler.multiIncarnation` + +This package owns incarnation context construction, selection, and manual +conflict detection. + +Core concepts: + +- `StableElementKey` is the stable, model-independent key for types, fields, + methods, and enum constants. +- `IncarnationContext` is the single immutable source of truth for one mapping. + It maps stable reference keys to `MappedElement` values containing the stable + concrete key and the concrete symbol needed by AST operations. Symbols are + payload and never lookup keys. +- `IncarnationContextBuilder` builds contexts from cdconcretization results and + stereotype overlays. +- `ManualIncarnationContextBuilder` builds contexts without mutating or + completing the concrete CD. +- `AdaptationConflictDetector` validates manual mappings before output is + removed. It is a facade over `AdaptationConflictCheck` strategies in the + `handler.multiIncarnation.conflict` package. +- adaptation passes select concrete types by stable key; member selection is derived directly from + the selected stable concrete owner. + +This package operates on CD ASTs and symbols. It does not read or write Java +source files. + +#### Conflict Checks + +Manual conflict detection is strategy-based. `ConflictDetectionContext` owns +the read-only model indexes, mapping names, incarnation contexts, conformance +parameters, type maps, and common-parent flag. `ConflictCollector` owns message +formatting and deterministic sorting. + +The default registry `DefaultConflictChecks` runs these checks: + +- `ManualMappingConflictCheck` for explicit type, field, and method mapping + conflicts. +- `ConcreteTypeStructureConflictCheck` for invalid inheritance shapes and + inheritance cycles. +- `ConcreteMemberConflictCheck` for duplicate concrete members and inherited + field conflicts. +- `EnumConflictCheck` for enum constant order conflicts. +- `AssociationConflictCheck` for association direction, cardinality, and role + field conflicts. +- `UnderspecifiedTypeConflictCheck` for unresolved `any` types in manual mode. +- `AmbiguousStereotypeConflictCheck` for overloaded method stereotypes without + signatures. +- `ForEachConflictCheck` for `forEach` declarations without concrete + incarnations. + +`AdaptationConflictDetector` exposes one static validation path over this fixed built-in set. +Test-only injection of additional checks is not part of the production API. + +### `de.monticore.codeAdaption.updater` + +The updater package defines Java source-rewriting capabilities. + +`CodeUpdater` is the facade used by orchestration and handlers. It contains the +operations the rest of the adapter needs: + +- rename Java types, fields, methods, variables, parameters, and supertypes +- update CD-only type references +- register owner-aware method rewrites and concrete invocation signatures +- add or remove fields, methods, and generated types through one field-generation operation and one + structured `MethodBodySpec` method-generation operation +- write the current Java model +- clean generated source code + +The interface is intentionally broader than simple renaming because cdconcretization and template +expansion create Java elements that were not present in the reference code. It contains only +capabilities used by production orchestration and handlers. Spoon decides internally whether a +generated method must remain signature-only; that implementation detail is not part of the updater +contract. Unsupported defaults remain only where the protected `RegexUpdater` compatibility class +cannot implement Spoon-backed generation behavior. + +`CodeUpdaterMill` owns one synchronized updater provider and one current updater +instance. `init()` restores Spoon, `init(Supplier)` configures an interchangeable +implementation, `getUpdater()` lazily creates the updater for one pass, and +`reset()` discards that instance while retaining its provider. +`MappingAdaptationRunner` and `OutputCodeService` own exception-safe reset +boundaries for isolated passes and final cleanup respectively. + +### `de.monticore.codeAdaption.updater.spoonUpdater` + +`SpoonUpdater` is the supported updater implementation. It parses Java files +with Spoon, maps MontiCore Java AST elements to Spoon elements, applies +refactorings, creates missing members, and prints generated Java. + +Spoon-specific responsibilities stay in this package: + +- Spoon model setup and output +- Spoon refactoring calls +- Spoon type-reference creation +- Spoon method-body construction +- Spoon cleanup of adapter annotations + +`SpoonUpdater` is a facade over four coarse-grained collaborators: + +- `SpoonWorkspace` owns model/factory lifecycle, printing, cleanup, and shared + type-reference construction. +- `SpoonTransformationService` owns declaration and variable renames, grouping, and + type-reference mutations. It uses the workspace's type-reference factory but owns replacement + policy because replacement changes an existing model. +- `SpoonExecutableRepairService` owns invocation renames, overload-aware method rewrites, + concrete signature repair, and interface executable validity. +- `SpoonGenerationService` owns type/member creation, method bodies, and removal. + +`SpoonElementResolver` owns JavaDSL-to-Spoon element lookup and resets its caches +when a new workspace is loaded. The transformation and executable-repair services share the same +workspace and resolver instances. Grouping/type transformations run before executable repair, and +the grouping map is passed read-only for that repair pass rather than cached twice. The facade +keeps its public compatibility wrappers. + +Other packages do not import `spoon.reflect.*`. + +### `de.monticore.codeAdaption.updater.regexUpdater` + +`RegexUpdater` is protected deprecated legacy source. It remains outside the +main architecture path and is preserved unchanged for source compatibility; +its historical text replacement is not a supported updater implementation. +New production code uses `SpoonUpdater` through `CodeUpdater`. + +### `de.monticore.codeAdaption.utils` + +Utility classes provide shared infrastructure: + +- `JavaLoader` parses and prints Java/CD inputs. +- `CDModelIndex` precomputes CD type, member, owner, inheritance, and + association relationships. +- `CDTypeRelations` centralizes direct access to CD type relationships. +- `JavaSourceNames` centralizes Java/CD type rendering, signature keys, simple + names, generics, arrays, primitives, and `any` normalization. +- `JavaMethodSignatures` validates method lookup keys with JavaDSL. Parameter + lists are represented as one synthetic generic type so the grammar, not + comma/depth string logic, decides whether they are valid. +- `JavaSourcePostProcessor` performs final import cleanup. +- `AdapterUtils` contains AST merge and file-name helpers. + +Utilities are shared only when they are stable and domain-neutral. New behavior +that belongs to a specific phase belongs in the package for that phase, not in +`utils`. + +### `de.monticore.codeAdaption.utils.visitors` + +Visitors collect Java AST elements and remove adapter metadata. They provide +small traversal helpers rather than adaptation decisions. + +`JavaAstElemCollector` gives handlers stable access to type declarations, +fields, methods, local variables, parameters, and supertypes in one Java +compilation unit. + +## Runtime Adaptation Flow + +The main workflow has these phases: + +1. `CodeAdapter` parses the concrete and reference CDs through `JavaLoader`. +2. The reference CD and original concrete CD receive immutable indexes. In + concretization mode, `ConcretizationService` completes a cloned concrete CD + once for all mappings. When `persistConcretizedCD` is enabled, this working + clone is written to the output directory even if completion or a later step + fails. In manual mode, the concrete CD remains unchanged. +3. The completed concrete CD receives one immutable index. The reference and + completed indexes are reused by context construction and every Java + adaptation pass; the original concrete index remains available only for + pre-completion decisions. +4. `AdaptationContextFactory` builds one incarnation context for each mapping. +5. Manual mode and any stereotype-derived concretization fallback run + `AdaptationConflictDetector` before staging or output publication begins. +6. `CodeAdapter` parses reference Java once, creates conformance checkers and + Java validators, and in concretization mode computes each mapping's + transitive Java-only helper closure. +7. Validation uses an explicit helper-preservation policy only in + concretization mode. Annotation and declaration-shape checks remain active; + manual-mode validation remains unchanged. +8. `AdaptationWorkspace` creates an isolated staging directory only after all + preflight checks succeed. +9. For each mapping, `GroupingMappingService` computes optional common-parent + replacements and `CodeAdapter` plans zero, one, or many isolated passes + using immutable stable-key selections; + the ordinary case is one pass with an empty selection. +10. `AdaptedCodeMerger.filterCodeForMapping(...)` keeps the mapped roots and + their package-qualified helper closure for the active mapping. +11. `MappingAdaptationRunner` configures one `BasicUpdateHandler` for the pass and runs the updater + lifecycle in a temporary output directory. +12. `AdaptedCodeMerger.splitCompilationUnitsByType(...)` gives generated + top-level types separate Java units. +13. `AdaptedCodeMerger.mergeAdaptedCode(...)` accumulates results from all + mapping passes. +14. The selected API performs final composition: `adapt(...)` overlays adapted + Java on concrete HWC, while `adaptWithTopSeparation(...)` creates separate + HWC and TOP declarations per matching concrete type. +15. `OutputCodeService.copyConcreteFiles(...)` copies remaining concrete + handwritten files into staging. +16. `OutputCodeService.cleanCode(...)` removes adapter annotations and invalid + generated imports. +17. When enabled, the completed CD is added to staging under the input concrete + CD's filename. `AdaptationWorkspace` then atomically publishes staging and + restores the prior output on publication failure. + +The workflow protects user output by performing conflict detection before +staging and replacing output only after every mapping and cleanup phase +succeeds. + +## Manual Mode and Concretization Mode + +The adapter supports two modes. + +### Manual Mode + +Manual mode uses stereotypes, deterministic name rules, and manual `forEach` +rules. It does not call cdconcretization and does not mutate the concrete CD. + +Manual mode requires strict conflict detection because there is no model-level +repair phase. `AdaptationConflictDetector` reports ambiguous or unsafe cases +before output files are touched. + +`ManualIncarnationContextBuilder` constructs the shared context shape consumed +by later handlers in three ordered passes: + +1. Map reference type keys to concrete types from explicit stereotypes on the + concrete CD, falling back to enabled deterministic name rules. +2. Map fields and methods only inside the reference owners established by the + type pass. Explicit member stereotypes take precedence over same-name or + same-signature matching. +3. Expand reference-side `forEach` declarations from mappings already known. + Type/member targets copy their incarnations; attribute-driven methods derive + exact concrete method-name candidates and still require a matching concrete + declaration. + +Stable keys, rather than symbol-object identity, make the result reusable after +index construction. The builder only records relationships; it creates no +concrete CD element and performs no Java or filesystem mutation. + +#### Manual mode with no concrete Java + +`adaptWithoutConcreteCode(..., false, ...)` composes the manual mapping path +with direct adapted-code output: + +```text +reference CD + concrete CD + reference Java + | + v +manual contexts -> conflict/Java validation -> mapping passes + | + v +adapted Java in staging -> cleanup -> atomic publication +``` + +The absence of concrete Java does not change how mappings are derived. It only +means that adapted reference Java becomes the complete Java output. The adapter +does not run a regular generator, so an empty reference directory produces no +model-only Java. The dependency selector and helper-preservation validator are +not enabled in this mode. + +### Concretization Mode + +Concretization mode calls `ConcretizationCompleter` before code adaptation. +`ConcretizationService` owns this call and the fail-quick handling around it. +It rebuilds the completed clone's symbol table after mutation so later indexes, +conformance checks, and contexts observe added model elements. +After completion, `AdaptationContextFactory` uses conformance mappings when +available and falls back to manual stereotype-derived contexts when conformance +information is missing. Such fallback mappings discard the failed checker and +must pass the same conflict detector as manual mode before staging. + +Completed-CD projection is part of `JavaTypeUpdateService`. It writes +Java-expressible elements introduced by the completed CD into generated Java +while sharing generated-type tracking and template-member handling with the +rest of type generation. + +Concretization mode also enables helper-closure preservation. Java-only helper +members and transitively required helper classes may remain unannotated. They +are retained only when reachable from a mapped reference type, and reference-CD +type uses within them participate in incarnation-pass planning and normal Spoon +rewriting. Divergent variants of one package-qualified helper produced by +multi-incarnation passes are rejected before AST merging. + +## Clean Layer Boundaries + +The following dependency rules keep the layers separated: + +- `CodeAdapter` coordinates phases but does not inspect individual Java members + for renaming details. +- `CodeAdapter` delegates context construction, grouping-map calculation, + adapted-code merging, output cleanup, and concrete-file copying to dedicated + collaborators. +- `BasicUpdateHandler` calls `CodeUpdater` but does not import Spoon. +- `JavaMemberUpdateService` owns both ordinary member changes and + association-derived role field rewrites so their target-detection and + exclusion rules stay together. +- `CodeUpdater` does not know how incarnation contexts are built. +- `SpoonUpdater` does not parse CD mappings or call matchers. +- `AdaptationConflictDetector` reports model conflicts but does not clean, + copy, or print Java files. +- `CDModelIndex` exposes model relationships and avoids repeated full-CD scans + throughout adaptation logic. +- `JavaSourceNames` owns string rendering of names and types, while + `JavaMethodSignatures` owns signature lookup keys. Open-coded signature + parsing or generic type printing stays out of handlers and validators. + Malformed signature text is not partially normalized into a potentially + valid lookup key. +- `CDTypeRelations` uses direct CD AST APIs for type relationships; runtime + reflection is not part of normal adaptation logic. + +## R-017 Refactoring State + +Requirement R-017 reduces oversized classes by responsibility, not by arbitrary +line slicing. The current refactoring state is: + +| Class | Current role | Current state | +| --- | --- | --- | +| `CodeAdapter` | Public API, pass planning, and adaptation-run orchestration | Context creation, grouping maps, merge logic, cleanup, concrete file copying, generation, and isolated pass execution have dedicated collaborators. Pass calculation remains close to orchestration because it combines mapping context, source selection, and output identity. | +| `MappingAdaptationRunner` | Isolated mapping-pass execution | Package-private runner for clone, print, updater/handler lifecycle, output filtering, merge, cleanup, and wrapped diagnostics. | +| `AdaptedCodeMerger` | Java AST filtering, splitting, deduplication, and merge policy | Extracted from `CodeAdapter`; package-private orchestration helper. | +| `OutputCodeService` | Final cleanup and concrete handwritten-file inclusion | Extracted from `CodeAdapter`; package-private orchestration helper. | +| `AdaptationContextFactory` | Mapping-to-context construction for manual and concretization modes | Extracted into `de.monticore.codeAdaption.context`. | +| `MappingConformanceService` | Safe conformance checker creation and execution | Extracted into `de.monticore.codeAdaption.context`. | +| `ConcretizationService` | Concrete CD completion and fail-quick handling | Extracted into `de.monticore.codeAdaption.context`. | +| `GroupingMappingService` | Mapping-local concrete-to-grouping replacement map with conflict rejection | Extracted into `de.monticore.codeAdaption.context`. | +| `BasicUpdateHandler` | Unified handler pipeline coordinator | About 160 lines. Accepts an optional immutable stable-key selection, delegates resolution/member/type changes, and has no compatibility constructors or subclass hooks. | +| `AdaptationConflictDetector` | Manual-mode model conflict aggregation | A roughly 60-line static facade over the built-in `AdaptationConflictCheck` strategies under `handler.multiIncarnation.conflict`. | +| `SpoonElementResolver` | JavaDSL-to-Spoon declaration lookup and caches | Extracted from `SpoonUpdater`; owns type/method matching and cloned-type cache registration. | +| `SpoonUpdater` | Concrete `CodeUpdater` facade backed by Spoon | About 184 lines after generation overloads were consolidated. Delegates to the workspace, transformation, generation, and element-resolution collaborators. | +| `SpoonTransformationService` | Spoon declaration/variable renames, grouping rewrites, and type-reference transformations | About 376 lines after executable repair was separated. It retains the lazy type-reference indexes and resolved-library safeguards. | +| `SpoonExecutableRepairService` | Invocation and concrete-signature repair | About 300 lines. It shares the workspace/resolver, treats resolved overload declarations as authoritative, and owns no duplicate type or grouping cache. | +| `IncarnationContext` | Immutable stable-key incarnation and grouping lookup | About 70 lines. Owns the only runtime incarnation representation; MontiCore symbols are mapped-element payload only. | +| `IncarnationContextBuilder` | Conformance mappings with optional stereotype overlay | About 140 lines. Populates the stable-key context directly through `IncarnationMappingSupport`. | +| `ManualIncarnationContextBuilder` | Non-mutating manual and `forEach` mapping construction | About 240 lines. Uses stable-key lookups throughout, including `forEach` expansion. | +| `IncarnationMappingSupport` | Shared stable-key mapping, stereotype, reference-index, and grouping infrastructure | About 380 lines. It builds one canonical map and one immutable implementer-to-grouping index; it has no symbol-identity registry. | + +Most production Java classes are below the 500-physical-line target. R-017 remains +open because `CodeAdapter` is the known deferred exception; its next split should +follow the responsibilities described above. `CodeAdapter` and the updater +mill retain their documented entry points. Handler, context-builder, and conflict-detector helpers +are internal implementation surfaces and retain only production-used entry points. The +concretization oracle compares Java-expressible completion deltas from the +original `*Conc.cd` to `*Out.cd` for materialized adapter types and compiles +actual adapter output rather than generating a synthetic substitute. Golden +models that depend on completion behavior absent from the current upstream +dependency, or that cannot describe valid concrete Java, are registered with a +fixture-specific reason; those cases still pass the strict compilation oracle. + +## Data Ownership + +### CD ASTs + +Reference and concrete CD ASTs are owned by the adaptation run. In manual mode, +both ASTs are read-only after parsing. In concretization mode, the concrete CD +is completed before indexing and adaptation. + +### Incarnation Contexts + +An `IncarnationContext` belongs to one mapping. Stable reference keys map to +immutable lists of mapped concrete elements. Each mapped element retains its +stable key and MontiCore symbol, but symbols never participate in identity or +fallback lookup. An adaptation pass optionally selects mapped elements with another immutable +stable-key map. A single immutable implementer-to-grouping index supports common-parent handling. + +### Java ASTs + +`CodeAdapter` parses reference Java into JavaDSL ASTs. Each isolated mapping +pass works on a deep clone of those ASTs. The `CodeUpdater` implementation owns +the source model it rewrites and prints. + +### Output Files + +Temporary mapping and CD4Code-generation directories are separate from the +final isolated staging workspace. Existing output remains untouched until all +adaptation, generation, merging, and cleanup phases succeed and publication is +performed transactionally. The deliberate exception is the diagnostic +concretized-CD write: when `persistConcretizedCD` is enabled, the current model +is written directly to the output directory before a failure is rethrown. When +the flag is disabled, no CD is written on either success or failure. + +## Error Handling + +Conflict detection errors are aggregated into `CodeAdaptationException` so users +receive all manual-mode problems at once. + +Conformance and concretization failures are logged and converted into fallback +paths where deterministic manual mapping information exists. Fallback behavior +does not hide ambiguity; unsafe cases are still rejected by conflict detection. + +Updater errors indicate source-rewriting failures and are allowed to fail the +current adaptation pass. If concretized-CD persistence is enabled, the last +working model remains in the output directory to help explain these failures. + +## Testing Architecture + +The test suite uses several verification levels: + +- adaptation tests assert generated files and compile generated Java +- cdconcretization-derived tests compare materialized generated Java structure + against the expected `*Conc.cd` to `*Out.cd` completion delta, with every + non-applicable golden model registered with an explicit reason +- conflict detector tests assert deterministic diagnostics for unsafe manual + cases +- updater behavior is covered through end-to-end adaptation scenarios + +The concretization compilation oracle treats the original concrete CD as the boundary for +external generated dependencies. It prefers real adapted and handwritten Java sources and creates +a minimal stub only for an original concrete-CD type in one unambiguous concrete package. It never +completes the stub set from the reference CD. This makes stale reference types, wrong packages, and +missing adapter-generated types visible as compilation failures. + +Structural verification is preferred over exact source text comparison. Source +formatting remains the responsibility of Spoon and final post-processing. diff --git a/ref-code-adaptation/evaluation.md b/ref-code-adaptation/evaluation.md new file mode 100644 index 00000000..c3450d4d --- /dev/null +++ b/ref-code-adaptation/evaluation.md @@ -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. diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/AdaptationWorkspace.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/AdaptationWorkspace.java new file mode 100644 index 00000000..1de5d7ac --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/AdaptationWorkspace.java @@ -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 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 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 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 concreteSource, Path output) { + Path normalizedReference = normalizedPath(referenceSource); + Optional 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); + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/AdaptedCodeMerger.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/AdaptedCodeMerger.java new file mode 100644 index 00000000..56d119b1 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/AdaptedCodeMerger.java @@ -0,0 +1,679 @@ +package de.monticore.codeAdaption; + +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.AdapterUtils; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.codeAdaption.validator.CodeValidator; +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTCompilationUnit; +import de.monticore.java.javadsl._ast.ASTImportDeclaration; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.java.javadsl._visitor.JavaDSLTraverser; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** Merges, filters, splits, and deduplicates Java AST units produced by mapping runs. */ +final class AdaptedCodeMerger { + + /** + * Caches platform-class lookups used while repairing imports. A cached {@code true} means the + * fully qualified name resolves, without class initialization, to a public top-level JDK type; + * such a type is already available through {@code java.lang} or a matching wildcard import. + */ + private static final Map PLATFORM_PUBLIC_TOP_LEVEL_TYPE_CACHE = + new ConcurrentHashMap<>(); + + /** + * Merges newly adapted units into output accumulated from earlier mapping passes. + * + *

Units are matched by package-qualified top-level identity, not by source-file path. Thus two + * passes producing {@code shipping.Port} contribute members to one unit, while {@code api.Port} + * remains separate. + */ + Set mergeAdaptedCode( + Set actualCode, + Set newAdaptedCode) { + Map merged = indexAndMerge(actualCode); + for (ASTOrdinaryCompilationUnit newAdapted : ordered(newAdaptedCode)) { + String key = compilationUnitKey(newAdapted); + merged.compute( + key, (k, existing) -> existing == null ? newAdapted : AdapterUtils.mergeAsts(existing, newAdapted)); + } + return new LinkedHashSet<>(merged.values()); + } + + /** + * Relocates adapted declarations to the package of a unique same-name concrete declaration + * without merging their members. + * + *

TOP composition needs the same package and import decisions as the legacy merger, but it + * deliberately keeps the two declarations separate. Relocation happens before the adapted + * declaration receives its {@code TOP} suffix so references continue to target the public + * concrete type. + */ + Set relocateAdaptedCodeToConcretePackages( + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + Map concreteByType = indexAndMerge(concreteCode); + Map adaptedByType = indexAndMerge(adaptedCode); + Map relocations = + findRelocations(concreteByType, adaptedByType, concreteIndex); + Map> importsByOriginalPackage = + adaptedTypeImports(adaptedByType, relocations); + Map relocated = new LinkedHashMap<>(); + Map> repairableMemberTypesByUnit = new LinkedHashMap<>(); + + for (ASTOrdinaryCompilationUnit original : adaptedByType.values()) { + String originalKey = compilationUnitKey(original); + ASTOrdinaryCompilationUnit copy = original.deepClone(); + Relocation relocation = relocations.get(originalKey); + String targetPackage = relocation == null ? packageName(copy) : relocation.targetPackage(); + addRelocationImports( + copy, + importsByOriginalPackage.getOrDefault(packageName(original), Map.of()), + targetPackage); + if (relocation != null) { + ASTOrdinaryCompilationUnit concreteTarget = concreteByType.get(relocation.targetKey()); + if (concreteTarget.isPresentPackageDeclaration()) { + copy.setPackageDeclaration(concreteTarget.getPackageDeclaration().deepClone()); + } else { + copy.setPackageDeclarationAbsent(); + } + } + + String targetKey = compilationUnitKey(copy); + ASTOrdinaryCompilationUnit conflict = relocated.putIfAbsent(targetKey, copy); + if (conflict != null) { + throw new CodeAdaptationException( + "Several adapted declarations relocate to '" + targetKey + "'"); + } + repairableMemberTypesByUnit.put(targetKey, unqualifiedMemberTypeNames(copy)); + } + + repairMemberTypeImports(relocated, repairableMemberTypesByUnit); + return new LinkedHashSet<>(relocated.values()); + } + + /** + * Retains compilation units relevant to one mapping-specific incarnation context. + * + *

A unit is retained when a top-level Java type matches a reference-CD type having at least + * one incarnation in {@code context}. A unit explicitly marked {@code @Adapt(ignore = true)} is + * also retained: ignore prevents transformation of that type, but does not mean that its source + * file should disappear from the mapping output. + */ + Set filterCodeForMapping( + Set javaFiles, + CodeValidator validator, + IncarnationContext context, + CDModelIndex referenceIndex, + Set selectedTypeIdentities) { + validator.initializeTypeMatcher(javaFiles); + Set result = new LinkedHashSet<>(); + for (ASTOrdinaryCompilationUnit unit : javaFiles) { + JavaAstElemCollector collector = collect(unit); + + boolean mappedTopLevel = + collector.getAllTypeDeclarations().stream() + .map(validator::getMatchedType) + .filter(Optional::isPresent) + .map(Optional::get) + .flatMap(matching -> matching.getReferences().stream()) + .anyMatch( + reference -> StableElementKey.fromSymbol(reference, referenceIndex) + .map(context::getIncarnations) + .filter(incarnations -> !incarnations.isEmpty()) + .isPresent()); + boolean ignoredTopLevel = + collector.getAllTypeDeclarations().stream() + .map(validator::getMatchedType) + .filter(Optional::isPresent) + .map(Optional::get) + .anyMatch( + matching -> + !matching.mustBePerform() && matching.isExplicitAnnotation()); + boolean selectedDependency = + unit.getTypeDeclarationList().stream() + .map(type -> qualifiedTypeIdentity(unit, type)) + .anyMatch(selectedTypeIdentities::contains); + if (mappedTopLevel || ignoredTopLevel || selectedDependency) { + result.add(unit); + } + } + return result; + } + + private String qualifiedTypeIdentity( + ASTOrdinaryCompilationUnit unit, ASTTypeDeclaration type) { + String typePackage = packageName(unit); + return typePackage.isBlank() ? type.getName() : typePackage + "." + type.getName(); + } + + /** + * Builds the final generated code on top of concrete handwritten classes. Adapted and concrete + * units with the same package-qualified top-level identity are merged. + * + *

For example, an adapted {@code adapter.Port} containing {@code void send()} can be relocated + * to a uniquely matching handwritten {@code concrete.Port}; the result remains in package {@code + * concrete} and contains {@code send()}. + */ + Set mergeAdaptedCodeIntoConcreteBase( + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + + Map result = indexAndMerge(concreteCode); + Map mergedAdapted = indexAndMerge(adaptedCode); + Map relocations = findRelocations(result, mergedAdapted, concreteIndex); + Map> importsByOriginalPackage = + adaptedTypeImports(mergedAdapted, relocations); + Map> repairableMemberTypesByUnit = new LinkedHashMap<>(); + + // Every unit in adaptedCode has already passed filterCodeForMapping. Retaining that explicit + // selection avoids guessing from coincidental type-name prefixes or suffixes. + for (ASTOrdinaryCompilationUnit originalAdapted : mergedAdapted.values()) { + String originalKey = compilationUnitKey(originalAdapted); + ASTOrdinaryCompilationUnit adaptedUnit = originalAdapted.deepClone(); + Relocation relocation = relocations.get(originalKey); + addRelocationImports( + adaptedUnit, + importsByOriginalPackage.getOrDefault(packageName(originalAdapted), Map.of()), + relocation == null ? packageName(adaptedUnit) : relocation.targetPackage()); + + String key = compilationUnitKey(adaptedUnit); + ASTOrdinaryCompilationUnit concreteMatch = result.get(key); + ASTOrdinaryCompilationUnit mergeableAdapted = adaptedUnit; + if (concreteMatch == null && relocation != null) { + key = relocation.targetKey(); + concreteMatch = result.get(key); + if (concreteMatch.isPresentPackageDeclaration()) { + mergeableAdapted.setPackageDeclaration( + concreteMatch.getPackageDeclaration().deepClone()); + } else { + mergeableAdapted.setPackageDeclarationAbsent(); + } + } + Set repairableMemberTypes = unqualifiedMemberTypeNames(mergeableAdapted); + if (concreteMatch != null) { + // Imports affect every occurrence of a simple name in the compilation unit. If concrete + // handwritten code already used that name, changing its binding would be unsafe even when + // an adapted member also uses it. + repairableMemberTypes.removeAll(unqualifiedMemberTypeNames(concreteMatch)); + } + repairableMemberTypesByUnit + .computeIfAbsent(key, ignored -> new LinkedHashSet<>()) + .addAll(repairableMemberTypes); + result.put( + key, + concreteMatch == null + ? mergeableAdapted + : AdapterUtils.mergeAstsPreferringLeft(concreteMatch, mergeableAdapted)); + } + + repairMemberTypeImports(result, repairableMemberTypesByUnit); + + return new LinkedHashSet<>(result.values()); + } + + /** + * Imports final concrete declaration locations used by generated fields, methods and + * supertypes. Completion can introduce member types after the initial reference-CD import + * projection, and relocation can place those types in packages different from their adapted + * owner. + */ + private void repairMemberTypeImports( + Map units, + Map> repairableMemberTypesByUnit) { + Map> declarationsBySimpleName = new LinkedHashMap<>(); + for (ASTOrdinaryCompilationUnit unit : units.values()) { + String declarationPackage = packageName(unit); + for (ASTTypeDeclaration type : unit.getTypeDeclarationList()) { + declarationsBySimpleName + .computeIfAbsent(type.getName(), ignored -> new LinkedHashSet<>()) + .add(qualify(declarationPackage, type.getName())); + } + } + + for (Map.Entry unitEntry : units.entrySet()) { + ASTOrdinaryCompilationUnit unit = unitEntry.getValue(); + Set repairableMemberTypes = + repairableMemberTypesByUnit.getOrDefault(unitEntry.getKey(), Set.of()); + if (repairableMemberTypes.isEmpty()) { + continue; + } + Set referencedTypes = memberTypeNames(unit); + Set declaredSimpleNames = + unit.getTypeDeclarationList().stream() + .map(ASTTypeDeclaration::getName) + .collect(java.util.stream.Collectors.toSet()); + Set existingImports = + unit.getImportDeclarationList().stream() + .filter( + importDeclaration -> + !importDeclaration.isStatic() && !importDeclaration.isSTAR()) + .map(importDeclaration -> importDeclaration.getMCQualifiedName().getQName()) + .filter(qualifiedName -> qualifiedName.contains(".")) + .collect(java.util.stream.Collectors.toSet()); + Map explicitImportsBySimpleName = new LinkedHashMap<>(); + for (String existingImport : existingImports) { + explicitImportsBySimpleName.put(simpleName(existingImport), existingImport); + } + Set wildcardImportPackages = + unit.getImportDeclarationList().stream() + .filter( + importDeclaration -> + !importDeclaration.isStatic() && importDeclaration.isSTAR()) + .map(importDeclaration -> importDeclaration.getMCQualifiedName().getQName()) + .collect(java.util.stream.Collectors.toSet()); + Map requiredImports = new LinkedHashMap<>(); + for (JavaSourceNames.TypeReferenceName reference : referencedTypes) { + if (reference.qualified()) { + continue; + } + String simpleName = reference.simpleName(); + if (!repairableMemberTypes.contains(simpleName)) { + continue; + } + Set candidates = declarationsBySimpleName.getOrDefault(simpleName, Set.of()); + if (declaredSimpleNames.contains(simpleName) || candidates.isEmpty()) { + continue; + } + String explicitImport = explicitImportsBySimpleName.get(simpleName); + if (explicitImport != null) { + // A single-type import is an established binding. It may belong to handwritten code + // that shares a compilation unit with generated members, so never reinterpret it from + // coincidental declarations in the final output. + continue; + } + boolean samePackageCandidate = + candidates.stream() + .anyMatch(candidate -> packageName(candidate).equals(packageName(unit))); + if (samePackageCandidate) { + continue; + } + if (isImplicitJavaLangType(simpleName)) { + // The implicit java.lang binding has the same precedence as a pre-existing explicit + // import for this purpose. Generated code must use a qualified name if it means another + // type with the same simple name. + continue; + } + List matchingWildcardPackages = + wildcardImportPackages.stream() + .filter( + importedPackage -> + isAvailableThroughWildcardImport( + importedPackage, simpleName, candidates)) + .toList(); + if (matchingWildcardPackages.size() == 1) { + // Unrelated declarations do not make the wildcard binding ambiguous: only declarations + // from imported packages participate in that binding. + continue; + } + if (matchingWildcardPackages.size() > 1) { + throw new CodeAdaptationException( + "Cannot repair generated member type '" + + simpleName + + "' because multiple wildcard imports provide final declarations: " + + matchingWildcardPackages); + } + if (candidates.size() == 1) { + // A single-type import takes precedence over unrelated on-demand imports. + requiredImports.put(simpleName, candidates.iterator().next()); + } else if (candidates.size() > 1) { + throw new CodeAdaptationException( + "Cannot import generated member type '" + + simpleName + + "' because final output contains multiple declarations: " + + candidates); + } + } + addRelocationImports(unit, requiredImports, packageName(unit)); + } + } + + /** Returns whether the running Java platform implicitly provides the simple name. */ + private boolean isImplicitJavaLangType(String simpleName) { + if (simpleName == null || simpleName.isBlank() || simpleName.contains(".")) { + return false; + } + return isLoadablePublicTopLevelType("java.lang." + simpleName); + } + + /** + * A wildcard import covers either a type produced in the final output or a public top-level JDK + * type. Checking both avoids adding redundant explicit imports after relocation. + */ + private boolean isAvailableThroughWildcardImport( + String importedPackage, String simpleName, Set outputCandidates) { + String qualifiedName = qualify(importedPackage, simpleName); + return outputCandidates.contains(qualifiedName) + || isLoadablePublicTopLevelType(qualifiedName); + } + + private boolean isLoadablePublicTopLevelType(String qualifiedName) { + return PLATFORM_PUBLIC_TOP_LEVEL_TYPE_CACHE.computeIfAbsent( + qualifiedName, AdaptedCodeMerger::loadPublicTopLevelType); + } + + /** + * Probes only the JDK platform class loader and does not initialize the class. Nested and + * non-public classes are deliberately rejected because a Java wildcard import cannot expose + * them as top-level source types. + */ + private static boolean loadPublicTopLevelType(String qualifiedName) { + try { + Class type = + Class.forName(qualifiedName, false, ClassLoader.getPlatformClassLoader()); + return type.getEnclosingClass() == null + && java.lang.reflect.Modifier.isPublic(type.getModifiers()); + } catch (ClassNotFoundException | LinkageError ignored) { + return false; + } + } + + /** Collects unqualified signature names that originated in an adapted compilation unit. */ + private Set unqualifiedMemberTypeNames(ASTOrdinaryCompilationUnit unit) { + return memberTypeNames(unit).stream() + .filter(reference -> !reference.qualified()) + .map(JavaSourceNames.TypeReferenceName::simpleName) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + } + + /** Collects type names appearing in declaration signatures that may require final imports. */ + private Set memberTypeNames( + ASTOrdinaryCompilationUnit unit) { + Set names = new LinkedHashSet<>(); + JavaAstElemCollector collector = collect(unit); + for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { + collector.getAllFSuperTypeDeclarations(type).forEach(value -> collectTypeNames(value, names)); + collector + .getAllFieldDeclarations(type) + .forEach(field -> collectTypeNames(field.getMCType(), names)); + collector + .getAllMethodDeclarations(type) + .forEach( + method -> { + collectTypeNames(method.getMCReturnType(), names); + collector + .getAllParameters(type, method) + .forEach(parameter -> collectTypeNames(parameter.getMCType(), names)); + }); + } + return names; + } + + /** Retains qualified identity while traversing arrays, generics, and wildcard bounds. */ + private void collectTypeNames( + de.monticore.ast.ASTNode type, Set names) { + names.addAll(JavaSourceNames.typeReferences(JavaLoader.print(type))); + } + + /** + * Finds adapted units that must move to an existing concrete handwritten package before merge. + * + *

The result maps the adapted unit's original package-qualified identity to its concrete + * target identity and package. Relocation requires one unique concrete Java declaration with the + * same concrete-CD type name; zero matches leave the unit in place and multiple matches fail. + */ + private Map findRelocations( + Map concreteCode, + Map adaptedCode, + CDModelIndex concreteIndex) { + Map relocations = new LinkedHashMap<>(); + for (Map.Entry entry : adaptedCode.entrySet()) { + if (concreteCode.containsKey(entry.getKey())) { + continue; + } + Optional> target = + uniqueConcreteTypeMatch(concreteCode, entry.getValue(), concreteIndex); + if (target.isEmpty()) { + continue; + } + ASTOrdinaryCompilationUnit adaptedUnit = entry.getValue(); + ASTOrdinaryCompilationUnit concreteUnit = target.get().getValue(); + String simpleName = adaptedUnit.getTypeDeclarationList().get(0).getName(); + relocations.put( + entry.getKey(), + new Relocation( + simpleName, + target.get().getKey(), + packageName(concreteUnit))); + } + return relocations; + } + + /** + * Indexes final adapted type locations for cross-package import repair. + * + *

The outer key is the type's original package. Each inner map is {@code simple type name -> + * final qualified name}. For example, relocating {@code adapter.Port} to {@code concrete.Port} + * records {@code adapter -> {Port=concrete.Port}}, allowing other units originally in {@code + * adapter} to import the relocated type. + */ + private Map> adaptedTypeImports( + Map adaptedCode, + Map relocations) { + Map> result = new LinkedHashMap<>(); + for (Map.Entry entry : adaptedCode.entrySet()) { + ASTOrdinaryCompilationUnit unit = entry.getValue(); + if (unit.getTypeDeclarationList().size() != 1) { + continue; + } + String originalPackage = packageName(unit); + String typeName = unit.getTypeDeclarationList().get(0).getName(); + Relocation relocation = relocations.get(entry.getKey()); + String qualifiedTarget = + relocation == null + ? qualify(originalPackage, typeName) + : qualify(relocation.targetPackage(), typeName); + Map bySimpleName = + result.computeIfAbsent(originalPackage, ignored -> new LinkedHashMap<>()); + String conflict = bySimpleName.putIfAbsent(typeName, qualifiedTarget); + if (conflict != null && !conflict.equals(qualifiedTarget)) { + throw new CodeAdaptationException( + "Ambiguous concrete package for adapted type '" + + typeName + + "': " + + conflict + + " and " + + qualifiedTarget); + } + } + return result; + } + + private void addRelocationImports( + ASTOrdinaryCompilationUnit unit, + Map relocatedTypes, + String finalPackage) { + Set declaredTypes = + unit.getTypeDeclarationList().stream() + .map(ASTTypeDeclaration::getName) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + Map importsBySimpleName = new LinkedHashMap<>(); + for (ASTImportDeclaration existingImport : + new ArrayList<>(unit.getImportDeclarationList())) { + String qualifiedName = existingImport.getMCQualifiedName().getQName(); + String importedSimpleName = simpleName(qualifiedName); + if (!qualifiedName.contains(".") && relocatedTypes.containsKey(importedSimpleName)) { + unit.removeImportDeclaration(existingImport); + } else { + importsBySimpleName.put(importedSimpleName, qualifiedName); + } + } + for (Map.Entry relocatedType : relocatedTypes.entrySet()) { + String simpleName = relocatedType.getKey(); + String qualifiedName = relocatedType.getValue(); + if (declaredTypes.contains(simpleName) || packageName(qualifiedName).equals(finalPackage)) { + continue; + } + String conflict = importsBySimpleName.putIfAbsent(simpleName, qualifiedName); + if (conflict != null && !conflict.equals(qualifiedName)) { + throw new CodeAdaptationException( + "Cannot import relocated type '" + + qualifiedName + + "' because '" + + conflict + + "' already uses the simple name '" + + simpleName + + "'"); + } + if (conflict == null) { + unit.addImportDeclaration(parseImport(qualifiedName)); + } + } + } + + private ASTImportDeclaration parseImport(String qualifiedName) { + try { + Optional parsed = + JavaDSLMill.parser() + .parse_StringCompilationUnit( + "import " + qualifiedName + ";" + System.lineSeparator() + "class Holder {}"); + if (parsed.isEmpty() || !(parsed.get() instanceof ASTOrdinaryCompilationUnit unit)) { + throw new CodeAdaptationException("Could not create Java import for " + qualifiedName); + } + return unit.getImportDeclaration(0).deepClone(); + } catch (IOException exception) { + throw new CodeAdaptationException("Could not create Java import for " + qualifiedName, exception); + } + } + + private Optional> uniqueConcreteTypeMatch( + Map concreteCode, + ASTOrdinaryCompilationUnit adaptedUnit, + CDModelIndex concreteIndex) { + if (adaptedUnit.getTypeDeclarationList().size() != 1) { + return Optional.empty(); + } + String adaptedType = adaptedUnit.getTypeDeclarationList().get(0).getName(); + boolean isConcreteType = concreteIndex.hasType(adaptedType); + if (!isConcreteType) { + return Optional.empty(); + } + List> candidates = + concreteCode.entrySet().stream() + .filter( + entry -> + entry.getValue().getTypeDeclarationList().stream() + .anyMatch(type -> adaptedType.equals(type.getName()))) + .toList(); + if (candidates.size() > 1) { + throw new CodeAdaptationException( + "Cannot relocate adapted type '" + + adaptedType + + "' because concrete Java declares it in multiple packages: " + + candidates.stream().map(Map.Entry::getKey).sorted().toList()); + } + return candidates.size() == 1 ? Optional.of(candidates.get(0)) : Optional.empty(); + } + + Set splitCompilationUnitsByType( + Set units) { + Set result = new LinkedHashSet<>(); + for (ASTOrdinaryCompilationUnit unit : ordered(units)) { + List types = new ArrayList<>(unit.getTypeDeclarationList()); + if (types.size() <= 1) { + result.add(unit); + continue; + } + + for (ASTTypeDeclaration type : types) { + ASTOrdinaryCompilationUnit copy = unit.deepClone(); + for (ASTTypeDeclaration copiedType : new ArrayList<>(copy.getTypeDeclarationList())) { + if (!copiedType.getName().equals(type.getName())) { + copy.removeTypeDeclaration(copiedType); + } + } + + String fileName = type.getName() + ".java"; + copy.get_SourcePositionStart().setFileName(fileName); + result.add(copy); + } + } + return result; + } + + /** + * Indexes units by package-qualified top-level identity and merges duplicate identities. + * Source paths do not participate in the key. + */ + private Map indexAndMerge( + Set files) { + Map byQualifiedType = new LinkedHashMap<>(); + for (ASTOrdinaryCompilationUnit file : ordered(files)) { + String key = compilationUnitKey(file); + ASTOrdinaryCompilationUnit existing = byQualifiedType.get(key); + if (existing == null) { + byQualifiedType.put(key, file); + } else { + byQualifiedType.put(key, AdapterUtils.mergeAsts(existing, file)); + } + } + return byQualifiedType; + } + + private List ordered(Set files) { + return files.stream() + .sorted( + Comparator.comparing(this::compilationUnitKey) + .thenComparing(AdapterUtils::getFileName)) + .toList(); + } + + /** Returns the package-qualified identity of a split compilation unit. */ + private String compilationUnitKey(ASTOrdinaryCompilationUnit unit) { + String packageName = packageName(unit); + String typeIdentity = + unit.getTypeDeclarationList().stream() + .map(ASTTypeDeclaration::getName) + .sorted() + .reduce((left, right) -> left + "+" + right) + .orElseGet(() -> AdapterUtils.getSimpleFileName(unit)); + return packageName.isEmpty() ? typeIdentity : packageName + "." + typeIdentity; + } + + private String packageName(ASTOrdinaryCompilationUnit unit) { + return unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + } + + private String packageName(String qualifiedName) { + int separator = qualifiedName.lastIndexOf('.'); + return separator < 0 ? "" : qualifiedName.substring(0, separator); + } + + private String simpleName(String qualifiedName) { + int separator = qualifiedName.lastIndexOf('.'); + return separator < 0 ? qualifiedName : qualifiedName.substring(separator + 1); + } + + private String qualify(String packageName, String simpleName) { + return packageName.isEmpty() ? simpleName : packageName + "." + simpleName; + } + + private JavaAstElemCollector collect(ASTOrdinaryCompilationUnit unit) { + JavaAstElemCollector collector = new JavaAstElemCollector(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(collector); + unit.accept(traverser); + return collector; + } + + private record Relocation(String simpleName, String targetKey, String targetPackage) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdaptationException.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdaptationException.java new file mode 100644 index 00000000..ef13ae28 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdaptationException.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption; + +/** + * Runtime exception for adaptation setup problems that would lead to invalid generated Java. + */ +public class CodeAdaptationException extends RuntimeException { + public CodeAdaptationException(String message) { + super(message); + } + + public CodeAdaptationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapter.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapter.java index 21abdd5f..d9c94414 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapter.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapter.java @@ -1,43 +1,87 @@ package de.monticore.codeAdaption; -import static de.monticore.codeAdaption.utils.AdapterUtils.getFileName; - import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cd4code.CD4CodeMill; import de.monticore.cdconformance.CDConfParameter; import de.monticore.cdconformance.CDConformanceChecker; -import de.monticore.codeAdaption.handler.BasicUpdateHandler; -import de.monticore.codeAdaption.updater.CodeUpdater; -import de.monticore.codeAdaption.updater.spoonUpdater.SpoonUpdater; +import de.monticore.codeAdaption.context.AdaptationContextFactory; +import de.monticore.codeAdaption.context.AdaptationContextFactory.AdaptationContextResult; +import de.monticore.codeAdaption.context.ConcretizationService; +import de.monticore.codeAdaption.context.GroupingMappingService; +import de.monticore.codeAdaption.context.MappingConformanceService; +import de.monticore.codeAdaption.dependency.ReferenceCodeDependencySelector; +import de.monticore.codeAdaption.dependency.ReferenceCodeSelection; +import de.monticore.codeAdaption.MappingAdaptationRunner.AdaptationPass; +import de.monticore.codeAdaption.handler.multiIncarnation.AdaptationConflictDetector; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.matcher.CodeMatching; +import de.monticore.codeAdaption.matcher.MatcherHelper; import de.monticore.codeAdaption.utils.AdapterParam; -import de.monticore.codeAdaption.utils.AdapterUtils; +import de.monticore.codeAdaption.utils.CDImportProjector; +import de.monticore.codeAdaption.utils.CDModelIndex; import de.monticore.codeAdaption.utils.JavaLoader; -import de.monticore.codeAdaption.utils.visitors.AnnotationRemover; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; import de.monticore.codeAdaption.validator.CodeValidator; import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; -import de.se_rwth.commons.logging.Log; +import de.monticore.symboltable.ISymbol; import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import static java.util.stream.Collectors.toCollection; + +/** + * Adapts handwritten reference Java to a concrete class diagram through + * mapping-specific transformation passes. + * + *

A failed run never publishes its staging directory over existing output. In concretization + * mode, only the completed or partially completed concrete CD is retained there as a diagnostic. + */ public class CodeAdapter { - protected Set adapterParams; - protected Set confParams; + /** Maximum number of Cartesian multi-incarnation selections accepted for one source unit. */ + private static final int MAX_TYPE_SELECTIONS = 1024; + + private final Set adapterParams; + private final Set confParams; + + @FunctionalInterface + private interface FinalCodeComposer { + FinalCodeComposition compose( + AdaptedCodeMerger merger, + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex); + } + + private record FinalCodeComposition( + Set code, Map topToPublicSelfTypes) {} + /** + * Creates an adapter with explicit Java matching and CD-conformance policies. + * + * @param adapterParams enabled Java matching strategies and unmatched-element policies + * @param confParams enabled CD conformance parameters + */ public CodeAdapter(Set adapterParams, Set confParams) { - this.confParams = confParams; - this.adapterParams = adapterParams; + this.adapterParams = Set.copyOf(Objects.requireNonNull(adapterParams, "adapterParams")); + this.confParams = Set.copyOf(Objects.requireNonNull(confParams, "confParams")); } - /*** + /** + * Adapts reference Java code to a concrete class diagram without completing the concrete model + * first. * - * @param referenceCD the reference class diagram. - * @param concreteCD, the concrete class diagram. - * @param refHwcPath the handwritten reference-code. - * @param conHwcPath path to the concrete code, - * @param mappings different maping names. - * @param outputPath the output path + * @param referenceCD reference class diagram + * @param concreteCD concrete class diagram + * @param mappings mapping stereotype names to apply + * @param refHwcPath reference handwritten-code directory + * @param conHwcPath concrete handwritten-code directory + * @param outputPath output directory + * @throws CodeAdaptationException if validation or deterministic adaptation fails */ public void adapt( File referenceCD, @@ -46,84 +90,960 @@ public void adapt( Path refHwcPath, Path conHwcPath, Path outputPath) { + adapt(referenceCD, concreteCD, mappings, refHwcPath, conHwcPath, outputPath, false, true); + } + + /** + * Adapts reference Java without a concrete handwritten-code source tree. + * + * @param referenceCD reference class diagram + * @param concreteCD concrete class diagram + * @param mappings mapping stereotype names to apply + * @param refHwcPath reference handwritten-code directory + * @param outputPath output directory + */ + public void adaptWithoutConcreteCode( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path outputPath) { + adaptWithoutConcreteCode( + referenceCD, concreteCD, mappings, refHwcPath, outputPath, false, true); + } + + /** + * Adapts reference Java without concrete HWC. + * + *

This overload is the no-concrete-input form of TOP separation. Because no concrete HWC + * declarations exist, every adapted type is emitted directly under its adapted concrete name. + */ + public void adaptWithTopSeparation( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path outputPath) { + adaptWithTopSeparation( + referenceCD, concreteCD, mappings, refHwcPath, outputPath, false, true); + } + + /** + * Keeps adapted implementations separate from matching concrete HWC through TOP inheritance. + */ + public void adaptWithTopSeparation( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path conHwcPath, + Path outputPath) { + adaptWithTopSeparation( + referenceCD, + concreteCD, + mappings, + refHwcPath, + conHwcPath, + outputPath, + false, + true); + } + + /** + * Adapts reference Java code to a concrete class diagram. + * + *

With {@code useConcretization=true}, cdconcretization may complete the concrete CD before + * adaptation. With {@code useConcretization=false}, incarnation contexts are derived manually from + * stereotypes and deterministic name rules, conflicts are reported before the output directory is + * cleaned, and the concrete CD is not changed. + * + *

{@code useCommonParentForMultipleIncarnations} permits manual adaptation to represent an + * exact group of concrete incarnations through a common concrete interface. For example, if + * {@code Payment} maps to {@code CreditCard} and {@code Invoice}, and those are exactly the + * concrete implementers of {@code PaymentMethod}, shared Java declarations may use {@code + * PaymentMethod}. A broader interface that also has unrelated implementers is not selected. + * + * @param referenceCD reference class diagram + * @param concreteCD concrete class diagram + * @param mappings mapping stereotype names to apply + * @param refHwcPath reference handwritten-code directory + * @param conHwcPath concrete handwritten-code directory + * @param outputPath output directory published only after a successful run + * @param useConcretization whether to complete the concrete CD before adaptation + * @param useCommonParentForMultipleIncarnations whether a common concrete interface whose + * implementers exactly match an incarnation group may replace the individual incarnation + * types in shared Java declarations + * @throws CodeAdaptationException if validation, conflict detection, or adaptation fails + */ + public void adapt( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path conHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations) { + adapt( + referenceCD, + concreteCD, + mappings, + refHwcPath, + conHwcPath, + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + true); + } + + /** + * Full adaptation API with explicit control over persistence of the concretized class diagram. + * + * @param persistConcretizedCD whether to write the working concrete CD to the output directory; + * ignored when {@code useConcretization} is false + */ + public void adapt( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path conHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations, + boolean persistConcretizedCD) { + adaptInternal( + referenceCD, + concreteCD, + mappings, + refHwcPath, + Optional.of(Objects.requireNonNull(conHwcPath, "conHwcPath")), + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + persistConcretizedCD, + CodeAdapter::mergeFinalCode); + } + + /** + * Adapts reference Java without a concrete handwritten-code input. + */ + public void adaptWithoutConcreteCode( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations) { + adaptWithoutConcreteCode( + referenceCD, + concreteCD, + mappings, + refHwcPath, + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + true); + } + + /** No-concrete-code API with explicit control over concretized-CD persistence. */ + public void adaptWithoutConcreteCode( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations, + boolean persistConcretizedCD) { + adaptInternal( + referenceCD, + concreteCD, + mappings, + refHwcPath, + Optional.empty(), + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + persistConcretizedCD, + CodeAdapter::directFinalCode); + } + + /** Full TOP-separation API without a concrete handwritten-code source tree. */ + public void adaptWithTopSeparation( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations) { + adaptWithTopSeparation( + referenceCD, + concreteCD, + mappings, + refHwcPath, + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + true); + } + + /** + * Full TOP-separation API without concrete HWC and with explicit concretized-CD persistence. + */ + public void adaptWithTopSeparation( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations, + boolean persistConcretizedCD) { + adaptInternal( + referenceCD, + concreteCD, + mappings, + refHwcPath, + Optional.empty(), + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + persistConcretizedCD, + CodeAdapter::composeWithTopSeparation); + } + + /** Full TOP-separation API with an optional-per-type concrete HWC source tree. */ + public void adaptWithTopSeparation( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path conHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations) { + adaptWithTopSeparation( + referenceCD, + concreteCD, + mappings, + refHwcPath, + conHwcPath, + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + true); + } + + /** Full TOP-separation API with explicit control over concretized-CD persistence. */ + public void adaptWithTopSeparation( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Path conHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations, + boolean persistConcretizedCD) { + adaptInternal( + referenceCD, + concreteCD, + mappings, + refHwcPath, + Optional.of(Objects.requireNonNull(conHwcPath, "conHwcPath")), + outputPath, + useConcretization, + useCommonParentForMultipleIncarnations, + persistConcretizedCD, + CodeAdapter::composeWithTopSeparation); + } + + private void adaptInternal( + File referenceCD, + File concreteCD, + Set mappings, + Path refHwcPath, + Optional conHwcPath, + Path outputPath, + boolean useConcretization, + boolean useCommonParentForMultipleIncarnations, + boolean persistConcretizedCD, + FinalCodeComposer finalCodeComposer) { + AdaptationWorkspace workspace = + new AdaptationWorkspace(refHwcPath, conHwcPath, outputPath); + workspace.validateReadOnlyInput(referenceCD.toPath(), "reference class diagram"); + workspace.validateReadOnlyInput(concreteCD.toPath(), "concrete class diagram"); + Path normalizedRefHwcPath = workspace.referenceSource(); + Optional normalizedConHwcPath = workspace.concreteSourceOptional(); + SortedSet validatedMappingsInOrder = validatedMappings(mappings); // load CD models - ASTCDCompilationUnit refCD = JavaLoader.loadCD(referenceCD); - ASTCDCompilationUnit conCD = JavaLoader.loadCD(concreteCD); + ASTCDCompilationUnit refCD = JavaLoader.parseCD(referenceCD.getPath()); + ASTCDCompilationUnit conCD = JavaLoader.parseCD(concreteCD.getPath()); + CDModelIndex inputConcreteIndex = CDModelIndex.of(conCD); + CDModelIndex referenceIndex = CDModelIndex.of(refCD); + MappingConformanceService conformanceService = new MappingConformanceService(confParams); + Path concretizedCDFileName = concreteCD.toPath().getFileName(); - Set adaptedCode = new LinkedHashSet<>(); + if (useConcretization) { + ASTCDCompilationUnit completedCD = conCD.deepClone(); + try { + new ConcretizationService(confParams) + .completeConcreteCDInPlace(completedCD, refCD, validatedMappingsInOrder); + } catch (RuntimeException | Error failure) { + if (persistConcretizedCD) { + persistConcretizedCDAfterFailure( + completedCD, workspace.output().resolve(concretizedCDFileName), failure); + } + throw failure; + } + conCD = completedCD; + if (persistConcretizedCD) { + persistConcretizedCD(conCD, workspace.output().resolve(concretizedCDFileName)); + } + } + CDModelIndex conIndex = CDModelIndex.of(conCD); + AdaptedCodeMerger codeMerger = new AdaptedCodeMerger(); + OutputCodeService outputCode = new OutputCodeService(); - for (String mapping : mappings) { + // Build each context and checker once, then reuse that exact checker state in handlers. + Map contextResults = + new AdaptationContextFactory(confParams, conformanceService) + .buildResults(referenceIndex, conIndex, validatedMappingsInOrder, useConcretization); + Map incarnationContextsByMapping = new LinkedHashMap<>(); + Map checkers = new LinkedHashMap<>(); + Map validators = new LinkedHashMap<>(); + Set refCode = JavaLoader.readJavaCode(normalizedRefHwcPath); + ReferenceCodeDependencySelector dependencySelector = + useConcretization + ? new ReferenceCodeDependencySelector( + normalizedRefHwcPath, refCode, referenceIndex.typeNames()) + : null; + Map codeSelections = new LinkedHashMap<>(); + for (String mapping : validatedMappingsInOrder) { + AdaptationContextResult result = contextResults.get(mapping); + incarnationContextsByMapping.put(mapping, result.context()); + checkers.put(mapping, result.checker()); + CodeValidator validator = + useConcretization + ? new CodeValidator( + refCD, + adapterParams, + CodeValidator.ValidationPolicy.PRESERVE_CONCRETIZATION_HELPERS) + : new CodeValidator(refCD, adapterParams); + validators.put(mapping, validator); + if (useConcretization) { + Set mappedRoots = + mappedRootTypeIdentities( + refCode, validator, result.context(), referenceIndex); + codeSelections.put(mapping, dependencySelector.select(mappedRoots)); + } else { + codeSelections.put(mapping, emptyCodeSelection()); + } + } + + SortedSet fallbackMappings = + contextResults.entrySet().stream() + .filter(entry -> !entry.getValue().conformanceValid()) + .map(Map.Entry::getKey) + .collect( + toCollection(TreeSet::new)); + if (!useConcretization || !fallbackMappings.isEmpty()) { + Set mappingsToValidate = + useConcretization ? fallbackMappings : validatedMappingsInOrder; + Map contextsToValidate = new LinkedHashMap<>(); + mappingsToValidate.forEach( + mapping -> contextsToValidate.put(mapping, incarnationContextsByMapping.get(mapping))); + AdaptationConflictDetector.validate( + referenceIndex, + conIndex, + mappingsToValidate, + contextsToValidate, + confParams, + useCommonParentForMultipleIncarnations); + } + + // Validate before creating staging directories or changing existing output. + for (String mapping : validatedMappingsInOrder) { + CodeValidator validator = validators.get(mapping); + boolean valid = + validator != null + && (useConcretization + ? validator.isValid( + refCD, + refCode, + codeSelections.get(mapping).selectedTypeIdentities()) + : validator.isValid(refCD, refCode)); + if (!valid) { + throw new CodeAdaptationException( + "Reference code is not valid for the reference CD (mapping '" + mapping + "')"); + } + } + + CDImportProjector.project(refCode, refCD, conCD); + Path stagingPath = workspace.createStagingDirectory(); + boolean published = false; + try { + Set adaptedCode = new LinkedHashSet<>(); + MappingAdaptationRunner passRunner = + new MappingAdaptationRunner( + workspace, + codeMerger, + stagingPath, + referenceIndex, + conIndex, + inputConcreteIndex, + useCommonParentForMultipleIncarnations); + + for (String mapping : validatedMappingsInOrder) { + if (!incarnationContextsByMapping.containsKey(mapping)) { + continue; // Skip mappings that failed to build contexts + } - // check conformance - CDConformanceChecker checker = new CDConformanceChecker(confParams); - if (!checker.checkConformance(conCD, refCD, mapping)) { - Log.error("The concrete CD is not Conform to the Reference CD"); + IncarnationContext ctx = incarnationContextsByMapping.get(mapping); + Map groupingMappings = + useCommonParentForMultipleIncarnations + ? new GroupingMappingService().compute(ctx) + : Collections.emptyMap(); + Set mappingCode = cloneUnits(refCode); + mappingCode = codeMerger.splitCompilationUnitsByType(mappingCode); + ReferenceCodeSelection codeSelection = codeSelections.get(mapping); + mappingCode = + codeMerger.filterCodeForMapping( + mappingCode, + validators.get(mapping), + ctx, + referenceIndex, + codeSelection.selectedTypeIdentities()); + + Set mappingAdaptedCode = + passRunner.run( + mapping, + mappingCode, + buildAdaptationPasses( + mappingCode, + validators.get(mapping), + ctx, + referenceIndex, + conIndex, + inputConcreteIndex, + codeSelection, + useCommonParentForMultipleIncarnations), + checkers.get(mapping), + validators.get(mapping), + ctx, + groupingMappings, + codeSelection.helperTypeIdentities()); + adaptedCode = codeMerger.mergeAdaptedCode(adaptedCode, mappingAdaptedCode); } - // check validity of the reference code - CodeValidator validator = new CodeValidator(refCD, adapterParams); - if (!validator.isValid(refCD, refHwcPath)) { - Log.error("The reference code is not valid for the reference CD"); + // Composition is selected by the public API, while mapping and adaptation stay identical. + Set concreteCode = + normalizedConHwcPath.filter(Files::exists) + .map(JavaLoader::readJavaCode) + .orElseGet(LinkedHashSet::new); + concreteCode = codeMerger.splitCompilationUnitsByType(concreteCode); + FinalCodeComposition finalComposition = + finalCodeComposer.compose(codeMerger, concreteCode, adaptedCode, conIndex); + Set finalCode = finalComposition.code(); + if (!finalCode.isEmpty()) { + JavaLoader.printAST(finalCode, stagingPath); + } + normalizedConHwcPath.ifPresent(path -> outputCode.copyConcreteFiles(path, stagingPath)); + if (containsJavaFiles(stagingPath)) { + // Cleanup runs once after the selected composition has produced the complete Java tree. + outputCode.cleanCode(stagingPath, finalComposition.topToPublicSelfTypes()); + } + if (useConcretization && persistConcretizedCD) { + persistConcretizedCD(conCD, stagingPath.resolve(concretizedCDFileName)); } + workspace.publish(stagingPath); + published = true; + } finally { + if (!published) { + workspace.discard(stagingPath); + } + } + } + + private static void persistConcretizedCD(ASTCDCompilationUnit concreteCD, Path target) { + JavaLoader.writeFile(target, CD4CodeMill.prettyPrint(concreteCD, true)); + } + + private static void persistConcretizedCDAfterFailure( + ASTCDCompilationUnit concreteCD, Path target, Throwable originalFailure) { + try { + persistConcretizedCD(concreteCD, target); + } catch (RuntimeException | Error persistenceFailure) { + originalFailure.addSuppressed(persistenceFailure); + } + } + + private static FinalCodeComposition mergeFinalCode( + AdaptedCodeMerger merger, + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + return new FinalCodeComposition( + merger.mergeAdaptedCodeIntoConcreteBase(concreteCode, adaptedCode, concreteIndex), + Map.of()); + } - // build updater and clean output directory - CodeUpdater updater = new SpoonUpdater(); - updater.setCodePath(refHwcPath); - updater.setOutputDirectory(outputPath); - JavaLoader.removeDirectory(outputPath); + private static FinalCodeComposition directFinalCode( + AdaptedCodeMerger merger, + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + LinkedHashSet result = new LinkedHashSet<>(concreteCode); + result.addAll(adaptedCode); + return new FinalCodeComposition(result, Map.of()); + } - // update handler - BasicUpdateHandler handler = - new BasicUpdateHandler(refCD, conCD, conHwcPath, checker, updater, validator); + private static FinalCodeComposition composeWithTopSeparation( + AdaptedCodeMerger merger, + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + TopCodeComposer.CompositionResult result = + new TopCodeComposer(merger) + .composeWithSelfTypeBindings(concreteCode, adaptedCode, concreteIndex); + return new FinalCodeComposition(result.code(), result.topToPublicSelfTypes()); + } - // perform updates - handler.handleUpdate(JavaLoader.readJavaCode(refHwcPath)); + static void validatePaths(Path refHwcPath, Path conHwcPath, Path outputPath) { + AdaptationWorkspace.validatePaths(refHwcPath, conHwcPath, outputPath); + } - // clean code - cleanCode(outputPath); - adaptedCode = mergeAdaptedCode(adaptedCode, JavaLoader.readJavaCode(outputPath)); + private static boolean containsJavaFiles(Path sourcePath) { + if (!Files.exists(sourcePath)) { + return false; + } + if (!Files.isDirectory(sourcePath)) { + throw new IllegalArgumentException("Java source path is not a directory: " + sourcePath); + } + try (var paths = Files.walk(sourcePath)) { + return paths.anyMatch( + path -> Files.isRegularFile(path) && path.getFileName().toString().endsWith(".java")); + } catch (java.io.IOException exception) { + throw new CodeAdaptationException( + "Could not inspect Java source directory " + sourcePath, exception); } - JavaLoader.printAST(adaptedCode, outputPath); } - private Set mergeAdaptedCode( - Set actualCode, Set newAdaptedCode) { - if (actualCode.isEmpty()) { - return newAdaptedCode; + private static Set mappedRootTypeIdentities( + Set units, + CodeValidator validator, + IncarnationContext context, + CDModelIndex referenceIndex) { + validator.initializeTypeMatcher(units); + Set roots = new LinkedHashSet<>(); + for (ASTOrdinaryCompilationUnit unit : units) { + for (var type : unit.getTypeDeclarationList()) { + Optional matching = validator.getMatchedType(type); + if (matching.isEmpty()) { + continue; + } + if (!matching.get().mustBePerform()) { + if (matching.get().isExplicitAnnotation()) { + roots.add(qualifiedTypeIdentity(unit, type.getName())); + } + continue; + } + boolean mapped = + matching.get().getReferences().stream() + .map(reference -> StableElementKey.fromSymbol(reference, referenceIndex)) + .flatMap(Optional::stream) + .map(context::getIncarnations) + .anyMatch(incarnations -> !incarnations.isEmpty()); + if (mapped) { + roots.add(qualifiedTypeIdentity(unit, type.getName())); + } + } } + return Set.copyOf(roots); + } + + private static ReferenceCodeSelection emptyCodeSelection() { + return new ReferenceCodeSelection(Set.of(), Set.of(), Map.of(), Map.of(), List.of()); + } - for (ASTOrdinaryCompilationUnit newAdapted : newAdaptedCode) { - Optional actual = - actualCode.stream().filter(f -> getFileName(f).equals(getFileName(newAdapted))).findAny(); - if (actual.isEmpty()) { + private static Set cloneUnits( + Set units) { + Set clones = new LinkedHashSet<>(); + units.forEach(unit -> clones.add(unit.deepClone())); + return clones; + } - actualCode.add(newAdapted); + private static List buildAdaptationPasses( + Set units, + CodeValidator validator, + IncarnationContext context, + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + CDModelIndex inputConcreteIndex, + ReferenceCodeSelection codeSelection, + boolean useCommonParentForMultipleIncarnations) { + List passes = new ArrayList<>(); + boolean hasDefaultPass = false; + Set defaultOutputTypeNames = new LinkedHashSet<>(); + for (ASTOrdinaryCompilationUnit unit : units) { + Set relevantTypes = new LinkedHashSet<>(); + JavaAstElemCollector collector = collectJavaElements(unit); + for (String identity : topLevelTypeIdentities(unit)) { + for (String referenceType : + codeSelection + .referencedCDTypeKeysBySourceType() + .getOrDefault(identity, Set.of())) { + addRelevantTypeKey( + StableElementKey.type(referenceType), + context, + relevantTypes, + useCommonParentForMultipleIncarnations); + } + } + for (var type : collector.getAllTypeDeclarations()) { + addRelevantTypeReferences( + validator.getMatchedType(type), + context, + relevantTypes, + referenceIndex, + useCommonParentForMultipleIncarnations); + for (var field : collector.getAllFieldDeclarations(type)) { + addRelevantTypeReferences( + validator.getMatchedField(type, field), + context, + relevantTypes, + referenceIndex, + useCommonParentForMultipleIncarnations); + } + for (var supertype : collector.getAllFSuperTypeDeclarations(type)) { + addRelevantTypeReferences( + validator.getMatchedSupertype(type, supertype), + context, + relevantTypes, + referenceIndex, + useCommonParentForMultipleIncarnations); + } + for (var method : collector.getAllMethodDeclarations(type)) { + addRelevantTypeReferences( + validator.getMatchedMethod(type, method), + context, + relevantTypes, + referenceIndex, + useCommonParentForMultipleIncarnations); + for (var parameter : collector.getAllParameters(type, method)) { + addRelevantTypeReferences( + validator.getMatchedParameter(type, method, parameter), + context, + relevantTypes, + referenceIndex, + useCommonParentForMultipleIncarnations); + } + for (var local : collector.getAllLocVariables(type, method)) { + addRelevantTypeReferences( + validator.getMatchedLocalVariable(type, method, local), + context, + relevantTypes, + referenceIndex, + useCommonParentForMultipleIncarnations); + } + } + } + List orderedReferences = + relevantTypes.stream() + .sorted(Comparator.comparing(StableElementKey::signature)) + .toList(); + if (orderedReferences.isEmpty()) { + hasDefaultPass = true; + defaultOutputTypeNames.addAll( + expectedOutputTypeIdentities(unit, validator, context, Map.of(), referenceIndex)); } else { - actualCode.add(AdapterUtils.mergeAsts(actual.get(), newAdapted)); + for (Map selection : + buildCompleteTypeSelections( + orderedReferences, + context, + referenceIndex, + concreteIndex, + inputConcreteIndex)) { + Set outputIdentities = + new LinkedHashSet<>( + expectedOutputTypeIdentities( + unit, validator, context, selection, referenceIndex)); + outputIdentities.addAll(codeSelection.helperTypeIdentities()); + passes.add(new AdaptationPass(selection, outputIdentities)); + } } } - return actualCode; + defaultOutputTypeNames.addAll(codeSelection.helperTypeIdentities()); + if (hasDefaultPass) { + passes.add(0, new AdaptationPass(Map.of(), Set.copyOf(defaultOutputTypeNames))); + } + return passes; } - /*** - * clean the annotations and further extra information - * in the adapted code. - * @param codePath the code path. + /** + * Builds the owner-aware Java element index used while planning multi-incarnation passes for one + * compilation unit. */ - private static void cleanCode(Path codePath) { + private static JavaAstElemCollector collectJavaElements(ASTOrdinaryCompilationUnit unit) { + JavaAstElemCollector collector = new JavaAstElemCollector(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(collector); + unit.accept(traverser); + return collector; + } + + /** + * Adds reference types that require pass expansion because a performing match has multiple + * concrete incarnations. Exact common-grouping sets stay in a single grouped pass, and non-type + * references do not participate in type selection. + */ + private static void addRelevantTypeReferences( + Optional matching, + IncarnationContext context, + Set relevantTypes, + CDModelIndex referenceIndex, + boolean useCommonParentForMultipleIncarnations) { + if (matching.isEmpty() || !matching.get().mustBePerform()) { + return; + } + for (ISymbol reference : matching.get().getReferences()) { + if (!(reference instanceof de.monticore.cdbasis._symboltable.CDTypeSymbol)) { + continue; + } + List incarnations = + StableElementKey.fromSymbol(reference, referenceIndex) + .map(context::getIncarnations) + .orElseGet(List::of); + if (incarnations.size() < 2 + || (useCommonParentForMultipleIncarnations + && isExactGroupingSet(context, incarnations))) { + continue; + } + StableElementKey.fromSymbol(reference, referenceIndex) + .ifPresent(relevantTypes::add); + } + } - Set asts = JavaLoader.readJavaCode(codePath); + private static void addRelevantTypeKey( + StableElementKey referenceType, + IncarnationContext context, + Set relevantTypes, + boolean useCommonParentForMultipleIncarnations) { + List incarnations = + context.getIncarnations(referenceType); + if (incarnations.size() < 2 + || (useCommonParentForMultipleIncarnations + && isExactGroupingSet(context, incarnations))) { + return; + } + relevantTypes.add(referenceType); + } - JavaDSLTraverser traverser = JavaDSLMill.traverser(); - AnnotationRemover remover = new AnnotationRemover(); - traverser.add4JavaDSL(remover); - traverser.add4JavaLight(remover); - traverser.add4MCCommonStatements(remover); - traverser.add4MCVarDeclarationStatements(remover); + private static boolean isExactGroupingSet( + IncarnationContext context, List incarnations) { + Set incarnationNames = + incarnations.stream() + .map(element -> element.key().getName()) + .collect(java.util.stream.Collectors.toSet()); + Set derivedGroupings = + incarnationNames.stream() + .map(name -> groupingName(context, name)) + .flatMap(Optional::stream) + .collect(java.util.stream.Collectors.toSet()); + if (derivedGroupings.size() == 1) { + String grouping = derivedGroupings.iterator().next(); + boolean everyIncarnationBelongsToGrouping = + incarnationNames.stream() + .allMatch( + incarnation -> + grouping.equals(incarnation) + || groupingName(context, incarnation) + .filter(grouping::equals) + .isPresent()); + if (everyIncarnationBelongsToGrouping) { + return true; + } + } + for (String possibleGrouping : incarnationNames) { + boolean foundImplementer = false; + boolean exact = true; + for (String incarnation : incarnationNames) { + if (possibleGrouping.equals(incarnation)) { + continue; + } + Optional grouping = groupingName(context, incarnation); + foundImplementer |= grouping.isPresent(); + if (grouping.isEmpty() || !possibleGrouping.equals(grouping.get())) { + exact = false; + break; + } + } + if (exact && foundImplementer) { + return true; + } + } + return false; + } - asts.forEach(ast -> ast.accept(traverser)); + private static Set expectedOutputTypeIdentities( + ASTOrdinaryCompilationUnit unit, + CodeValidator validator, + IncarnationContext context, + Map selection, + CDModelIndex referenceIndex) { + Set result = new LinkedHashSet<>(); + for (var type : unit.getTypeDeclarationList()) { + Optional matching = validator.getMatchedType(type); + if (matching.isEmpty() || !matching.get().mustBePerform()) { + result.add(qualifiedTypeIdentity(unit, type.getName())); + continue; + } + boolean resolved = false; + List concreteReferences = new ArrayList<>(); + for (ISymbol reference : matching.get().getReferences()) { + IncarnationContext.MappedElement selected = + selectedIncarnation(reference, selection, context, referenceIndex); + concreteReferences.add(selected == null ? reference : selected.symbol()); + resolved |= selected != null; + } + String template = + matching.get().getGenerateTemplate() != null + && !matching.get().getGenerateTemplate().isEmpty() + ? matching.get().getGenerateTemplate() + : matching.get().getTemplate(); + if (resolved && template != null) { + result.add( + qualifiedTypeIdentity( + unit, MatcherHelper.fillTemplate(template, concreteReferences))); + } else { + result.add(qualifiedTypeIdentity(unit, type.getName())); + } + } + return Set.copyOf(result); + } - JavaLoader.printAST(asts, codePath); + private static Set topLevelTypeIdentities(ASTOrdinaryCompilationUnit unit) { + return unit.getTypeDeclarationList().stream() + .map(type -> qualifiedTypeIdentity(unit, type.getName())) + .collect(toCollection(LinkedHashSet::new)); } + + private static String qualifiedTypeIdentity( + ASTOrdinaryCompilationUnit unit, String simpleName) { + String packageName = + unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + return packageName.isBlank() ? simpleName : packageName + "." + simpleName; + } + + private static IncarnationContext.MappedElement selectedIncarnation( + ISymbol reference, + Map selection, + IncarnationContext context, + CDModelIndex referenceIndex) { + Optional referenceKey = + StableElementKey.fromSymbol(reference, referenceIndex); + IncarnationContext.MappedElement selected = referenceKey.map(selection::get).orElse(null); + if (selected != null) { + return selected; + } + List incarnations = + referenceKey.map(context::getIncarnations).orElseGet(List::of); + return incarnations.size() == 1 ? incarnations.get(0) : null; + } + + private static SortedSet validatedMappings(Set mappings) { + if (mappings == null || mappings.isEmpty()) { + throw new IllegalArgumentException("At least one mapping is required"); + } + SortedSet ordered = new TreeSet<>(); + for (String mapping : mappings) { + if (mapping == null || mapping.isBlank()) { + throw new IllegalArgumentException("Mapping names must not be null or blank"); + } + ordered.add(mapping); + } + return Collections.unmodifiableSortedSet(ordered); + } + + private static List> + buildCompleteTypeSelections( + List referenceTypes, + IncarnationContext context, + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + CDModelIndex inputConcreteIndex) { + List> selections = new ArrayList<>(); + selections.add(new LinkedHashMap<>()); + for (StableElementKey referenceType : referenceTypes) { + List incarnations = + context.getIncarnations(referenceType).stream() + .collect(toCollection(ArrayList::new)); + incarnations.removeIf(incarnation -> !concreteIndex.hasType(incarnation.key().getName())); + String referenceName = referenceType.getName(); + if (incarnations.size() > 1 && !inputConcreteIndex.hasType(referenceName)) { + incarnations.removeIf(incarnation -> referenceName.equals(incarnation.key().getName())); + } + List sameKindIncarnations = + incarnations.stream() + .filter( + incarnation -> + sameTypeKind( + referenceIndex.type(referenceName).orElse(null), + concreteIndex.type(incarnation.key().getName()).orElse(null))) + .toList(); + if (!sameKindIncarnations.isEmpty()) { + incarnations = new ArrayList<>(sameKindIncarnations); + } + incarnations.sort(Comparator.comparing(incarnation -> incarnation.key().signature())); + if (incarnations.isEmpty()) { + throw new CodeAdaptationException( + "No incarnations available for reference type '" + referenceType.getName() + "'"); + } + long newSize = (long) selections.size() * incarnations.size(); + if (newSize > MAX_TYPE_SELECTIONS) { + throw new CodeAdaptationException( + "Multi-incarnation type selections exceed the safe limit of " + + MAX_TYPE_SELECTIONS + + " runs"); + } + List> expanded = + new ArrayList<>((int) newSize); + for (Map selection : selections) { + for (IncarnationContext.MappedElement incarnation : incarnations) { + Map copy = + new LinkedHashMap<>(selection); + copy.put(referenceType, incarnation); + expanded.add(Map.copyOf(copy)); + } + } + selections = expanded; + } + return selections; + } + + private static boolean sameTypeKind( + de.monticore.cdbasis._ast.ASTCDType reference, + de.monticore.cdbasis._ast.ASTCDType incarnation) { + return (reference instanceof de.monticore.cdbasis._ast.ASTCDClass + && incarnation instanceof de.monticore.cdbasis._ast.ASTCDClass) + || (reference instanceof de.monticore.cdinterfaceandenum._ast.ASTCDInterface + && incarnation + instanceof de.monticore.cdinterfaceandenum._ast.ASTCDInterface) + || (reference instanceof de.monticore.cdinterfaceandenum._ast.ASTCDEnum + && incarnation instanceof de.monticore.cdinterfaceandenum._ast.ASTCDEnum); + } + + private static Optional groupingName( + IncarnationContext context, String concreteTypeName) { + return context + .getGroupingFor(StableElementKey.type(concreteTypeName)) + .map(grouping -> grouping.key().getName()); + } + } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapterTool.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapterTool.java index 14666089..641f270a 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapterTool.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/CodeAdapterTool.java @@ -1,102 +1,212 @@ package de.monticore.codeAdaption; +import static de.monticore.cdconformance.CDConfParameter.ADAPTED_NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_CARD_RESTRICTION; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.METHOD_OVERLOADING; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.SRC_TARGET_ASSOC_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE_MEMBER; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.INFIX_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; + import de.monticore.CD4CodeTool; import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.utils.AdapterParam; +import de.monticore.codeAdaption.utils.JavaLoader; import de.se_rwth.commons.logging.Log; -import java.util.*; -import org.apache.commons.cli.*; +import java.io.File; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Locale; +import java.util.Set; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +/** Command-line entry point for reference-code adaptation. */ public class CodeAdapterTool extends CD4CodeTool { - /** - * Processes user input from command line and delegates to the corresponding tools. - * - * @param args The input parameters for configuring the OCL tool. - */ + public static void main(String[] args) { + int exitCode = new CodeAdapterTool().execute(args); + if (exitCode != 0) { + System.exit(exitCode); + } + } + @Override public void run(String[] args) { - init(); - Options options = initOptions(); - /* - java - jar AdapterTool . jar -i " University . cd " \ - 2 -- reference " UserRole . cd " -- rc " code " - */ + execute(args); + } + /** Executes one CLI invocation and returns a process-style exit code. */ + int execute(String[] args) { + init(); + Options options = addAdditionalOptions(new Options()); + if (Arrays.stream(args).anyMatch(argument -> "-h".equals(argument) || "--help".equals(argument))) { + printAdapterHelp(options); + return 0; + } try { - // create CLI parser and parse input options from command line - CommandLineParser cliparser = new DefaultParser(); - CommandLine cmd = cliparser.parse(options, args); - - if (cmd.hasOption("i")) { - // -option developer logging - if (cmd.hasOption("d")) { - Log.initDEBUG(); - } else { - Log.init(); - } + CommandLine commandLine = new DefaultParser().parse(options, args); + if (commandLine.hasOption("debug")) { + Log.initDEBUG(); + } else { + Log.init(); } - } catch (ParseException e) { - Log.error("0xA7101 Could not process CLI parameters: " + e.getMessage()); + Set mappings = values(commandLine, "mapping"); + if (mappings.isEmpty()) { + throw new ParseException("At least one --mapping must be supplied"); + } + + Set adapterParameters = matchingParameters(commandLine); + if (commandLine.hasOption("ignore-unmatched")) { + adapterParameters.add(IGNORE_NON_MATCHED_TYPE); + adapterParameters.add(IGNORE_NON_MATCHED_TYPE_MEMBER); + adapterParameters.add(IGNORE_NON_MATCHED_VAR); + } + + CodeAdapter adapter = new CodeAdapter(adapterParameters, defaultConformanceParameters()); + adapter.adapt( + requiredFile(commandLine, "reference"), + requiredFile(commandLine, "concrete"), + mappings, + requiredPath(commandLine, "reference-code"), + requiredPath(commandLine, "concrete-code"), + requiredPath(commandLine, "output"), + commandLine.hasOption("concretize"), + !commandLine.hasOption("no-common-parent"), + !commandLine.hasOption("no-persist-concretized-cd")); + return 0; + } catch (ParseException | IllegalArgumentException | CodeAdaptationException exception) { + Log.warn("Code adaptation failed: " + exception.getMessage()); + printAdapterHelp(options); + return 2; + } catch (RuntimeException exception) { + Log.warn("Unexpected code-adaptation failure: " + exception.getMessage()); + return 1; } } - /*=================================================================*/ - /* Part 2: Executing arguments - /*=================================================================*/ - - /** - * Parses the contents of a given file as OCL. - * - * @param path The path to the OCL-file as String - * @return parsed AST - */ @Override public ASTCDCompilationUnit parse(String path) { - return null; + return JavaLoader.parseCD(path); } - /** - * Initializes the additional options for the OCL tool. - * - * @return The CLI options with arguments. - */ @Override public Options addAdditionalOptions(Options options) { - - // accept VariableSymbols - Option varSymbols = - Option.builder("i") - .longOpt("concrete") - .optionalArg(false) - .argName("concrete class diagram") - .hasArgs() - .desc("introduce the concrete class diagram") - .build(); - options.addOption(varSymbols); - - // accept FunctionSymbols - Option funcSymbols = - Option.builder("rc") - .longOpt("reference-code") - .optionalArg(false) - .argName("reference-code") + options.addOption(requiredPathOption("r", "reference", "reference class diagram")); + options.addOption(requiredPathOption("c", "concrete", "concrete class diagram")); + options.addOption(requiredPathOption("rc", "reference-code", "reference Java source directory")); + options.addOption(requiredPathOption("cc", "concrete-code", "concrete Java source directory")); + options.addOption(requiredPathOption("o", "output", "generated Java output directory")); + options.addOption( + Option.builder("m") + .longOpt("mapping") .hasArgs() - .desc("Introduce the reference code.") - .build(); - options.addOption(funcSymbols); - - // accept FunctionSymbols - Option ignoreSymbols = - Option.builder("r") - .longOpt("reference") - .optionalArg(false) - .argName("reference class diagram") + .argName("name...") + .desc("mapping stereotype name; at least one is required") + .build()); + options.addOption( + Option.builder() + .longOpt("matching") .hasArgs() - .desc("introduce the reference class diagram") - .build(); - options.addOption(ignoreSymbols); - + .argName("name|infix|annotation") + .desc("matching strategies; defaults to all strategies") + .build()); + options.addOption( + Option.builder().longOpt("concretize").desc("complete the concrete CD first").build()); + options.addOption( + Option.builder() + .longOpt("no-persist-concretized-cd") + .desc("do not write the concretized CD to the output directory") + .build()); + options.addOption( + Option.builder() + .longOpt("no-common-parent") + .desc("disable common-parent grouping for multiple incarnations") + .build()); + options.addOption( + Option.builder() + .longOpt("ignore-unmatched") + .desc("retain unmatched reference-code elements instead of reporting them") + .build()); + options.addOption(Option.builder("d").longOpt("debug").desc("enable debug logging").build()); + options.addOption(Option.builder("h").longOpt("help").desc("show this help").build()); return options; } + + private static Option requiredPathOption(String shortName, String longName, String description) { + return Option.builder(shortName) + .longOpt(longName) + .hasArg() + .required() + .argName("path") + .desc(description) + .build(); + } + + private static File requiredFile(CommandLine commandLine, String option) throws ParseException { + return requiredPath(commandLine, option).toFile(); + } + + private static Path requiredPath(CommandLine commandLine, String option) throws ParseException { + String value = commandLine.getOptionValue(option); + if (value == null || value.isBlank()) { + throw new ParseException("Missing required option --" + option); + } + return Path.of(value).toAbsolutePath().normalize(); + } + + private static Set values(CommandLine commandLine, String option) { + String[] supplied = commandLine.getOptionValues(option); + return supplied == null + ? new LinkedHashSet<>() + : new LinkedHashSet<>(Arrays.asList(supplied)); + } + + private static Set matchingParameters(CommandLine commandLine) + throws ParseException { + Set requested = values(commandLine, "matching"); + if (requested.isEmpty()) { + return new LinkedHashSet<>(Set.of(NAME_MATCHING, INFIX_MATCHING, ANNOTATION_MATCHING)); + } + Set result = new LinkedHashSet<>(); + for (String value : requested) { + switch (value.toLowerCase(Locale.ROOT)) { + case "name" -> result.add(NAME_MATCHING); + case "infix" -> result.add(INFIX_MATCHING); + case "annotation" -> result.add(ANNOTATION_MATCHING); + default -> throw new ParseException("Unknown matching strategy: " + value); + } + } + return result; + } + + private static Set defaultConformanceParameters() { + return Set.of( + STEREOTYPE_MAPPING, + NAME_MAPPING, + SRC_TARGET_ASSOC_MAPPING, + INHERITANCE, + ALLOW_CARD_RESTRICTION, + METHOD_OVERLOADING, + ADAPTED_NAME_MAPPING, + ALLOW_ADDITIONAL_PARAMETERS); + } + + private static void printAdapterHelp(Options options) { + new HelpFormatter().printHelp("java -jar CodeAdapter.jar", options, true); + } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/HelperVariantRegistry.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/HelperVariantRegistry.java new file mode 100644 index 00000000..38216d4c --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/HelperVariantRegistry.java @@ -0,0 +1,62 @@ +package de.monticore.codeAdaption; + +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Tracks the canonical adapted form of every concretization helper during one adaptation. + * + *

The owning {@link MappingAdaptationRunner} is created once per adaptation, so entries are + * shared by all incarnation passes and mapping names without leaking into later adaptations. + */ +final class HelperVariantRegistry { + private final Map variants = new LinkedHashMap<>(); + + /** + * Registers one adapted helper variant. + * + * @return {@code true} when the caller must retain the unit, or {@code false} when an identical + * variant was registered earlier and the unit can be deduplicated + * @throws CodeAdaptationException when the helper was previously produced with a structurally + * different AST + */ + boolean register( + String helperIdentity, + ASTOrdinaryCompilationUnit variant, + String mapping, + String passDescription) { + Objects.requireNonNull(helperIdentity); + Objects.requireNonNull(variant); + Origin incomingOrigin = new Origin(mapping, passDescription); + RegisteredVariant registered = variants.get(helperIdentity); + if (registered == null) { + variants.put(helperIdentity, new RegisteredVariant(variant.deepClone(), incomingOrigin)); + return true; + } + if (registered.canonical().deepEquals(variant, true)) { + return false; + } + throw new CodeAdaptationException( + "Helper type '" + + helperIdentity + + "' has divergent outputs from " + + registered.origin().describe() + + " and " + + incomingOrigin.describe()); + } + + private record RegisteredVariant(ASTOrdinaryCompilationUnit canonical, Origin origin) {} + + private record Origin(String mapping, String passDescription) { + private Origin { + Objects.requireNonNull(mapping); + Objects.requireNonNull(passDescription); + } + + String describe() { + return "mapping '" + mapping + "', type selection " + passDescription; + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/MappingAdaptationRunner.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/MappingAdaptationRunner.java new file mode 100644 index 00000000..918cc0d1 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/MappingAdaptationRunner.java @@ -0,0 +1,204 @@ +package de.monticore.codeAdaption; + +import de.monticore.cdconformance.CDConformanceChecker; +import de.monticore.codeAdaption.handler.BasicUpdateHandler; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.updater.CodeUpdater; +import de.monticore.codeAdaption.updater.CodeUpdaterMill; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.validator.CodeValidator; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** Executes the complete isolated updater lifecycle for one mapping's adaptation passes. */ +final class MappingAdaptationRunner { + /** + * One deterministic multi-incarnation choice and the top-level Java types retained from it. + * + *

{@code typeSelection} maps reference type keys to the one concrete incarnation used by this + * pass, for example {@code Payment -> CreditCard}. {@code outputTypeIdentities} contains + * package-qualified concrete Java top-level names such as {@code billing.CreditCardAdapter}; all + * other transformed units are discarded after serving as cross-file resolution context. + */ + record AdaptationPass( + Map typeSelection, + Set outputTypeIdentities) { + AdaptationPass { + typeSelection = Map.copyOf(typeSelection); + outputTypeIdentities = Set.copyOf(outputTypeIdentities); + } + + String describeSelection() { + return typeSelection.entrySet().stream() + .sorted(Map.Entry.comparingByKey(Comparator.comparing(StableElementKey::signature))) + .map(entry -> entry.getKey().signature() + "=" + entry.getValue().key().signature()) + .collect(Collectors.joining(", ", "[", "]")); + } + } + + private final AdaptationWorkspace workspace; + private final AdaptedCodeMerger codeMerger; + private final Path stagingPath; + private final CDModelIndex referenceIndex; + private final CDModelIndex concreteIndex; + private final CDModelIndex inputConcreteIndex; + private final boolean useCommonParentForMultipleIncarnations; + private final HelperVariantRegistry helperVariantRegistry = new HelperVariantRegistry(); + + MappingAdaptationRunner( + AdaptationWorkspace workspace, + AdaptedCodeMerger codeMerger, + Path stagingPath, + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + CDModelIndex inputConcreteIndex, + boolean useCommonParentForMultipleIncarnations) { + this.workspace = workspace; + this.codeMerger = codeMerger; + this.stagingPath = stagingPath; + this.referenceIndex = referenceIndex; + this.concreteIndex = concreteIndex; + this.inputConcreteIndex = inputConcreteIndex; + this.useCommonParentForMultipleIncarnations = + useCommonParentForMultipleIncarnations; + } + + /** + * Runs every planned pass for one mapping and merges the retained output units. + * + * @param mapping stereotype name identifying the isolated incarnation context + * @param mappingCode complete filtered source set loaded into every pass for cross-file resolution + * @param passes concrete incarnation selections to execute + * @param checker usable conformance checker, or {@code null} for manual mapping + * @param validator matcher/validation state for the same mapping + * @param context reference-element to concrete-incarnation mappings for this mapping only + * @param groupingMappings incarnation simple name to common grouping-type simple name + * @param helperTypeIdentities package-qualified top-level helper names retained by every pass + */ + Set run( + String mapping, + Set mappingCode, + List passes, + CDConformanceChecker checker, + CodeValidator validator, + IncarnationContext context, + Map groupingMappings, + Set helperTypeIdentities) { + Set adaptedCode = new LinkedHashSet<>(); + for (AdaptationPass pass : passes) { + // Load the complete mapping source set so Spoon can update cross-file references. The pass + // still controls which transformed top-level types are retained below. + Set mappingRefCode = cloneUnits(mappingCode); + Path tempPath = workspace.createMappingDirectory(stagingPath); + try { + JavaLoader.printAST(mappingRefCode, tempPath); + CodeUpdater updater = prepareUpdater(tempPath, groupingMappings); + BasicUpdateHandler handler = + new BasicUpdateHandler( + referenceIndex, + concreteIndex, + inputConcreteIndex, + checker, + updater, + validator, + context, + pass.typeSelection(), + useCommonParentForMultipleIncarnations); + + handler.handleUpdate(mappingRefCode); + updater.printCode(); + Set processedCode = + codeMerger.splitCompilationUnitsByType(JavaLoader.readJavaCode(tempPath)); + processedCode = + processedCode.stream() + .filter( + unit -> + declaredTypeIdentities(unit).stream() + .anyMatch(pass.outputTypeIdentities()::contains)) + .collect(Collectors.toCollection(LinkedHashSet::new)); + processedCode = + removeDuplicateHelperVariants(processedCode, helperTypeIdentities, mapping, pass); + adaptedCode = codeMerger.mergeAdaptedCode(adaptedCode, processedCode); + } catch (CodeAdaptationException exception) { + String selection = + pass.typeSelection().isEmpty() + ? "" + : " for type selection " + pass.describeSelection(); + throw new CodeAdaptationException( + "Failed to process mapping '" + + mapping + + "'" + + selection + + ": " + + exception.getMessage(), + exception); + } catch (RuntimeException | AssertionError exception) { + String selection = + pass.typeSelection().isEmpty() + ? "" + : " for type selection " + pass.describeSelection(); + throw new CodeAdaptationException( + "Failed to process mapping '" + mapping + "'" + selection, exception); + } finally { + CodeUpdaterMill.reset(); + workspace.discard(tempPath); + } + } + return adaptedCode; + } + + private CodeUpdater prepareUpdater(Path tempPath, Map groupingMappings) { + CodeUpdaterMill.reset(); + CodeUpdater updater = CodeUpdaterMill.getUpdater(); + updater.setCodePath(tempPath); + updater.setGroupingMappings(groupingMappings); + updater.setOutputDirectory(tempPath); + return updater; + } + + private static Set cloneUnits( + Set units) { + Set clones = new LinkedHashSet<>(); + units.forEach(unit -> clones.add(unit.deepClone())); + return clones; + } + + private Set removeDuplicateHelperVariants( + Set incoming, + Set helperTypeIdentities, + String mapping, + AdaptationPass pass) { + Set retained = new LinkedHashSet<>(); + for (ASTOrdinaryCompilationUnit unit : incoming) { + String helperIdentity = + declaredTypeIdentities(unit).stream() + .filter(helperTypeIdentities::contains) + .findFirst() + .orElse(null); + if (helperIdentity == null + || helperVariantRegistry.register( + helperIdentity, unit, mapping, pass.describeSelection())) { + retained.add(unit); + } + } + return retained; + } + + private static Set declaredTypeIdentities(ASTOrdinaryCompilationUnit unit) { + String packageName = + unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + return unit.getTypeDeclarationList().stream() + .map(type -> packageName.isBlank() ? type.getName() : packageName + "." + type.getName()) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/OutputCodeService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/OutputCodeService.java new file mode 100644 index 00000000..f817e516 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/OutputCodeService.java @@ -0,0 +1,196 @@ +package de.monticore.codeAdaption; + +import de.monticore.codeAdaption.updater.CodeUpdater; +import de.monticore.codeAdaption.updater.CodeUpdaterMill; +import de.monticore.codeAdaption.utils.AdapterUtils; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * Handles final generated-source cleanup and inclusion of pre-existing concrete handwritten files. + * + *

Adapted files are already present in the staging output. Concrete handwritten files are added + * without overwriting the user's source tree. Multi-type Java units are split by declaration and + * merged into an existing staged target with the staged output taking precedence. + */ +final class OutputCodeService { + + /** + * Copies concrete handwritten files into their final package-relative output locations. + * + *

Java files are placed according to their declared package rather than their input folder. + * For example, {@code misc/Port.java} declaring {@code package shipping;} targets {@code + * shipping/Port.java}. Non-Java files preserve their path relative to {@code conHwcPath}. If two + * inputs target the same output path, identical single-file content is deduplicated and + * overlapping declarations are rejected. + */ + void copyConcreteFiles(Path conHwcPath, Path outputPath) { + if (!Files.exists(conHwcPath)) { + try { + Files.createDirectories(outputPath); + } catch (IOException e) { + throw new IllegalStateException("Failed to create output directory " + outputPath, e); + } + return; + } + try (var paths = Files.walk(conHwcPath)) { + Map sourcesByTarget = new LinkedHashMap<>(); + Map splitJavaByTarget = new LinkedHashMap<>(); + Map ownersByTarget = new LinkedHashMap<>(); + for (Path source : + paths + .filter(Files::isRegularFile) + .sorted(java.util.Comparator.comparing(Path::toString)) + .toList()) { + if (source.toString().endsWith(".java")) { + ASTOrdinaryCompilationUnit unit = loadConcreteJava(source); + if (unit.getTypeDeclarationList().size() > 1) { + for (ASTTypeDeclaration type : unit.getTypeDeclarationList()) { + ASTOrdinaryCompilationUnit split = compilationUnitForType(unit, type.getName()); + Path target = + concreteTypeTarget(split, type.getName(), outputPath) + .toAbsolutePath() + .normalize(); + registerUniqueTarget(ownersByTarget, target, source); + splitJavaByTarget.put(target, split); + } + continue; + } + } + Path target = concreteCopyTarget(conHwcPath, source, outputPath).toAbsolutePath().normalize(); + Path existingSource = ownersByTarget.putIfAbsent(target, source); + if (existingSource != null) { + if (!splitJavaByTarget.containsKey(target) + && Files.mismatch(existingSource, source) == -1L) { + continue; + } + throw targetCollision(existingSource, source, target); + } + sourcesByTarget.put(target, source); + } + for (Map.Entry entry : sourcesByTarget.entrySet()) { + Path target = entry.getKey(); + Path source = entry.getValue(); + Files.createDirectories(target.getParent()); + if (!Files.exists(target)) { + Files.copy(source, target); + } + } + for (Map.Entry entry : splitJavaByTarget.entrySet()) { + Path target = entry.getKey(); + Files.createDirectories(target.getParent()); + ASTOrdinaryCompilationUnit concreteFragment = entry.getValue(); + ASTOrdinaryCompilationUnit finalUnit = + Files.exists(target) + ? AdapterUtils.mergeAstsPreferringLeft( + JavaLoader.loadJava(target.toFile()), concreteFragment) + : concreteFragment; + JavaLoader.printAST(Set.of(finalUnit), outputPath); + } + } catch (IOException e) { + throw new IllegalStateException("Failed to include concrete handwritten code", e); + } + } + + /** Removes adapter-only annotations/imports and performs the updater's final source cleanup. */ + void cleanCode(Path codePath) { + cleanCode(codePath, Map.of()); + } + + /** Cleans final Java and applies self-type bindings recorded by TOP composition. */ + void cleanCode(Path codePath, Map topToPublicSelfTypes) { + CodeUpdaterMill.reset(); + try { + CodeUpdater updater = CodeUpdaterMill.getUpdater(); + updater.cleanCode(codePath, topToPublicSelfTypes); + } finally { + CodeUpdaterMill.reset(); + } + } + + private ASTOrdinaryCompilationUnit loadConcreteJava(Path source) { + try { + return JavaLoader.loadJava(source.toFile()); + } catch (RuntimeException | AssertionError parseFailure) { + throw new IllegalStateException( + "Failed to determine declarations in concrete Java file " + source, parseFailure); + } + } + + private ASTOrdinaryCompilationUnit compilationUnitForType( + ASTOrdinaryCompilationUnit source, String typeName) { + ASTOrdinaryCompilationUnit split = source.deepClone(); + for (ASTTypeDeclaration type : + new java.util.ArrayList<>(split.getTypeDeclarationList())) { + if (!type.getName().equals(typeName)) { + split.removeTypeDeclaration(type); + } + } + return split; + } + + private Path concreteTypeTarget( + ASTOrdinaryCompilationUnit unit, String typeName, Path outputPath) { + if (unit.isPresentPackageDeclaration()) { + Path packagePath = + Path.of( + unit.getPackageDeclaration() + .getMCQualifiedName() + .getQName() + .replace('.', File.separatorChar)); + return outputPath.resolve(packagePath).resolve(typeName + ".java"); + } + return outputPath.resolve(typeName + ".java"); + } + + private void registerUniqueTarget( + Map ownersByTarget, Path target, Path source) { + Path existingSource = ownersByTarget.putIfAbsent(target, source); + if (existingSource != null) { + throw targetCollision(existingSource, source, target); + } + } + + private IllegalStateException targetCollision( + Path existingSource, Path source, Path target) { + return new IllegalStateException( + "Concrete files '" + + existingSource + + "' and '" + + source + + "' both target '" + + target + + "' but declare overlapping Java output"); + } + + private Path concreteCopyTarget(Path conHwcPath, Path source, Path outputPath) { + if (!source.toString().endsWith(".java")) { + return outputPath.resolve(conHwcPath.relativize(source)); + } + try { + ASTOrdinaryCompilationUnit ast = JavaLoader.loadJava(source.toFile()); + if (ast.isPresentPackageDeclaration()) { + Path packagePath = + Path.of( + ast.getPackageDeclaration() + .getMCQualifiedName() + .getQName() + .replace('.', File.separatorChar)); + return outputPath.resolve(packagePath).resolve(source.getFileName()); + } + } catch (RuntimeException | AssertionError parseFailure) { + throw new IllegalStateException( + "Failed to determine package for concrete Java file " + source, parseFailure); + } + return outputPath.resolve(conHwcPath.relativize(source)); + } + +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/TopCodeComposer.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/TopCodeComposer.java new file mode 100644 index 00000000..74afb8cc --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/TopCodeComposer.java @@ -0,0 +1,377 @@ +package de.monticore.codeAdaption; + +import de.monticore.codeAdaption.utils.AdapterUtils; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTClassDeclaration; +import de.monticore.java.javadsl._ast.ASTCompilationUnit; +import de.monticore.java.javadsl._ast.ASTEnumDeclaration; +import de.monticore.java.javadsl._ast.ASTInterfaceDeclaration; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.javalight._ast.ASTConstructorDeclaration; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Keeps adapted implementations separate from concrete handwritten code through TOP inheritance. */ +final class TopCodeComposer { + + private final AdaptedCodeMerger codeMerger; + + TopCodeComposer(AdaptedCodeMerger codeMerger) { + this.codeMerger = codeMerger; + } + + /** + * Produces one output set containing unchanged concrete HWC and separate adapted TOP companions. + * Analysis completes before either cloned input set is mutated, so invalid inheritance or name + * collisions fail deterministically. + */ + Set compose( + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + return composeWithSelfTypeBindings(concreteCode, adaptedCode, concreteIndex).code(); + } + + /** + * Composes the sources and records which generated TOP implementations represent a public HWC + * subtype. The binding is used later to repair explicit {@code this} values in fluent APIs. + */ + CompositionResult composeWithSelfTypeBindings( + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + Set concreteCopies = cloneUnits(concreteCode); + Set relocatedAdapted = + codeMerger.relocateAdaptedCodeToConcretePackages( + concreteCopies, cloneUnits(adaptedCode), concreteIndex); + + List plan = + analyze(concreteCopies, relocatedAdapted, concreteIndex); + plan.forEach(this::apply); + + LinkedHashSet result = new LinkedHashSet<>(); + ordered(concreteCopies).forEach(result::add); + ordered(relocatedAdapted).forEach(result::add); + assertUniqueOutputTypes(result); + Map selfTypeBindings = new LinkedHashMap<>(); + for (CompositionEntry entry : plan) { + String targetPackage = packageName(entry.concreteUnit()); + selfTypeBindings.put( + qualify(targetPackage, entry.topName()), + qualify(targetPackage, entry.concreteName())); + } + return new CompositionResult(result, Map.copyOf(selfTypeBindings)); + } + + private List analyze( + Set concreteCode, + Set adaptedCode, + CDModelIndex concreteIndex) { + Map concreteByQualifiedName = indexTypes(concreteCode, "concrete HWC"); + Map> concreteBySimpleName = indexBySimpleName(concreteByQualifiedName); + List plan = new ArrayList<>(); + Map finalIdentities = new LinkedHashMap<>(); + + concreteByQualifiedName.keySet().stream() + .sorted() + .forEach(identity -> reserve(finalIdentities, identity, "concrete HWC")); + + for (ASTOrdinaryCompilationUnit adaptedUnit : ordered(adaptedCode)) { + if (adaptedUnit.getTypeDeclarationList().size() != 1) { + throw new CodeAdaptationException( + "TOP composition requires one top-level declaration per adapted compilation unit: " + + AdapterUtils.getFileName(adaptedUnit)); + } + ASTTypeDeclaration adaptedType = adaptedUnit.getTypeDeclaration(0); + String concreteName = adaptedType.getName(); + String adaptedIdentity = qualify(packageName(adaptedUnit), concreteName); + TypeLocation concrete = + matchingConcreteType( + adaptedIdentity, concreteName, concreteByQualifiedName, concreteBySimpleName, + concreteIndex); + + if (concrete == null) { + reserve(finalIdentities, adaptedIdentity, "adapted code"); + continue; + } + + validateKinds(concreteName, adaptedType, concrete.type()); + validateConcreteInheritance(concreteName, concrete); + validateAdaptedBase(concreteName, adaptedType); + + String topName = concreteName + "TOP"; + String topIdentity = qualify(packageName(concrete.unit()), topName); + reserve(finalIdentities, topIdentity, "adapted TOP code"); + plan.add( + new CompositionEntry( + adaptedUnit, + adaptedType, + concrete.unit(), + concrete.type(), + concreteName, + topName)); + } + return List.copyOf(plan); + } + + private TypeLocation matchingConcreteType( + String adaptedIdentity, + String concreteName, + Map concreteByQualifiedName, + Map> concreteBySimpleName, + CDModelIndex concreteIndex) { + TypeLocation exact = concreteByQualifiedName.get(adaptedIdentity); + if (exact != null) { + return exact; + } + // Generated pattern types such as PersonBuilder need not be declared in the concrete CD. The + // CD is required only before falling back from a qualified identity to a simple-name match. + if (!concreteIndex.hasType(concreteName)) { + return null; + } + List candidates = concreteBySimpleName.getOrDefault(concreteName, List.of()); + if (candidates.size() > 1) { + throw new CodeAdaptationException( + "Cannot select concrete HWC for '" + + concreteName + + "' because it is declared in several packages: " + + candidates.stream() + .map(candidate -> qualify(packageName(candidate.unit()), concreteName)) + .sorted() + .toList()); + } + return candidates.isEmpty() ? null : candidates.get(0); + } + + private void validateKinds( + String concreteName, ASTTypeDeclaration adaptedType, ASTTypeDeclaration concreteType) { + if (adaptedType instanceof ASTEnumDeclaration || concreteType instanceof ASTEnumDeclaration) { + throw new CodeAdaptationException( + "TOP separation is not supported for enum '" + concreteName + "'"); + } + if (!adaptedType.getClass().equals(concreteType.getClass())) { + throw new CodeAdaptationException( + "Cannot TOP-compose '" + concreteName + "' because Java declaration kinds differ"); + } + } + + private void validateConcreteInheritance(String concreteName, TypeLocation concrete) { + ASTTypeDeclaration concreteType = concrete.type(); + if (!(concreteType instanceof ASTClassDeclaration concreteClass) + || !concreteClass.isPresentSuperClass()) { + return; + } + String expectedSimpleName = concreteName + "TOP"; + String expectedIdentity = qualify(packageName(concrete.unit()), expectedSimpleName); + if (!isExpectedTopType( + concrete.unit(), concreteClass.getSuperClass(), expectedSimpleName, expectedIdentity)) { + throw new CodeAdaptationException( + "Concrete HWC class '" + + concreteName + + "' already extends unrelated class '" + + JavaSourceNames.printQualifiedType(concreteClass.getSuperClass()) + + "'"); + } + } + + private boolean isExpectedTopType( + ASTOrdinaryCompilationUnit unit, + ASTMCType type, + String expectedSimpleName, + String expectedIdentity) { + String printedType = JavaSourceNames.printQualifiedType(type); + List references = + JavaSourceNames.typeReferences(printedType); + String identity = references.isEmpty() ? printedType : references.get(0).originalName(); + return JavaSourceNames.simpleName(identity).equals(expectedSimpleName) + && (!identity.contains(".") || identity.equals(expectedIdentity)) + && !importsDifferentTop(unit, expectedSimpleName, expectedIdentity); + } + + /** Rejects an unqualified TOP name when an explicit import binds it to another package. */ + private boolean importsDifferentTop( + ASTOrdinaryCompilationUnit unit, String simpleName, String expectedIdentity) { + return unit.getImportDeclarationList().stream() + .filter(importDeclaration -> !importDeclaration.isStatic() && !importDeclaration.isSTAR()) + .map(importDeclaration -> importDeclaration.getMCQualifiedName().getQName()) + .filter(importedName -> JavaSourceNames.simpleName(importedName).equals(simpleName)) + .anyMatch(importedName -> !importedName.equals(expectedIdentity)); + } + + private void validateAdaptedBase(String concreteName, ASTTypeDeclaration adaptedType) { + if (adaptedType instanceof ASTClassDeclaration adaptedClass + && adaptedClass.getJavaModifierList().stream() + .map(JavaLoader::print) + .map(String::trim) + .anyMatch("final"::equals)) { + throw new CodeAdaptationException( + "Adapted class '" + concreteName + "' is final and cannot become a TOP base class"); + } + } + + private void apply(CompositionEntry entry) { + addTopParent(entry.concreteType(), entry.topName()); + shiftSelfTopParent(entry.adaptedUnit(), entry.adaptedType(), entry.topName()); + + // A global type refactoring would also rewrite fields, return types, and `new Concrete()`. + // Only the implementation declaration and its constructors belong to the TOP companion. + entry.adaptedType().setName(entry.topName()); + if (entry.adaptedType() instanceof ASTClassDeclaration adaptedClass) { + adaptedClass.getClassBody().getClassBodyDeclarationList().stream() + .filter(ASTConstructorDeclaration.class::isInstance) + .map(ASTConstructorDeclaration.class::cast) + .forEach(constructor -> constructor.setName(entry.topName())); + } + entry.adaptedUnit().get_SourcePositionStart().setFileName(entry.topName() + ".java"); + } + + private void addTopParent(ASTTypeDeclaration concreteType, String topName) { + if (concreteType instanceof ASTClassDeclaration concreteClass) { + if (!concreteClass.isPresentSuperClass()) { + concreteClass.setSuperClass(typeReference(topName)); + } + return; + } + ASTInterfaceDeclaration concreteInterface = (ASTInterfaceDeclaration) concreteType; + boolean alreadyPresent = + concreteInterface.getExtendedInterfaceList().stream() + .anyMatch(type -> simpleTypeName(type).equals(topName)); + if (!alreadyPresent) { + concreteInterface.getExtendedInterfaceList().add(typeReference(topName)); + } + } + + private void shiftSelfTopParent( + ASTOrdinaryCompilationUnit adaptedUnit, ASTTypeDeclaration adaptedType, String topName) { + String topTopName = topName + "TOP"; + String expectedIdentity = qualify(packageName(adaptedUnit), topName); + if (adaptedType instanceof ASTClassDeclaration adaptedClass) { + if (adaptedClass.isPresentSuperClass() + && isExpectedTopType( + adaptedUnit, adaptedClass.getSuperClass(), topName, expectedIdentity)) { + adaptedClass.setSuperClass(typeReference(topTopName)); + } + return; + } + ASTInterfaceDeclaration adaptedInterface = (ASTInterfaceDeclaration) adaptedType; + List parents = adaptedInterface.getExtendedInterfaceList(); + for (int index = 0; index < parents.size(); index++) { + if (isExpectedTopType(adaptedUnit, parents.get(index), topName, expectedIdentity)) { + parents.set(index, typeReference(topTopName)); + } + } + } + + private ASTMCType typeReference(String name) { + try { + ASTCompilationUnit parsed = + JavaDSLMill.parser() + .parse_StringCompilationUnit("class Holder extends " + name + " {}") + .orElseThrow( + () -> new CodeAdaptationException("Could not create Java type '" + name + "'")); + ASTOrdinaryCompilationUnit unit = (ASTOrdinaryCompilationUnit) parsed; + return ((ASTClassDeclaration) unit.getTypeDeclaration(0)).getSuperClass().deepClone(); + } catch (IOException exception) { + throw new CodeAdaptationException("Could not create Java type '" + name + "'", exception); + } + } + + private Map indexTypes( + Set units, String sourceLabel) { + Map result = new LinkedHashMap<>(); + for (ASTOrdinaryCompilationUnit unit : ordered(units)) { + for (ASTTypeDeclaration type : unit.getTypeDeclarationList()) { + String identity = qualify(packageName(unit), type.getName()); + if (result.putIfAbsent(identity, new TypeLocation(unit, type)) != null) { + throw new CodeAdaptationException( + "Duplicate " + sourceLabel + " declaration '" + identity + "'"); + } + } + } + return result; + } + + private Map> indexBySimpleName( + Map byQualifiedName) { + Map> result = new LinkedHashMap<>(); + byQualifiedName.values().forEach( + location -> + result.computeIfAbsent(location.type().getName(), ignored -> new ArrayList<>()) + .add(location)); + return result; + } + + private void assertUniqueOutputTypes(Set units) { + indexTypes(units, "output"); + } + + private void reserve(Map identities, String identity, String owner) { + String conflict = identities.putIfAbsent(identity, owner); + if (conflict != null) { + throw new CodeAdaptationException( + "Cannot produce '" + identity + "' because it is already provided by " + conflict); + } + } + + private Set cloneUnits(Set units) { + LinkedHashSet result = new LinkedHashSet<>(); + ordered(units).forEach(unit -> result.add(unit.deepClone())); + return result; + } + + private List ordered(Set units) { + return units.stream() + .sorted( + Comparator.comparing(this::unitIdentity) + .thenComparing(AdapterUtils::getFileName)) + .toList(); + } + + private String unitIdentity(ASTOrdinaryCompilationUnit unit) { + return packageName(unit) + + ":" + + unit.getTypeDeclarationList().stream() + .map(ASTTypeDeclaration::getName) + .sorted() + .reduce((left, right) -> left + "+" + right) + .orElse(""); + } + + private String packageName(ASTOrdinaryCompilationUnit unit) { + return unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + } + + private String qualify(String packageName, String simpleName) { + return packageName.isEmpty() ? simpleName : packageName + "." + simpleName; + } + + private String simpleTypeName(ASTMCType type) { + return JavaSourceNames.simpleName(JavaSourceNames.printNormalizedType(type)); + } + + private record TypeLocation(ASTOrdinaryCompilationUnit unit, ASTTypeDeclaration type) {} + + record CompositionResult( + Set code, Map topToPublicSelfTypes) {} + + private record CompositionEntry( + ASTOrdinaryCompilationUnit adaptedUnit, + ASTTypeDeclaration adaptedType, + ASTOrdinaryCompilationUnit concreteUnit, + ASTTypeDeclaration concreteType, + String concreteName, + String topName) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/AdaptationContextFactory.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/AdaptationContextFactory.java new file mode 100644 index 00000000..e3f84146 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/AdaptationContextFactory.java @@ -0,0 +1,128 @@ +package de.monticore.codeAdaption.context; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.cdconformance.CDConformanceChecker; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContextBuilder; +import de.monticore.codeAdaption.handler.multiIncarnation.ManualIncarnationContextBuilder; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.se_rwth.commons.logging.Log; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** + * Builds one incarnation context per requested mapping stereotype. + * + *

For example, mappings {@code [shop, billing]} produce a result map whose keys are {@code + * shop} and {@code billing}. Each value contains only the incarnations derived for that mapping; + * mappings are never combined into one context. + */ +public final class AdaptationContextFactory { + + private final Set confParams; + private final MappingConformanceService conformanceService; + + public AdaptationContextFactory( + Set confParams, MappingConformanceService conformanceService) { + this.confParams = Set.copyOf(confParams); + this.conformanceService = conformanceService; + } + + /** + * Builds mapping-name to adaptation-result entries in deterministic mapping-name order. + * + *

If {@code useConcretizationMappings} is {@code false}, every context is derived directly + * from stereotypes and deterministic name matches; the result has no checker. If it is {@code + * true}, each mapping is first checked independently. A successful checker result is retained + * only when it also supplies an incarnation mapping. Otherwise that one mapping falls back to + * manual derivation while other mappings may still use their checker results. + * + * @param referenceIndex indexed reference CD whose elements become mapping keys + * @param concreteIndex indexed concrete CD whose elements become incarnations + * @param mappings stereotype names to evaluate, for example {@code shop} and {@code billing} + * @param useConcretizationMappings whether to prefer usable conformance-checker incarnation + * mappings over manual stereotype derivation + * @return mapping stereotype name to its isolated context, optional checker, and checker-mapping + * availability flag + */ + public Map buildResults( + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + Set mappings, + boolean useConcretizationMappings) { + + ASTCDCompilationUnit refCD = referenceIndex.cd(); + ASTCDCompilationUnit conCD = concreteIndex.cd(); + + Map results = new LinkedHashMap<>(); + + if (!useConcretizationMappings) { + ManualIncarnationContextBuilder builder = + new ManualIncarnationContextBuilder(referenceIndex, concreteIndex, confParams); + mappings.stream() + .sorted() + .forEach( + mapping -> + results.put( + mapping, + new AdaptationContextResult( + builder.buildContextForMapping(mapping), null, false))); + return results; + } + + for (String mapping : mappings.stream().sorted().toList()) { + CDConformanceChecker checker = conformanceService.newChecker(); + boolean mappingValid = conformanceService.checkOrFalse(checker, conCD, refCD, mapping); + + if (!mappingValid) { + Log.warn( + "Mapping '" + + mapping + + "' failed conformance check - will use conflict-checked stereotype-based fallback for this mapping"); + } else { + Log.info( + "Mapping '" + + mapping + + "' passed conformance check with incarnation mapping: " + + (checker.getIncarnationMapping() != null + ? checker.getIncarnationMapping().getClass().getSimpleName() + : "null"), + "CodeAdapter"); + } + + boolean conformanceMappingAvailable = + mappingValid && checker.getIncarnationMapping() != null; + IncarnationContext context; + if (!conformanceMappingAvailable) { + context = + new ManualIncarnationContextBuilder(referenceIndex, concreteIndex, confParams) + .buildContextForMapping(mapping); + } else { + IncarnationContextBuilder builder = + new IncarnationContextBuilder(checker, referenceIndex, concreteIndex); + context = builder.buildContextForMapping(mapping, false); + } + results.put( + mapping, + new AdaptationContextResult( + context, + conformanceMappingAvailable ? checker : null, + conformanceMappingAvailable)); + } + + return results; + } + + /** + * Result for one mapping stereotype. + * + * @param context incarnations used by adaptation; always present, including after manual fallback + * @param checker checker retained only when it produced the context's usable incarnation mapping + * @param conformanceValid whether a usable conformance-checker incarnation mapping produced this + * context; {@code false} also covers a successful check that exposed no incarnation mapping + */ + public record AdaptationContextResult( + IncarnationContext context, CDConformanceChecker checker, boolean conformanceValid) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/ConcretizationService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/ConcretizationService.java new file mode 100644 index 00000000..37721d3e --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/ConcretizationService.java @@ -0,0 +1,74 @@ +package de.monticore.codeAdaption.context; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdconcretization.ConcretizationCompleter; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.se_rwth.commons.logging.Log; +import java.util.ArrayList; +import java.util.Set; + +/** Wraps cdconcretization setup and fail-quick handling for adaptation runs. */ +public final class ConcretizationService { + + private final Set confParams; + + public ConcretizationService(Set confParams) { + this.confParams = Set.copyOf(confParams); + } + + public ASTCDCompilationUnit completeConcreteCD( + ASTCDCompilationUnit conCD, ASTCDCompilationUnit refCD, Set mappings) { + return completeConcreteCDInPlace(conCD.deepClone(), refCD, mappings); + } + + /** + * Completes a caller-owned working copy in place so its partial state remains available when + * cdconcretization reports an error. + */ + public ASTCDCompilationUnit completeConcreteCDInPlace( + ASTCDCompilationUnit completedCD, ASTCDCompilationUnit refCD, Set mappings) { + ConcretizationCompleter completer = new ConcretizationCompleter(confParams); + JavaLoader.initializeCDSymbolTable(completedCD); + synchronized (Log.class) { + boolean failQuickEnabled = Log.isFailQuickEnabled(); + int findingsBefore = Log.getFindings().size(); + long errorsBefore = Log.getErrorCount(); + try { + Log.enableFailQuick(false); + ArrayList orderedMappings = new ArrayList<>(mappings); + orderedMappings.sort(String::compareTo); + completer.completeCD(completedCD, refCD, orderedMappings); + // Completion mutates the cloned AST by adding and repairing model elements. Rebuild its + // symbols before conformance/context construction so added fields, methods, types and + // inheritance relationships are visible through the same completed snapshot. + JavaLoader.initializeCDSymbolTable(completedCD); + if (Log.getErrorCount() > errorsBefore) { + String diagnostics = + Log.getFindings().subList(findingsBefore, Log.getFindings().size()).stream() + .map(finding -> finding.getMsg()) + .distinct() + .reduce((left, right) -> left + "; " + right) + .orElse("unknown concretization error"); + throw new IllegalStateException( + "CD concretization reported errors; adaptation cannot continue safely: " + + diagnostics); + } + Log.info("Concretized concrete CD before code adaptation", "CodeAdapter"); + return completedCD; + } catch (Exception | AssertionError throwable) { + throw new IllegalStateException( + "CD concretization failed before code adaptation; the input concrete CD was not modified", + throwable); + } finally { + // Log.enableFailQuick(true) terminates the process when error findings exist. The errors + // produced by this guarded dependency call are represented by the exception above, so + // remove only those new findings before restoring the caller's global logging policy. + if (Log.getErrorCount() > errorsBefore && Log.getFindings().size() > findingsBefore) { + Log.getFindings().subList(findingsBefore, Log.getFindings().size()).clear(); + } + Log.enableFailQuick(failQuickEnabled); + } + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/GroupingMappingService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/GroupingMappingService.java new file mode 100644 index 00000000..9e80a12c --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/GroupingMappingService.java @@ -0,0 +1,64 @@ +package de.monticore.codeAdaption.context; + +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Derives Java type-name replacements for groups of concrete incarnations. + * + *

An incarnation is a concrete CD type to which a reference type is mapped. If one reference + * type has several incarnations, those incarnations form an incarnation group. Java declarations + * that must work for the entire group cannot use the type of only one incarnation. They instead + * use a common class or interface from the concrete CD that represents the group; this class calls + * that common type the grouping type. + */ +public final class GroupingMappingService { + + /** + * Computes replacements for one mapping only, preventing grouping decisions from leaking into + * another mapping. + * + *

Each entry maps the simple type name of one concrete incarnation to the simple name of the + * common type representing its incarnation group. The code updater uses these replacements in + * shared declarations such as fields, parameters, return types, and collection element types, so + * that the resulting Java declaration can hold or accept every incarnation in the group. + * + *

For example, assume the reference type {@code Payment} has the concrete incarnations {@code + * CreditCard} and {@code Invoice}. If both are represented by the common concrete interface + * {@code PaymentMethod}, this method returns {@code {CreditCard=PaymentMethod, + * Invoice=PaymentMethod}}. A shared parameter can then be written as {@code PaymentMethod payment} + * instead of being tied to only {@code CreditCard} or {@code Invoice}. + * + *

Identity replacements, such as {@code PaymentMethod -> PaymentMethod}, are omitted because + * they do not require a Java type rewrite. + * + * @param context the mapping-local incarnation and grouping context + * @return insertion-ordered incarnation-simple-name to grouping-type-simple-name replacements + * @throws IllegalStateException if one incarnation simple name resolves to different grouping + * types + */ + public Map compute(IncarnationContext context) { + Map result = new LinkedHashMap<>(); + context.getGroupingMappings().entrySet().stream() + .sorted(Map.Entry.comparingByKey(java.util.Comparator.comparing(key -> key.signature()))) + .forEach( + entry -> { + String concrete = entry.getKey().getName(); + String grouping = entry.getValue().key().getName(); + if (!grouping.equals(concrete)) { + putUnambiguous(result, concrete, grouping); + } + }); + return result; + } + + /** Adds one incarnation-to-grouping-type rewrite while rejecting ambiguous simple names. */ + private static void putUnambiguous(Map result, String concrete, String grouping) { + String previous = result.putIfAbsent(concrete, grouping); + if (previous != null && !previous.equals(grouping)) { + throw new IllegalStateException( + "Ambiguous grouping for concrete type '" + concrete + "': '" + previous + "' and '" + grouping + "'"); + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/MappingConformanceService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/MappingConformanceService.java new file mode 100644 index 00000000..97ff9e2d --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/context/MappingConformanceService.java @@ -0,0 +1,65 @@ +package de.monticore.codeAdaption.context; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.cdconformance.CDConformanceChecker; +import de.se_rwth.commons.logging.Log; +import java.util.List; +import java.util.Set; + +/** Creates and runs conformance checkers with the adapter's fail-quick policy. */ +public final class MappingConformanceService { + + private final Set confParams; + + public MappingConformanceService(Set confParams) { + this.confParams = Set.copyOf(confParams); + } + + public CDConformanceChecker newChecker() { + return new CDConformanceChecker(confParams); + } + + public boolean checkOrFalse( + CDConformanceChecker checker, + ASTCDCompilationUnit conCD, + ASTCDCompilationUnit refCD, + String mapping) { + boolean valid = false; + Throwable failure = null; + List diagnostics; + synchronized (Log.class) { + boolean failQuickEnabled = Log.isFailQuickEnabled(); + int findingsBefore = Log.getFindings().size(); + try { + Log.enableFailQuick(false); + valid = checker.checkConformance(conCD, refCD, mapping); + } catch (RuntimeException | AssertionError throwable) { + failure = throwable; + } finally { + diagnostics = + Log.getFindings().subList(findingsBefore, Log.getFindings().size()).stream() + .map(finding -> finding.getMsg()) + .distinct() + .toList(); + // The guarded dependency call is represented by the boolean result and diagnostics below. + // Do not leak its findings into the caller's process-global logging state. + Log.getFindings().subList(findingsBefore, Log.getFindings().size()).clear(); + Log.enableFailQuick(failQuickEnabled); + } + } + if (failure != null) { + Log.warn( + "Conformance checker threw during check for mapping '" + + mapping + + "': " + + failure.getMessage() + + " - will use validated stereotype-based fallback"); + } + diagnostics.forEach( + diagnostic -> + Log.warn( + "Conformance check for mapping '" + mapping + "': " + diagnostic)); + return valid; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/dependency/ReferenceCodeDependencySelector.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/dependency/ReferenceCodeDependencySelector.java new file mode 100644 index 00000000..78db89ec --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/dependency/ReferenceCodeDependencySelector.java @@ -0,0 +1,49 @@ +package de.monticore.codeAdaption.dependency; + +import de.monticore.codeAdaption.updater.spoonUpdater.SpoonReferenceCodeDependencyAnalyzer; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.nio.file.Path; +import java.util.Objects; +import java.util.Set; + +/** + * Facade for selecting the transitive Java-only helper closure of mapped + * reference types. + * + *

The complete source graph is built once. Call {@link #select(Set)} for each mapping-specific + * set of mapped Java roots. Parser-specific implementation objects never escape this facade. + */ +public final class ReferenceCodeDependencySelector { + private final SpoonReferenceCodeDependencyAnalyzer analyzer; + + /** + * Builds a dependency graph from one immutable reference-source snapshot. + * + * @param sourceRoot root containing the same Java files represented by {@code parsedUnits} + * @param parsedUnits JavaDSL AST snapshot also supplied to helper-aware validation + * @param referenceCDTypeKeys simple reference-CD type names used to plan incarnation passes + */ + public ReferenceCodeDependencySelector( + Path sourceRoot, + Set parsedUnits, + Set referenceCDTypeKeys) { + Objects.requireNonNull(sourceRoot, "sourceRoot"); + Objects.requireNonNull(parsedUnits, "parsedUnits"); + Objects.requireNonNull(referenceCDTypeKeys, "referenceCDTypeKeys"); + analyzer = + new SpoonReferenceCodeDependencyAnalyzer( + sourceRoot, parsedUnits, referenceCDTypeKeys); + } + + /** + * Selects mapped roots and every transitively required source-local helper. + * + * @param mappedRootTypeIdentities package-qualified names are preferred; an unqualified name is + * accepted only when it identifies exactly one source type + * @throws de.monticore.codeAdaption.CodeAdaptationException if a root or reachable source-local + * dependency is ambiguous + */ + public ReferenceCodeSelection select(Set mappedRootTypeIdentities) { + return analyzer.select(mappedRootTypeIdentities); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/dependency/ReferenceCodeSelection.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/dependency/ReferenceCodeSelection.java new file mode 100644 index 00000000..9b7a5c9a --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/dependency/ReferenceCodeSelection.java @@ -0,0 +1,97 @@ +package de.monticore.codeAdaption.dependency; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * Immutable, parser-independent description of the reference Java retained for one mapping. + * + *

All Java type identities are package-qualified top-level names. Nested, local, and anonymous + * declarations are represented by their owning top-level identity because they cannot be retained + * independently of that source unit. + */ +public final class ReferenceCodeSelection { + private final Set rootTypeIdentities; + private final Set helperTypeIdentities; + private final Map> dependencyEdges; + private final Map> referencedCDTypeKeys; + private final List diagnostics; + + public ReferenceCodeSelection( + Set rootTypeIdentities, + Set helperTypeIdentities, + Map> dependencyEdges, + Map> referencedCDTypeKeys, + List diagnostics) { + this.rootTypeIdentities = immutableSortedSet(rootTypeIdentities); + this.helperTypeIdentities = immutableSortedSet(helperTypeIdentities); + this.dependencyEdges = immutableSortedMap(dependencyEdges); + this.referencedCDTypeKeys = immutableSortedMap(referencedCDTypeKeys); + List sortedDiagnostics = new ArrayList<>(diagnostics); + Collections.sort(sortedDiagnostics); + this.diagnostics = List.copyOf(sortedDiagnostics); + } + + /** Package-qualified mapped top-level Java types from which selection started. */ + public Set rootTypeIdentities() { + return rootTypeIdentities; + } + + /** Package-qualified Java-only types reached transitively from the mapped roots. */ + public Set helperTypeIdentities() { + return helperTypeIdentities; + } + + /** All retained top-level types, including roots and helpers. */ + public Set selectedTypeIdentities() { + Set selected = new LinkedHashSet<>(rootTypeIdentities); + selected.addAll(helperTypeIdentities); + return Collections.unmodifiableSet(selected); + } + + /** Directed source-local dependency edges, restricted to retained source types. */ + public Map> dependencyEdges() { + return dependencyEdges; + } + + /** Reference-CD type keys used by each retained source type. */ + public Map> referencedCDTypeKeysBySourceType() { + return referencedCDTypeKeys; + } + + /** Union of reference-CD type keys used anywhere in the retained closure. */ + public Set referencedCDTypeKeys() { + Set result = new TreeSet<>(); + referencedCDTypeKeys.values().forEach(result::addAll); + return Collections.unmodifiableSet(result); + } + + /** Deterministically ordered non-fatal diagnostics produced while selecting the closure. */ + public List diagnostics() { + return diagnostics; + } + + public boolean contains(String qualifiedTopLevelType) { + return rootTypeIdentities.contains(qualifiedTopLevelType) + || helperTypeIdentities.contains(qualifiedTopLevelType); + } + + private static Set immutableSortedSet(Set values) { + return Collections.unmodifiableSet(new LinkedHashSet<>(new TreeSet<>(values))); + } + + private static Map> immutableSortedMap(Map> values) { + Map> result = new LinkedHashMap<>(); + for (Map.Entry> entry : new TreeMap<>(values).entrySet()) { + result.put(entry.getKey(), immutableSortedSet(entry.getValue())); + } + return Collections.unmodifiableMap(result); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/BasicUpdateHandler.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/BasicUpdateHandler.java index 8a325491..b08e811e 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/BasicUpdateHandler.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/BasicUpdateHandler.java @@ -1,69 +1,80 @@ package de.monticore.codeAdaption.handler; -import de.monticore.cdbasis._ast.ASTCDAttribute; -import de.monticore.cdbasis._ast.ASTCDCompilationUnit; -import de.monticore.cdbasis._ast.ASTCDType; import de.monticore.cdbasis._symboltable.CDTypeSymbol; import de.monticore.cdconformance.CDConformanceChecker; -import de.monticore.cddiff.CDDiffUtil; -import de.monticore.codeAdaption.matcher.*; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.matcher.CodeMatching; import de.monticore.codeAdaption.updater.CodeUpdater; +import de.monticore.codeAdaption.utils.CDModelIndex; import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; import de.monticore.codeAdaption.validator.CodeValidator; import de.monticore.java.javadsl.JavaDSLMill; -import de.monticore.java.javadsl._ast.ASTFieldDeclaration; -import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; -import de.monticore.java.javadsl._ast.ASTTypeDeclaration; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; -import de.monticore.javalight._ast.ASTMethodDeclaration; -import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; import de.monticore.symboltable.ISymbol; -import de.monticore.types.mcbasictypes._ast.ASTMCType; -import java.nio.file.Path; -import java.util.*; -// TODO: 20.09.2023 handle concrete handwritten-code - -/*** - * this class is to handle is to handle the update operation. - * - - * 1- iterate over all adaptable code-elements (Type, field, method,local variable,formal parameters-names). - * 2- get the match of each element in the reference class diagram. - * 3- compute the concrete value of each element form the matching using the conformance checker. - * 4- use the updater to update the value in the code. - * - - * this handler assumes that each reference element has a unique incarnation. - */ -public class BasicUpdateHandler { - protected ASTCDCompilationUnit conCD; - protected ASTCDCompilationUnit refCD; - protected CDConformanceChecker checker; - protected CodeUpdater updater; - - protected CodeValidator validator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** Coordinates the CD-based adaptation of Java AST elements. */ +public final class BasicUpdateHandler { + final CDModelIndex conIndex; + final CDModelIndex inputConIndex; + final CDModelIndex refIndex; + final CDConformanceChecker checker; + final CodeUpdater updater; + final CodeValidator validator; + + /** Optional incarnation context for stereotype-based mapping when conformance is skipped. */ + final IncarnationContext incarnationContext; + + /** Stable concrete choices for the current isolated pass. Empty for the ordinary case. */ + final Map incarnationSelection; + + final boolean useCommonParentForMultipleIncarnations; + + private final ConcreteSymbolResolver symbolResolver; + private final JavaMemberUpdateService memberUpdates; + private final JavaTypeUpdateService typeUpdates; public BasicUpdateHandler( - ASTCDCompilationUnit refCD, - ASTCDCompilationUnit conCD, - Path conHwcPath, + CDModelIndex refIndex, + CDModelIndex conIndex, + CDModelIndex inputConIndex, CDConformanceChecker checker, CodeUpdater updater, - CodeValidator validator) { + CodeValidator validator, + IncarnationContext incarnationContext, + Map incarnationSelection, + boolean useCommonParentForMultipleIncarnations) { this.updater = updater; this.checker = checker; - this.conCD = conCD; - this.refCD = refCD; + this.conIndex = conIndex; + this.inputConIndex = inputConIndex; + this.refIndex = refIndex; this.validator = validator; + this.incarnationContext = incarnationContext; + this.incarnationSelection = Map.copyOf(incarnationSelection); + this.useCommonParentForMultipleIncarnations = useCommonParentForMultipleIncarnations; + this.symbolResolver = + new ConcreteSymbolResolver(this, refIndex.cd(), conIndex.cd()); + this.memberUpdates = + new JavaMemberUpdateService(validator, updater, refIndex, conIndex, symbolResolver); + this.typeUpdates = new JavaTypeUpdateService(this, symbolResolver, memberUpdates); } public void handleUpdate(Set javaFiles) { + validateIncarnationContext(); + typeUpdates.beginRun(); + validator.initializeTypeMatcher(javaFiles); - // Collect elements of each type Set typeElements = new LinkedHashSet<>(); for (ASTOrdinaryCompilationUnit ast : javaFiles) { - - // collect code elements JavaAstElemCollector collector = new JavaAstElemCollector(); JavaDSLTraverser traverser = JavaDSLMill.traverser(); traverser.add4JavaDSL(collector); @@ -71,126 +82,79 @@ public void handleUpdate(Set javaFiles) { typeElements.add(collector); } - // update local variable and method parameters - typeElements.forEach(this::handleVariableUpdate); - - // update methods and attributes - typeElements.forEach(this::handleTMemberUpdate); - - // update types - typeElements.forEach(this::handleTypeUpdate); - + typeElements.forEach(memberUpdates::handleVariableUpdate); + typeElements.forEach(memberUpdates::handleMemberUpdate); + typeElements.forEach(memberUpdates::handleAssociationRoleUpdate); + typeElements.forEach(typeUpdates::handleTypeUpdate); + typeElements.forEach(typeUpdates::projectCompletedMembers); updater.printCode(); } - protected void handleTypeUpdate(JavaAstElemCollector collector) { - // update type present in the reference code - for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { - Optional matching = validator.getMatchedType(type); - if (matching.isPresent() && matching.get().mustBePerform()) { - String newName = buildConcreteName(matching.get()); - updater.updateType(type, newName); - } - } - - // update type not present in the reference code - for (ASTCDType cdType : CDDiffUtil.getAllCDTypes(refCD)) { - String newName = getConTypeSymbol(cdType.getSymbol()).getName(); - updater.updateCDType(cdType, newName); + private void validateIncarnationContext() { + if (incarnationContext == null) { + return; } - } - - protected void handleTMemberUpdate(JavaAstElemCollector collector) { - for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { - - // update method names - for (ASTMethodDeclaration method : collector.getAllMethodDeclarations(type)) { - Optional matching = validator.getMatchedMethod(type, method); - if (matching.isPresent() && matching.get().mustBePerform()) { - String newName = buildConcreteName(matching.get()); - updater.updateMethod(type, method, newName); - } + for (Map.Entry> entry : + incarnationContext.getMappings().entrySet()) { + if (entry.getKey().getKind() == StableElementKey.Kind.METHOD + && entry.getValue().stream().map(value -> value.key().signature()).distinct().count() + != entry.getValue().size()) { + throw new IllegalStateException( + "Conflicting method incarnations for " + entry.getKey().signature()); } - - // update type parameters names - for (ASTFieldDeclaration field : collector.getAllFieldDeclarations(type)) { - Optional matching = validator.getMatchedField(type, field); - if (matching.isPresent() && matching.get().mustBePerform()) { - String newName = buildConcreteName(matching.get()); - updater.updateField(type, field, newName); - } + if (entry.getKey().getKind() != StableElementKey.Kind.FIELD) { + continue; } - - // update type parameters names - for (ASTMCType supertype : collector.getAllFSuperTypeDeclarations(type)) { - Optional matching = validator.getMatchedSupertype(type, supertype); - if (matching.isPresent() && matching.get().mustBePerform()) { - String newName = buildConcreteName(matching.get()); - updater.updateSuperType(type, supertype, newName); + Map ownersAndTypes = new LinkedHashMap<>(); + for (IncarnationContext.MappedElement mapped : entry.getValue()) { + StableElementKey target = mapped.key(); + String ownerAndName = target.getOwnerType().orElse("") + "." + target.getName(); + String fieldKind = target.getFieldKind().orElse(""); + String previous = ownersAndTypes.putIfAbsent(ownerAndName, fieldKind); + if (previous != null && !previous.equals(fieldKind)) { + throw new IllegalStateException( + "Conflicting field incarnations for " + entry.getKey().signature()); } } } } - protected void handleVariableUpdate(JavaAstElemCollector collector) { - for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { - for (ASTMethodDeclaration method : collector.getAllMethodDeclarations(type)) { - - // update local variables - for (ASTLocalVariableDeclaration var : collector.getAllLocVariables(type, method)) { - Optional matching = validator.getMatchedLocalVariable(type, method, var); - if (matching.isPresent() && matching.get().mustBePerform()) { - String newName = buildConcreteName(matching.get()); - updater.updateLocalVariable(type, method, var, newName); - } - } + /** Builds the concrete name for an element from the references in its matching. */ + String buildConcreteName(CodeMatching matching) { + return symbolResolver.buildConcreteName(matching); + } - // update all formal parameters - for (ASTFormalParameter param : collector.getAllParameters(type, method)) { - Optional matching = validator.getMatchedParameter(type, method, param); - if (matching.isPresent() && matching.get().mustBePerform()) { - String newName = buildConcreteName(matching.get()); - updater.updateMethodParameter(type, method, param, newName); - } - } - } - } + /** Resolves a reference symbol from the optional incarnation context. */ + Optional getSymbolFromContext(ISymbol refSymbol) { + return symbolResolver.getSymbolFromContext(refSymbol); } - /*** - * build concrete name of an element form the matching found in the class diagram - */ - private String buildConcreteName(CodeMatching codeMatching) { - List conReferences = new ArrayList<>(); - - // resolve concrete references - for (ISymbol refSymbol : codeMatching.getReferences()) { - if (refSymbol instanceof CDTypeSymbol) { - conReferences.add(getConTypeSymbol((CDTypeSymbol) refSymbol)); - } else { - conReferences.add(getConAttributeSymbol((FieldSymbol) refSymbol)); - } + Optional referenceKey(ISymbol symbol) { + return StableElementKey.fromSymbol(symbol, refIndex); + } + + Optional concreteKey(ISymbol symbol) { + return StableElementKey.fromSymbol(symbol, conIndex); + } + + List mappedIncarnations(ISymbol reference) { + if (incarnationContext == null) { + return List.of(); } - // fill the template with the references - return MatcherHelper.fillTemplate(codeMatching.getTemplate(), conReferences); + return referenceKey(reference) + .map(incarnationContext::getIncarnations) + .orElseGet(List::of); } - protected ISymbol getConTypeSymbol(CDTypeSymbol symbol) { - return checker - .getIncarnationMapping() - .getIncarnations(symbol.getAstNode()) - .iterator() - .next() - .getSymbol(); + ISymbol getConTypeSymbol(CDTypeSymbol symbol) { + return symbolResolver.getConTypeSymbol(symbol); } - protected ISymbol getConAttributeSymbol(FieldSymbol symbol) { + ISymbol getConAttributeSymbol(FieldSymbol symbol) { + return symbolResolver.getConAttributeSymbol(symbol); + } - return checker - .getIncarnationMapping() - .getIncarnations((ASTCDAttribute) symbol.getAstNode()) - .iterator() - .next() - .getSymbol(); + ISymbol getConMethodSymbol(ISymbol symbol) { + return symbolResolver.getConMethodSymbol(symbol); } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/ConcreteSymbolResolver.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/ConcreteSymbolResolver.java new file mode 100644 index 00000000..611d9b13 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/ConcreteSymbolResolver.java @@ -0,0 +1,483 @@ +package de.monticore.codeAdaption.handler; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdbasis._symboltable.CDTypeSymbol; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.matcher.CodeMatching; +import de.monticore.codeAdaption.matcher.MatcherHelper; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symboltable.ISymbol; +import de.se_rwth.commons.logging.Log; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** Resolves reference-CD symbols, signatures, and Java type names to concrete counterparts. */ +final class ConcreteSymbolResolver { + private final BasicUpdateHandler handler; + private final Map> concreteImportedTypes; + private final Set wildcardImportPackages; + + ConcreteSymbolResolver( + BasicUpdateHandler handler, + ASTCDCompilationUnit referenceCD, + ASTCDCompilationUnit concreteCD) { + this.handler = handler; + this.concreteImportedTypes = importedTypes(concreteCD, referenceCD); + this.wildcardImportPackages = wildcardImportPackages(concreteCD, referenceCD); + } + + String buildConcreteName(CodeMatching matching) { + List concreteReferences = new ArrayList<>(); + for (ISymbol reference : matching.getReferences()) { + Optional fromContext = handler.getSymbolFromContext(reference); + if (fromContext.isPresent()) { + concreteReferences.add(fromContext.get()); + } else if (reference instanceof CDTypeSymbol type) { + concreteReferences.add(handler.getConTypeSymbol(type)); + } else if (reference instanceof FieldSymbol field) { + concreteReferences.add(handler.getConAttributeSymbol(field)); + } else { + concreteReferences.add(handler.getConMethodSymbol(reference)); + } + } + String generationTemplate = matching.getGenerateTemplate(); + return MatcherHelper.fillTemplate( + generationTemplate != null && !generationTemplate.isEmpty() + ? generationTemplate + : matching.getTemplate(), + concreteReferences); + } + + Optional getSymbolFromContext(ISymbol reference) { + if (handler.incarnationContext == null) { + return Optional.empty(); + } + Optional selected = selectedIncarnation(reference); + if (selected.isPresent()) { + return selected.map(IncarnationContext.MappedElement::symbol); + } + List mappedIncarnations = + handler.mappedIncarnations(reference); + if (mappedIncarnations.isEmpty()) { + return Optional.empty(); + } + List incarnations = + mappedIncarnations.stream().map(IncarnationContext.MappedElement::symbol).toList(); + if (reference instanceof CDTypeSymbol && handler.useCommonParentForMultipleIncarnations) { + Optional commonParent = findCommonParent(incarnations); + if (commonParent.isPresent()) { + return commonParent; + } + for (ISymbol incarnation : incarnations) { + Optional grouping = groupingName(incarnation.getName()); + if (grouping.isPresent()) { + Optional groupingSymbol = findContextSymbolByName(grouping.get()); + if (groupingSymbol.isPresent()) { + return groupingSymbol; + } + } + } + } + return Optional.of(incarnations.get(0)); + } + + private Optional selectedIncarnation(ISymbol reference) { + Optional referenceKey = handler.referenceKey(reference); + if (referenceKey.isEmpty()) { + return Optional.empty(); + } + IncarnationContext.MappedElement direct = + handler.incarnationSelection.get(referenceKey.get()); + if (direct != null) { + return Optional.of(direct); + } + Optional referenceOwner = referenceKey.get().getOwnerType(); + if (referenceOwner.isEmpty()) { + return Optional.empty(); + } + IncarnationContext.MappedElement selectedOwner = + handler.incarnationSelection.get(StableElementKey.type(referenceOwner.get())); + if (selectedOwner == null) { + return Optional.empty(); + } + return handler.mappedIncarnations(reference).stream() + .filter( + incarnation -> + incarnation + .key() + .getOwnerType() + .filter(selectedOwner.key().getName()::equals) + .isPresent()) + .findFirst(); + } + + ISymbol getConTypeSymbol(CDTypeSymbol symbol) { + if (handler.checker == null || handler.checker.getIncarnationMapping() == null) { + Log.warn("No conformance incarnation mapping available for type " + symbol.getName()); + return symbol; + } + var incarnations = handler.checker.getIncarnationMapping().getIncarnations(symbol.getAstNode()); + if (incarnations != null && incarnations.iterator().hasNext()) { + return incarnations.iterator().next().getSymbol(); + } + for (ASTCDType referenceType : handler.refIndex.types()) { + if (referenceType.getName().equals(symbol.getName())) { + incarnations = handler.checker.getIncarnationMapping().getIncarnations(referenceType); + if (incarnations != null && incarnations.iterator().hasNext()) { + return incarnations.iterator().next().getSymbol(); + } + break; + } + } + Log.warn("No incarnation found for type " + symbol.getName() + "; using reference symbol"); + return symbol; + } + + ISymbol getConAttributeSymbol(FieldSymbol symbol) { + if (handler.checker == null || handler.checker.getIncarnationMapping() == null) { + Log.warn("No conformance incarnation mapping available for attribute " + symbol.getName()); + return symbol; + } + if (!(symbol.getAstNode() instanceof ASTCDAttribute attribute)) { + throw new IllegalStateException( + "Field symbol " + symbol.getName() + " has no ASTCDAttribute node"); + } + var incarnations = handler.checker.getIncarnationMapping().getIncarnations(attribute); + if (incarnations != null && incarnations.iterator().hasNext()) { + return incarnations.iterator().next().getSymbol(); + } + Log.warn("No incarnation found for attribute " + symbol.getName() + "; using reference symbol"); + return symbol; + } + + ISymbol getConMethodSymbol(ISymbol symbol) { + if (handler.checker != null + && handler.checker.getIncarnationMapping() != null + && symbol.getAstNode() instanceof ASTCDMethod method) { + var incarnations = handler.checker.getIncarnationMapping().getIncarnations(method); + if (incarnations != null && incarnations.iterator().hasNext()) { + return incarnations.iterator().next().getSymbol(); + } + } + Log.warn("No incarnation found for method " + symbol.getName() + "; using reference symbol"); + return symbol; + } + + Optional resolveConcreteMethodSymbol(CodeMatching matching) { + for (ISymbol reference : matching.getReferences()) { + if (!(reference.getAstNode() instanceof ASTCDMethod)) { + continue; + } + ISymbol concrete = + handler.getSymbolFromContext(reference).orElseGet(() -> handler.getConMethodSymbol(reference)); + if (concrete != reference && concrete.getAstNode() instanceof ASTCDMethod) { + return Optional.of(concrete); + } + } + return Optional.empty(); + } + + void registerConcreteMethodSignature( + de.monticore.java.javadsl._ast.ASTTypeDeclaration sourceOwner, + ISymbol reference, + ISymbol concrete) { + if (concrete == null || !(concrete.getAstNode() instanceof ASTCDMethod concreteMethod)) { + return; + } + if (handler.incarnationContext != null) { + Optional referenceKey = handler.referenceKey(reference); + Optional concreteKey = handler.concreteKey(concrete); + if (referenceKey.isPresent() && concreteKey.isPresent()) { + List qualifiedParameterTypes = + concreteMethod.getCDParameterList().stream() + .map( + parameter -> + qualifyCdType(JavaSourceNames.printNormalizedType(parameter.getMCType()))) + .toList(); + StableElementKey qualifiedConcreteKey = + StableElementKey.method( + concreteKey.get().getOwnerType().orElse(null), + concreteKey.get().getName(), + qualifiedParameterTypes, + concreteKey.get().getReturnType().orElse(null)); + handler.updater.registerMethodRewrite( + sourceOwner, referenceKey.get(), qualifiedConcreteKey); + } + } + handler.updater.registerConcreteMethodSignature( + concreteMethod.getName(), + concreteMethod.getCDParameterList().stream() + .map(parameter -> qualifyCdType(JavaSourceNames.printNormalizedType(parameter.getMCType()))) + .toList()); + } + + Optional findConcreteMethod( + String concreteOwner, + String concreteMethodName, + List referenceParameters) { + List candidates = + handler.conIndex.methods(concreteOwner, concreteMethodName).stream() + .filter(method -> method.getCDParameterList().size() == referenceParameters.size()) + .toList(); + if (candidates.size() <= 1) { + return candidates.stream().findFirst(); + } + List expectedTypes = + referenceParameters.stream() + .map(parameter -> concreteType(JavaSourceNames.printNormalizedType(parameter.getMCType()))) + .toList(); + List exact = + candidates.stream() + .filter(method -> normalizedParameterTypes(method).equals(expectedTypes)) + .toList(); + if (exact.size() == 1) { + return Optional.of(exact.get(0)); + } + Log.warn( + "Ambiguous concrete overload " + + concreteOwner + + "." + + concreteMethodName + + "(" + + String.join(",", expectedTypes) + + ")"); + return Optional.empty(); + } + + String resolveConcreteTypeName(String referenceTypeName) { + Optional referenceType = handler.refIndex.type(JavaSourceNames.simpleName(referenceTypeName)); + if (referenceType.isPresent()) { + ISymbol resolved = + handler + .getSymbolFromContext(referenceType.get().getSymbol()) + .orElseGet(() -> handler.getConTypeSymbol(referenceType.get().getSymbol())); + if (resolved.getName().equals(referenceType.get().getName()) + && handler.incarnationContext != null) { + Optional concreteAlternative = + handler.mappedIncarnations(referenceType.get().getSymbol()).stream() + .map(element -> element.key().getName()) + .filter(name -> !name.equals(referenceType.get().getName())) + .filter(handler.conIndex::hasType) + .sorted() + .findFirst(); + if (concreteAlternative.isPresent()) { + return concreteAlternative.get(); + } + } + return resolved.getName(); + } + return handler.conIndex.type(JavaSourceNames.simpleName(referenceTypeName)).isPresent() + ? JavaSourceNames.simpleName(referenceTypeName) + : referenceTypeName; + } + + Optional findConcreteType(String typeName) { + return handler.conIndex.type(JavaSourceNames.simpleName(typeName)); + } + + String qualifyCdType(String type) { + return JavaSourceNames.replaceTypeNames( + type, + reference -> { + if (reference.qualified()) { + return Optional.empty(); + } + String simpleName = reference.simpleName(); + if (handler.conIndex.type(simpleName).isPresent() + || handler.refIndex.type(simpleName).isPresent()) { + return Optional.empty(); + } + Set candidates = concreteImportedTypes.get(simpleName); + if (candidates == null || candidates.isEmpty()) { + LinkedHashSet classpathCandidates = new LinkedHashSet<>(); + String javaLang = "java.lang." + simpleName; + if (classpathTypeExists(javaLang)) { + classpathCandidates.add(javaLang); + } + for (String packageName : wildcardImportPackages) { + String candidate = packageName + "." + simpleName; + if (classpathTypeExists(candidate)) { + classpathCandidates.add(candidate); + } + } + candidates = classpathCandidates; + } + if (candidates.size() == 1) { + return Optional.of(candidates.iterator().next()); + } + if (candidates.isEmpty()) { + return Optional.empty(); + } + throw new IllegalStateException( + "Ambiguous class-diagram imports for used type '" + + simpleName + + "': " + + candidates.stream().sorted().collect(java.util.stream.Collectors.joining(", "))); + }); + } + + private static boolean classpathTypeExists(String qualifiedName) { + try { + Class.forName(qualifiedName, false, ConcreteSymbolResolver.class.getClassLoader()); + return true; + } catch (ClassNotFoundException | LinkageError ignored) { + return false; + } + } + + String resolveConcreteCdType(String type) { + return qualifyCdType(concreteType(type)); + } + + String replaceConcreteWithGroupingType(String rawType) { + if (rawType == null + || rawType.isEmpty() + || handler.incarnationContext == null + || !handler.useCommonParentForMultipleIncarnations) { + return rawType; + } + return JavaSourceNames.replaceSimpleTypeNames( + rawType, + simple -> { + Optional direct = groupingName(simple); + if (direct.isPresent()) { + return direct; + } + for (Map.Entry> entry : + handler.incarnationContext.getMappings().entrySet()) { + if (entry.getKey().getName().equals(simple) && !entry.getValue().isEmpty()) { + return groupingName(entry.getValue().get(0).key().getName()); + } + } + return Optional.empty(); + }); + } + + private List normalizedParameterTypes(ASTCDMethod method) { + return method.getCDParameterList().stream() + .map(ASTCDParameter::getMCType) + .map(JavaSourceNames::printNormalizedType) + .toList(); + } + + private String concreteType(String type) { + return JavaSourceNames.replaceSimpleTypeNames( + type, + simple -> { + String concrete = resolveConcreteTypeName(simple); + return concrete.equals(simple) ? Optional.empty() : Optional.of(concrete); + }); + } + + private Optional findCommonParent(List incarnations) { + Set names = new HashSet<>(); + incarnations.forEach(symbol -> names.add(symbol.getName())); + for (String possibleGrouping : names) { + Optional groupingElement = + handler.incarnationContext.getGroupingMappings().values().stream() + .filter(element -> element.key().getName().equals(possibleGrouping)) + .findFirst(); + if (groupingElement.isEmpty()) { + continue; + } + Set implementers = + handler.incarnationContext.getGroupingMappings().entrySet().stream() + .filter(entry -> entry.getValue().key().getName().equals(possibleGrouping)) + .map(entry -> entry.getKey().getName()) + .filter(name -> !name.equals(possibleGrouping)) + .collect(java.util.stream.Collectors.toSet()); + Set remaining = new HashSet<>(names); + remaining.remove(possibleGrouping); + if (remaining.equals(implementers)) { + return Optional.of(groupingElement.get().symbol()); + } + } + return Optional.empty(); + } + + private Optional findContextSymbolByName(String name) { + Optional grouping = + handler.incarnationContext.getGroupingMappings().values().stream() + .filter(element -> element.key().getName().equals(name)) + .map(element -> element.symbol()) + .findFirst(); + if (grouping.isPresent()) { + return grouping; + } + for (Map.Entry> entry : + handler.incarnationContext.getMappings().entrySet()) { + Optional incarnation = + entry.getValue().stream() + .filter(element -> element.key().getName().equals(name)) + .map(element -> element.symbol()) + .findFirst(); + if (incarnation.isPresent()) { + return incarnation; + } + } + return Optional.empty(); + } + + private Optional groupingName(String concreteTypeName) { + return handler.incarnationContext + .getGroupingFor(StableElementKey.type(concreteTypeName)) + .map(grouping -> grouping.key().getName()); + } + + private static Map> importedTypes( + ASTCDCompilationUnit primary, ASTCDCompilationUnit fallback) { + Map> imports = importedTypes(fallback); + // Generated signatures describe concrete declarations, so concrete imports intentionally win + // over same-named reference imports. Ambiguities within the selected CD remain detectable. + importedTypes(primary).forEach(imports::put); + return imports; + } + + private static Map> importedTypes(ASTCDCompilationUnit cd) { + Map> imports = new LinkedHashMap<>(); + if (cd == null) { + return imports; + } + for (var statement : cd.getMCImportStatementList()) { + String imported = statement.getMCQualifiedName().getQName(); + if (!statement.isStar()) { + imports + .computeIfAbsent( + JavaSourceNames.simpleName(imported), ignored -> new LinkedHashSet<>()) + .add(imported); + } + } + return imports; + } + + private static Set wildcardImportPackages( + ASTCDCompilationUnit primary, ASTCDCompilationUnit fallback) { + LinkedHashSet packages = new LinkedHashSet<>(wildcardImportPackages(fallback)); + packages.addAll(wildcardImportPackages(primary)); + return Set.copyOf(packages); + } + + private static Set wildcardImportPackages(ASTCDCompilationUnit cd) { + if (cd == null) { + return Set.of(); + } + return cd.getMCImportStatementList().stream() + .filter(statement -> statement.isStar()) + .map(statement -> statement.getMCQualifiedName().getQName()) + .collect( + java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/JavaMemberUpdateService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/JavaMemberUpdateService.java new file mode 100644 index 00000000..978bc103 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/JavaMemberUpdateService.java @@ -0,0 +1,440 @@ +package de.monticore.codeAdaption.handler; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdassociation._ast.ASTCDAssociation; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdbasis._symboltable.CDTypeSymbol; +import de.monticore.codeAdaption.matcher.CodeMatching; +import de.monticore.codeAdaption.updater.CodeUpdater; +import de.monticore.codeAdaption.updater.CodeUpdater.MethodBodySpec; +import de.monticore.codeAdaption.utils.AdaptReference; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.codeAdaption.validator.CodeValidator; +import de.monticore.java.javadsl._ast.ASTFieldDeclaration; +import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; +import de.monticore.symboltable.ISymbol; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * Translates member-level matchings into {@link CodeUpdater} operations. + * + *

The service handles local variables, parameters, fields, methods, supertypes, and Java member + * names derived from CD association roles. It resolves model mappings through {@link + * ConcreteSymbolResolver}; it does not orchestrate complete adaptation passes or mutate Spoon + * directly. + */ +final class JavaMemberUpdateService { + private final CodeValidator validator; + private final CodeUpdater updater; + private final CDModelIndex referenceIndex; + private final CDModelIndex concreteIndex; + private final ConcreteSymbolResolver symbols; + + /** Creates the member update phase from its matching, update, model, and symbol dependencies. */ + JavaMemberUpdateService( + CodeValidator validator, + CodeUpdater updater, + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + ConcreteSymbolResolver symbols) { + this.validator = validator; + this.updater = updater; + this.referenceIndex = referenceIndex; + this.concreteIndex = concreteIndex; + this.symbols = symbols; + } + + /** + * Renames matched local variables and parameters. Parameters that lack their own matching are + * subsequently aligned with the mapped concrete CD method by position. + */ + void handleVariableUpdate(JavaAstElemCollector collector) { + for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { + for (ASTMethodDeclaration method : collector.getAllMethodDeclarations(type)) { + for (ASTLocalVariableDeclaration variable : collector.getAllLocVariables(type, method)) { + Optional matching = + validator.getMatchedLocalVariable(type, method, variable); + if (matching.isPresent() && matching.get().mustBePerform()) { + updater.updateLocalVariable( + type, method, variable, symbols.buildConcreteName(matching.get())); + } + } + + Set updated = + Collections.newSetFromMap(new IdentityHashMap<>()); + for (ASTFormalParameter parameter : collector.getAllParameters(type, method)) { + Optional matching = + validator.getMatchedParameter(type, method, parameter); + if (matching.isPresent() && matching.get().mustBePerform()) { + updater.updateMethodParameter( + type, method, parameter, symbols.buildConcreteName(matching.get())); + updated.add(parameter); + } + } + updateMethodParametersFromConcreteCD(type, method, collector, updated); + } + } + } + + /** + * Copies still-unmodified parameter names from the mapped concrete CD method. An explicit + * parameter matching always wins and is therefore listed in {@code alreadyUpdated}. + */ + void updateMethodParametersFromConcreteCD( + ASTTypeDeclaration type, + ASTMethodDeclaration method, + JavaAstElemCollector collector, + Set alreadyUpdated) { + List referenceParameters = collector.getAllParameters(type, method); + if (referenceParameters.isEmpty() + || referenceParameters.stream().allMatch(alreadyUpdated::contains)) { + return; + } + Optional matching = validator.getMatchedMethod(type, method); + if (matching.isEmpty() || !matching.get().mustBePerform()) { + return; + } + + Optional concreteMethod = + symbols + .resolveConcreteMethodSymbol(matching.get()) + .filter(symbol -> symbol.getAstNode() instanceof ASTCDMethod) + .map(symbol -> (ASTCDMethod) symbol.getAstNode()); + if (concreteMethod.isEmpty()) { + String concreteOwner = symbols.resolveConcreteTypeName(type.getName()); + String concreteName = symbols.buildConcreteName(matching.get()); + concreteMethod = + symbols.findConcreteMethod(concreteOwner, concreteName, referenceParameters); + } + if (concreteMethod.isEmpty()) { + return; + } + + List concreteParameters = concreteMethod.get().getCDParameterList(); + for (int i = 0; i < referenceParameters.size() && i < concreteParameters.size(); i++) { + ASTFormalParameter referenceParameter = referenceParameters.get(i); + if (!alreadyUpdated.contains(referenceParameter)) { + updater.updateMethodParameter( + type, method, referenceParameter, concreteParameters.get(i).getName()); + } + } + } + + /** + * Updates methods, fields, and supertypes of ordinary mapped types. Types carrying a generation + * template are skipped because their member lifecycle is handled by generation. + */ + void handleMemberUpdate(JavaAstElemCollector collector) { + for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { + Optional typeMatching = validator.getMatchedType(type); + if (typeMatching.isPresent() + && typeMatching.get().getGenerateTemplate() != null + && !typeMatching.get().getGenerateTemplate().isEmpty()) { + continue; + } + updateMethods(type, collector); + updateFields(type, collector); + updateSupertypes(type, collector); + } + } + + /** + * Renames methods whose signature still fits the concrete declaration and replaces methods whose + * concrete parameter count differs. Methods used as {@code forEach} templates are not rewritten + * as ordinary one-to-one methods. + */ + private void updateMethods(ASTTypeDeclaration type, JavaAstElemCollector collector) { + for (ASTMethodDeclaration method : collector.getAllMethodDeclarations(type)) { + Optional matching = validator.getMatchedMethod(type, method); + if (matching.isEmpty() || !matching.get().mustBePerform()) { + continue; + } + if (matching.get().getReferences().stream().anyMatch(this::isForEachTargetMethod)) { + continue; + } + + boolean hasMethodReference = false; + Optional concreteMethod = Optional.empty(); + ISymbol referenceMethod = null; + for (ISymbol reference : matching.get().getReferences()) { + if (isMethodReference(reference)) { + hasMethodReference = true; + Optional resolved = resolveMappedMethod(reference); + if (resolved.isPresent()) { + referenceMethod = reference; + concreteMethod = resolved; + break; + } + } + } + + if (concreteMethod.isPresent()) { + symbols.registerConcreteMethodSignature(type, referenceMethod, concreteMethod.get()); + ASTCDMethod concreteDeclaration = (ASTCDMethod) concreteMethod.get().getAstNode(); + List sourceParameters = collector.getAllParameters(type, method); + if (sourceParameters.size() != concreteDeclaration.getCDParameterList().size()) { + updater.addMethod( + type, + method, + concreteDeclaration.getName(), + concreteDeclaration.getCDParameterList().stream() + .map( + parameter -> + symbols.resolveConcreteCdType( + JavaSourceNames.printNormalizedType(parameter.getMCType()))) + .toList(), + concreteDeclaration.getCDParameterList().stream() + .map(ASTCDParameter::getName) + .toList(), + symbols.resolveConcreteCdType( + JavaSourceNames.printNormalizedReturnType(concreteDeclaration)), + concreteDeclaration.getModifier().isStatic(), + concreteDeclaration.getCDParameterList().size() < sourceParameters.size() + ? MethodBodySpec.safeDefault() + : MethodBodySpec.empty()); + updater.removeMethod(type, method); + } else if (!concreteMethod.get().getName().equals(referenceMethod.getName())) { + updater.updateMethod(type, method, concreteMethod.get().getName()); + } + } else if (!hasMethodReference) { + updater.updateMethod(type, method, symbols.buildConcreteName(matching.get())); + } + } + } + + /** Resolves a reference method first from the incarnation context and then from conformance. */ + private Optional resolveMappedMethod(ISymbol reference) { + Optional fromContext = symbols.getSymbolFromContext(reference); + if (fromContext.isPresent() && fromContext.get().getAstNode() instanceof ASTCDMethod) { + return fromContext; + } + ISymbol fromChecker = symbols.getConMethodSymbol(reference); + if (fromChecker != reference && fromChecker.getAstNode() instanceof ASTCDMethod) { + return Optional.of(fromChecker); + } + return Optional.empty(); + } + + /** Distinguishes method-like symbols from CD type and field symbols in matcher references. */ + private boolean isMethodReference(ISymbol reference) { + return reference.getAstNode() instanceof ASTCDMethod + || (!(reference instanceof CDTypeSymbol) && !(reference instanceof FieldSymbol)); + } + + /** Renames every matched field of one Java type. */ + private void updateFields(ASTTypeDeclaration type, JavaAstElemCollector collector) { + for (ASTFieldDeclaration field : collector.getAllFieldDeclarations(type)) { + Optional matching = validator.getMatchedField(type, field); + if (matching.isPresent() && matching.get().mustBePerform()) { + updater.updateField(type, field, symbols.buildConcreteName(matching.get())); + } + } + } + + /** Rewrites every matched superclass or implemented interface of one Java type. */ + private void updateSupertypes(ASTTypeDeclaration type, JavaAstElemCollector collector) { + for (ASTMCType supertype : collector.getAllFSuperTypeDeclarations(type)) { + Optional matching = validator.getMatchedSupertype(type, supertype); + if (matching.isPresent() && matching.get().mustBePerform()) { + updater.updateSuperType(type, supertype, symbols.buildConcreteName(matching.get())); + } + } + } + + /** + * Rewrites Java names derived from CD association roles. For each adapted reference owner/target + * pair, the method finds the corresponding role in the concrete CD and rejects ambiguous role + * names instead of choosing one arbitrarily. + * + *

For example, if {@code Person -> Address} uses role {@code addresses} and the mapped {@code + * Customer -> Location} association uses {@code locations}, accesses to {@code + * Customer.addresses} are rewritten to {@code Customer.locations}. + */ + void handleAssociationRoleUpdate(JavaAstElemCollector collector) { + for (ASTTypeDeclaration javaType : collector.getAllTypeDeclarations()) { + Map rewrites = new LinkedHashMap<>(); + for (ASTCDAssociation referenceAssociation : referenceIndex.associations()) { + for (AssociationRole referenceRole : associationRoles(referenceAssociation)) { + if (!adaptsReferenceType(javaType, referenceRole.ownerType())) { + continue; + } + String concreteOwner = resolveConcreteAssociationType(referenceRole.ownerType()); + String concreteTarget = resolveConcreteAssociationType(referenceRole.targetType()); + Set concreteRoles = new LinkedHashSet<>(); + for (ASTCDAssociation concreteAssociation : concreteIndex.associations()) { + for (AssociationRole concreteRole : associationRoles(concreteAssociation)) { + if (concreteOwner.equals(concreteRole.ownerType()) + && concreteTarget.equals(concreteRole.targetType())) { + concreteRoles.add(concreteRole.roleName()); + } + } + } + addRoleRewrite(rewrites, referenceRole, concreteRoles); + } + } + rewrites.forEach( + (source, concrete) -> updater.updateAssociationRole(javaType, source, concrete)); + } + } + + /** Adds one unambiguous reference-role to concrete-role rewrite to the per-type rewrite map. */ + private void addRoleRewrite( + Map rewrites, AssociationRole referenceRole, Set concreteRoles) { + if (concreteRoles.size() > 1) { + if (concreteRoles.contains(referenceRole.roleName())) { + rewrites.putIfAbsent(referenceRole.roleName(), referenceRole.roleName()); + return; + } + throw new IllegalStateException( + "Ambiguous concrete association roles for " + + referenceRole.ownerType() + + "." + + referenceRole.roleName() + + ": " + + concreteRoles); + } + if (concreteRoles.size() == 1) { + String concreteRole = concreteRoles.iterator().next(); + String previous = rewrites.putIfAbsent(referenceRole.roleName(), concreteRole); + if (previous != null && !previous.equals(concreteRole)) { + throw new IllegalStateException( + "Conflicting concrete association roles for " + + referenceRole.ownerType() + + "." + + referenceRole.roleName()); + } + } + } + + /** + * Returns whether a referenced method is the target of another reference method's + * {@code <>} stereotype and must therefore be handled by expansion. + */ + boolean isForEachTargetMethod(ISymbol reference) { + if (!(reference.getAstNode() instanceof ASTCDMethod targetMethod)) { + return false; + } + Optional targetOwner = referenceIndex.ownerOf(targetMethod); + if (targetOwner.isEmpty()) { + return false; + } + for (ASTCDType referenceType : referenceIndex.types()) { + for (ASTCDMethod method : referenceType.getCDMethodList()) { + Optional target = stereotypeValue(method, "forEach"); + if (target.isPresent() + && referencesMethod(target.get(), referenceType, targetOwner.get(), targetMethod)) { + return true; + } + } + } + return false; + } + + /** Returns whether the Java declaration represents the supplied reference CD type. */ + private boolean adaptsReferenceType(ASTTypeDeclaration javaType, String referenceType) { + Optional matching = validator.getMatchedType(javaType); + return matching + .map( + value -> + value.getReferences().stream() + .filter(CDTypeSymbol.class::isInstance) + .map(ISymbol::getName) + .anyMatch(referenceType::equals)) + .orElse(false) + || referenceType.equals(javaType.getName()); + } + + /** Resolves a reference association endpoint to its selected concrete type name. */ + private String resolveConcreteAssociationType(String referenceType) { + Optional type = referenceIndex.type(referenceType); + if (type.isEmpty()) { + return referenceType; + } + ISymbol concrete = + symbols + .getSymbolFromContext(type.get().getSymbol()) + .orElseGet(() -> symbols.getConTypeSymbol(type.get().getSymbol())); + return JavaSourceNames.simpleName(concrete.getName()); + } + + /** + * Converts the present left and right CD roles into owner/target/name triples. A role belongs to + * the opposite endpoint because it names navigation from that owner toward the role's endpoint. + */ + private List associationRoles(ASTCDAssociation association) { + String left = JavaSourceNames.simpleName(association.getLeftQualifiedName().getQName()); + String right = JavaSourceNames.simpleName(association.getRightQualifiedName().getQName()); + List roles = new ArrayList<>(); + if (association.getLeft().isPresentCDRole()) { + roles.add(new AssociationRole(right, left, association.getLeft().getCDRole().getName())); + } + if (association.getRight().isPresentCDRole()) { + roles.add(new AssociationRole(left, right, association.getRight().getCDRole().getName())); + } + return roles; + } + + /** + * Matches a {@code forEach} target expressed as either a local method name or an owner-qualified + * method name. + */ + private boolean referencesMethod( + String referenceName, + ASTCDType annotatedOwner, + ASTCDType targetOwner, + ASTCDMethod method) { + Optional parsed = AdaptReference.parse(referenceName); + if (parsed.isEmpty()) { + return false; + } + AdaptReference reference = parsed.get(); + if (!method.getName().equals(reference.memberName())) { + return false; + } + if (reference.isMethod() + && !JavaSourceNames.methodSignature(method) + .equals(reference.methodSignature().orElseThrow())) { + return false; + } + if (reference.owner().isEmpty()) { + return annotatedOwner.getName().equals(targetOwner.getName()); + } + return targetOwner.getName().equals(JavaSourceNames.simpleName(reference.owner().get())); + } + + /** Reads one stereotype value and treats malformed generated AST values as absent. */ + private Optional stereotypeValue(ASTCDMethod method, String name) { + if (method.getModifier() == null || !method.getModifier().isPresentStereotype()) { + return Optional.empty(); + } + for (var stereotype : method.getModifier().getStereotype().getValuesList()) { + if (name.equals(stereotype.getName())) { + try { + return Optional.ofNullable(stereotype.getValue()); + } catch (RuntimeException ignored) { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + /** Directional association role: navigation owner, target type, and generated Java name. */ + private record AssociationRole(String ownerType, String targetType, String roleName) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/JavaTypeUpdateService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/JavaTypeUpdateService.java new file mode 100644 index 00000000..6e7b7785 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/JavaTypeUpdateService.java @@ -0,0 +1,744 @@ +package de.monticore.codeAdaption.handler; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdbasis._symboltable.CDTypeSymbol; +import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; +import de.monticore.codeAdaption.matcher.CodeMatching; +import de.monticore.codeAdaption.updater.CodeUpdater.MethodBodySpec; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.CDTypeRelations; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.java.javadsl._ast.ASTFieldDeclaration; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.monticore.symboltable.ISymbol; +import de.se_rwth.commons.logging.Log; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** Adapts and generates Java types and projects members introduced by CD completion. */ +final class JavaTypeUpdateService { + private final BasicUpdateHandler handler; + private final ConcreteSymbolResolver symbols; + private final JavaMemberUpdateService memberUpdates; + private final Set generatedTypes = new HashSet<>(); + + JavaTypeUpdateService( + BasicUpdateHandler handler, + ConcreteSymbolResolver symbols, + JavaMemberUpdateService memberUpdates) { + this.handler = handler; + this.symbols = symbols; + this.memberUpdates = memberUpdates; + } + + void beginRun() { + generatedTypes.clear(); + } + + void handleTypeUpdate(JavaAstElemCollector collector) { + for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { + Optional matching = handler.validator.getMatchedType(type); + if (matching.isEmpty() || !matching.get().mustBePerform()) { + continue; + } + if (matching.get().getGenerateTemplate() != null + && !matching.get().getGenerateTemplate().isEmpty()) { + generateTypeFromTemplate(type, matching.get(), collector); + } else { + handler.updater.updateType(type, handler.buildConcreteName(matching.get())); + } + } + + for (ASTCDType cdType : handler.refIndex.types()) { + String concreteName = symbols.resolveConcreteTypeName(cdType.getName()); + handler.updater.updateCDType(cdType, concreteName); + } + } + + private void generateTypeFromTemplate( + ASTTypeDeclaration templateType, + CodeMatching typeMatching, + JavaAstElemCollector collector) { + String generatedTypeName = handler.buildConcreteName(typeMatching); + if (!generatedTypes.add(generatedTypeName)) { + return; + } + handler.updater.addType(templateType, generatedTypeName); + + List templateFields = collector.getAllFieldDeclarations(templateType); + List templateMethods = collector.getAllMethodDeclarations(templateType); + if (templateFields.isEmpty() || templateMethods.isEmpty()) { + // A plain type template (for example Observer -> Attacker) is still a complete source + // template. Builder expansion needs both a field and a method prototype, but the absence of + // either prototype must not erase the declarations that the renamed type already owns. + return; + } + + String concreteName = resolveGeneratedTargetTypeName(templateType, typeMatching); + List attributes = collectGeneratedAttributes(concreteName); + ASTFieldDeclaration fieldTemplate = templateFields.get(0); + ASTMethodDeclaration methodTemplate = templateMethods.get(0); + List buildArguments = new ArrayList<>(); + for (GeneratedAttribute attribute : attributes) { + String fieldName = attribute.name() + "Field"; + handler.updater.addField(templateType, fieldTemplate, fieldName, attribute.type(), false); + buildArguments.add(fieldName); + handler.updater.addMethod( + templateType, + methodTemplate, + setterName(attribute.name()), + List.of(attribute.type()), + List.of(attribute.name()), + generatedTypeName, + false, + MethodBodySpec.assignFieldAndReturnThis(fieldName, attribute.name())); + } + handler.updater.addMethod( + templateType, + methodTemplate, + "build", + Collections.emptyList(), + Collections.emptyList(), + concreteName, + false, + MethodBodySpec.returnNew(concreteName, buildArguments)); + removeTemplateMembers(templateType, templateFields, templateMethods); + } + + private String resolveGeneratedTargetTypeName( + ASTTypeDeclaration templateType, CodeMatching matching) { + for (ISymbol reference : matching.getReferences()) { + if (reference instanceof CDTypeSymbol type) { + return handler + .getSymbolFromContext(reference) + .orElseGet(() -> handler.getConTypeSymbol(type)) + .getName(); + } + } + return symbols.resolveConcreteTypeName(templateType.getName()); + } + + private List collectGeneratedAttributes(String concreteName) { + Optional concreteType = symbols.findConcreteType(concreteName); + if (concreteType.isEmpty()) { + Log.warn("Cannot generate members: concrete type " + concreteName + " is not in the CD"); + return List.of(); + } + List attributes = new ArrayList<>(); + for (ASTCDAttribute attribute : concreteType.get().getCDAttributeList()) { + String type = JavaSourceNames.printNormalizedType(attribute.getMCType()); + if (handler.incarnationContext != null + && handler.useCommonParentForMultipleIncarnations) { + type = symbols.replaceConcreteWithGroupingType(type); + } + attributes.add( + new GeneratedAttribute(attribute.getName(), symbols.resolveConcreteCdType(type))); + } + return attributes; + } + + private void removeTemplateMembers( + ASTTypeDeclaration templateType, + List templateFields, + List templateMethods) { + for (ASTFieldDeclaration field : templateFields) { + try { + handler.updater.removeField(templateType, field); + } catch (RuntimeException exception) { + Log.debug( + "Could not remove template field: " + exception.getMessage(), + "JavaTypeUpdateService"); + } + } + for (ASTMethodDeclaration method : templateMethods) { + try { + handler.updater.removeMethod(templateType, method); + } catch (RuntimeException exception) { + Log.debug( + "Could not remove template method: " + exception.getMessage(), + "JavaTypeUpdateService"); + } + } + } + + void projectCompletedMembers(JavaAstElemCollector collector) { + for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { + Optional typeMatching = handler.validator.getMatchedType(type); + if (typeMatching.isEmpty() || !typeMatching.get().mustBePerform()) { + continue; + } + String concreteTypeName = handler.buildConcreteName(typeMatching.get()); + Optional concreteType = handler.conIndex.type(concreteTypeName); + if (concreteType.isEmpty()) { + continue; + } + projectFields(type, concreteType.get(), collector); + projectMethods(type, concreteType.get(), collector); + projectCompletionDelta(type, concreteType.get()); + } + } + + private void projectCompletionDelta(ASTTypeDeclaration javaType, ASTCDType completedType) { + Optional inputType = handler.inputConIndex.type(completedType.getName()); + + boolean completedAbstract = CDTypeRelations.isAbstract(completedType); + if (completedType instanceof ASTCDClass) { + // Reference templates may be abstract even when a concrete incarnation is not. Normalize + // every adapted class to the final target CD instead of treating abstractness only as a + // completion delta. + handler.updater.setTypeAbstract(javaType, completedAbstract); + } + + for (ASTCDAttribute completedField : completedType.getCDAttributeList()) { + Optional inputField = + inputType.flatMap( + type -> handler.inputConIndex.attribute(type.getName(), completedField.getName())); + String completedFieldType = JavaSourceNames.printNormalizedFieldType(completedField); + boolean unchanged = + inputField + .map(JavaSourceNames::printNormalizedFieldType) + .filter(completedFieldType::equals) + .isPresent(); + if (!unchanged) { + handler.updater.addField( + javaType, + null, + completedField.getName(), + symbols.resolveConcreteCdType(completedFieldType), + completedField.getModifier().isStatic()); + } + } + + for (ASTCDMethod completedMethod : completedType.getCDMethodList()) { + String signature = JavaSourceNames.methodSignature(completedMethod); + Optional inputMethod = + inputType.flatMap(type -> handler.inputConIndex.method(type.getName(), signature)); + String completedReturnType = JavaSourceNames.printNormalizedReturnType(completedMethod); + boolean unchanged = + inputMethod + .map(JavaSourceNames::printNormalizedReturnType) + .filter(completedReturnType::equals) + .isPresent(); + if (!unchanged) { + handler.updater.addMethod( + javaType, + null, + completedMethod.getName(), + completedMethod.getCDParameterList().stream() + .map(ASTCDParameter::getMCType) + .map(JavaSourceNames::printNormalizedType) + .map(symbols::resolveConcreteCdType) + .toList(), + completedMethod.getCDParameterList().stream().map(ASTCDParameter::getName).toList(), + symbols.resolveConcreteCdType(completedReturnType), + completedMethod.getModifier().isStatic(), + MethodBodySpec.empty()); + } + } + + String inputSuperclass = + inputType.flatMap(CDTypeRelations::firstSuperclassName) + .map(symbols::resolveConcreteCdType) + .orElse(null); + String completedSuperclass = + CDTypeRelations.firstSuperclassName(completedType) + .map(symbols::resolveConcreteCdType) + .orElse(null); + if (completedSuperclass != null && !completedSuperclass.equals(inputSuperclass)) { + handler.updater.addSuperType(javaType, completedSuperclass, false); + } + + Set inputInterfaces = + inputType.stream() + .flatMap(type -> CDTypeRelations.interfaceNames(type).stream()) + .map(symbols::resolveConcreteCdType) + .collect(java.util.stream.Collectors.toSet()); + List addedInterfaces = CDTypeRelations.interfaceNames(completedType).stream() + .map(symbols::resolveConcreteCdType) + .filter(interfaceName -> !inputInterfaces.contains(interfaceName)) + .distinct() + .toList(); + addedInterfaces.forEach( + interfaceName -> handler.updater.addSuperType(javaType, interfaceName, true)); + if (completedType instanceof ASTCDClass && !completedAbstract) { + Set completedEffectiveInterfaces = + effectiveInterfaceNames(handler.conIndex, completedType.getName()); + Set inputEffectiveInterfaces = + inputType + .map(type -> effectiveInterfaceNames(handler.inputConIndex, type.getName())) + .orElseGet(Set::of); + List introducedEffectiveInterfaces = + completedEffectiveInterfaces.stream() + .filter(interfaceName -> !inputEffectiveInterfaces.contains(interfaceName)) + .toList(); + projectInterfaceContracts(javaType, completedType, introducedEffectiveInterfaces); + } + + if (completedType instanceof ASTCDEnum completedEnum) { + Set inputConstants = + inputType + .filter(ASTCDEnum.class::isInstance) + .map(ASTCDEnum.class::cast) + .stream() + .flatMap(type -> type.getCDEnumConstantList().stream()) + .map(constant -> constant.getName()) + .collect(java.util.stream.Collectors.toSet()); + for (int index = 0; index < completedEnum.getCDEnumConstantList().size(); index++) { + String name = completedEnum.getCDEnumConstant(index).getName(); + if (!inputConstants.contains(name)) { + handler.updater.addEnumConstant(javaType, name, index); + } + } + } + } + + /** + * Returns every interface implemented directly or inherited through interfaces and + * superclasses. The visited set in the recursive helper makes malformed inheritance cycles safe. + */ + static Set effectiveInterfaceNames(CDModelIndex index, String typeName) { + Set result = new LinkedHashSet<>(); + collectEffectiveInterfaces(index, typeName, result, new LinkedHashSet<>()); + return result; + } + + private static void collectEffectiveInterfaces( + CDModelIndex index, + String typeName, + Set interfaces, + Set visitedTypes) { + String simpleName = JavaSourceNames.simpleName(typeName); + if (!visitedTypes.add(simpleName)) { + return; + } + ASTCDType type = index.type(simpleName).orElse(null); + if (type == null) { + return; + } + for (String interfaceName : CDTypeRelations.interfaceNames(type)) { + String simpleInterface = JavaSourceNames.simpleName(interfaceName); + interfaces.add(simpleInterface); + collectEffectiveInterfaces(index, simpleInterface, interfaces, visitedTypes); + } + CDTypeRelations.firstSuperclassName(type) + .ifPresent(parent -> collectEffectiveInterfaces(index, parent, interfaces, visitedTypes)); + } + + /** + * Materializes newly introduced interface contracts on a concrete Java class. Abstract classes + * intentionally skip this step; their concrete descendants receive any still-missing contracts. + */ + private void projectInterfaceContracts( + ASTTypeDeclaration javaType, ASTCDType completedType, List addedInterfaces) { + Map contracts = new LinkedHashMap<>(); + Set visitedInterfaces = new LinkedHashSet<>(); + for (String interfaceName : addedInterfaces) { + collectInterfaceContracts(interfaceName, contracts, visitedInterfaces); + } + + for (ASTCDMethod contract : contracts.values()) { + validateCompletedImplementation(completedType, contract); + handler.updater.addMethod( + javaType, + null, + contract.getName(), + contract.getCDParameterList().stream() + .map(ASTCDParameter::getMCType) + .map(JavaSourceNames::printNormalizedType) + .map(symbols::resolveConcreteCdType) + .toList(), + contract.getCDParameterList().stream().map(ASTCDParameter::getName).toList(), + symbols.resolveConcreteCdType(JavaSourceNames.printNormalizedReturnType(contract)), + false, + MethodBodySpec.interfaceContract()); + } + } + + /** + * Traverses parent interfaces first and keeps one method per normalized signature. Compatible + * covariant returns select the most specific declaration; incompatible returns fail explicitly. + */ + private void collectInterfaceContracts( + String interfaceName, + Map contracts, + Set visitedInterfaces) { + String simpleName = JavaSourceNames.simpleName(interfaceName); + if (!visitedInterfaces.add(simpleName)) { + return; + } + ASTCDType interfaceType = handler.conIndex.type(simpleName).orElse(null); + if (!(interfaceType instanceof ASTCDInterface)) { + throw new IllegalStateException( + "Completed Java interface '" + simpleName + "' is not an interface in the concrete CD"); + } + for (String parent : CDTypeRelations.interfaceNames(interfaceType)) { + collectInterfaceContracts(parent, contracts, visitedInterfaces); + } + for (ASTCDMethod method : interfaceType.getCDMethodList()) { + if (method.getModifier().isStatic()) { + continue; + } + String signature = JavaSourceNames.methodSignature(method); + ASTCDMethod existing = contracts.get(signature); + if (existing != null) { + String existingReturn = JavaSourceNames.printNormalizedReturnType(existing); + String candidateReturn = JavaSourceNames.printNormalizedReturnType(method); + if (!returnsAreCompatible(existingReturn, candidateReturn)) { + throw new IllegalStateException( + "Incompatible inherited interface contracts for " + + signature + + ": " + + existingReturn + + " and " + + candidateReturn); + } + if (isCovariantReturn(candidateReturn, existingReturn)) { + contracts.put(signature, method); + } + } else { + contracts.put(signature, method); + } + } + } + + private boolean returnsAreCompatible(String first, String second) { + return isCompatibleImplementationReturn(first, second) + || isCompatibleImplementationReturn(second, first); + } + + private boolean isCovariantReturn(String candidate, String parent) { + return !JavaSourceNames.normalizeType(candidate) + .equals(JavaSourceNames.normalizeType(parent)) + && isCompatibleImplementationReturn(candidate, parent); + } + + /** Tests Java-compatible equality or covariance for an implementation return type. */ + private boolean isCompatibleImplementationReturn( + String actualReturn, String expectedReturn) { + if (isCompatibleImplementationReturn(handler.conIndex, actualReturn, expectedReturn)) { + return true; + } + String qualifiedActual = symbols.qualifyCdType(actualReturn); + String qualifiedExpected = symbols.qualifyCdType(expectedReturn); + return isClasspathSubtype(qualifiedActual, qualifiedExpected); + } + + /** Tests local CD equality/covariance without guessing external type bindings. */ + static boolean isCompatibleImplementationReturn( + CDModelIndex index, String actualReturn, String expectedReturn) { + String actual = JavaSourceNames.normalizeType(actualReturn); + String expected = JavaSourceNames.normalizeType(expectedReturn); + if (actual.equals(expected)) { + return true; + } + if ("Object".equals(JavaSourceNames.simpleName(expected)) && !isPrimitive(actual)) { + return true; + } + return index.isSubtypeOf(actual, expected); + } + + private static boolean isClasspathSubtype(String actualReturn, String expectedReturn) { + Optional actualType = JavaSourceNames.erasedType(actualReturn); + Optional expectedType = JavaSourceNames.erasedType(expectedReturn); + if (actualType.isEmpty() || expectedType.isEmpty()) { + return false; + } + JavaSourceNames.ErasedType actual = actualType.get(); + JavaSourceNames.ErasedType expected = expectedType.get(); + if ((!actual.name().contains(".") && !isPrimitive(actual.name())) + || (!expected.name().contains(".") && !isPrimitive(expected.name())) + || actual.arrayDimensions() != expected.arrayDimensions() + || (!expected.typeArguments().isEmpty() + && !expected.typeArguments().equals(actual.typeArguments()))) { + return false; + } + try { + Class actualClass = classFor(actual); + Class expectedClass = classFor(expected); + return expectedClass.isAssignableFrom(actualClass); + } catch (ClassNotFoundException | LinkageError ignored) { + return false; + } + } + + private static Class classFor(JavaSourceNames.ErasedType type) + throws ClassNotFoundException { + Class component = primitiveClass(type.name()); + if (component == null) { + component = + Class.forName(type.name(), false, JavaTypeUpdateService.class.getClassLoader()); + } + return type.arrayDimensions() == 0 + ? component + : java.lang.reflect.Array.newInstance(component, new int[type.arrayDimensions()]).getClass(); + } + + private static Class primitiveClass(String name) { + return switch (name) { + case "boolean" -> boolean.class; + case "byte" -> byte.class; + case "short" -> short.class; + case "int" -> int.class; + case "long" -> long.class; + case "char" -> char.class; + case "float" -> float.class; + case "double" -> double.class; + case "void" -> void.class; + default -> null; + }; + } + + private static boolean isPrimitive(String type) { + return Set.of("boolean", "byte", "short", "int", "long", "char", "float", "double", "void") + .contains(type); + } + + /** Rejects a completed class method that already occupies a contract signature incompatibly. */ + private void validateCompletedImplementation(ASTCDType completedType, ASTCDMethod contract) { + String signature = JavaSourceNames.methodSignature(contract); + for (ASTCDMethod method : completedType.getCDMethodList()) { + if (!signature.equals(JavaSourceNames.methodSignature(method))) { + continue; + } + String expectedReturn = JavaSourceNames.printNormalizedReturnType(contract); + String actualReturn = JavaSourceNames.printNormalizedReturnType(method); + if (!isCompatibleImplementationReturn(actualReturn, expectedReturn)) { + throw new IllegalStateException( + "Method " + + completedType.getName() + + "." + + signature + + " has return type " + + actualReturn + + " but interface contract requires " + + expectedReturn); + } + return; + } + } + + private void projectFields( + ASTTypeDeclaration type, ASTCDType concreteType, JavaAstElemCollector collector) { + for (ASTFieldDeclaration template : collector.getAllFieldDeclarations(type)) { + Optional matching = handler.validator.getMatchedField(type, template); + if (matching.isEmpty() || !matching.get().mustBePerform()) { + continue; + } + boolean generated = false; + boolean retainsTemplate = false; + for (ISymbol reference : matching.get().getReferences()) { + if (!(reference.getAstNode() instanceof ASTCDAttribute)) { + continue; + } + for (ASTCDAttribute concreteAttribute : concreteAttributesFor(reference)) { + if (isOwnedBy(concreteAttribute, concreteType)) { + generated = true; + if (isSelectedIncarnation(reference, concreteAttribute.getSymbol())) { + retainsTemplate = true; + continue; + } + retainsTemplate |= matches(template, concreteAttribute); + handler.updater.addField( + type, + template, + concreteAttribute.getName(), + symbols.resolveConcreteCdType( + JavaSourceNames.printNormalizedFieldType(concreteAttribute)), + concreteAttribute.getModifier().isStatic()); + } + } + } + if (generated && !retainsTemplate) { + handler.updater.removeField(type, template); + } + } + } + + private void projectMethods( + ASTTypeDeclaration type, ASTCDType concreteType, JavaAstElemCollector collector) { + for (ASTMethodDeclaration template : collector.getAllMethodDeclarations(type)) { + Optional matching = handler.validator.getMatchedMethod(type, template); + if (matching.isEmpty() || !matching.get().mustBePerform()) { + continue; + } + boolean generated = false; + boolean retainsTemplate = false; + for (ISymbol reference : matching.get().getReferences()) { + if (!(reference.getAstNode() instanceof ASTCDMethod) + || memberUpdates.isForEachTargetMethod(reference)) { + continue; + } + for (ASTCDMethod concreteMethod : concreteMethodsFor(reference)) { + if (isOwnedBy(concreteMethod, concreteType)) { + generated = true; + boolean selected = + isSelectedIncarnation(reference, concreteMethod.getSymbol()); + String concreteReturn = + symbols.resolveConcreteCdType( + JavaSourceNames.printNormalizedReturnType(concreteMethod)); + retainsTemplate |= + selected && matchesMethodShape(template, concreteMethod, type, collector); + retainsTemplate |= matches(template, concreteMethod, type, collector); + handler.updater.addMethod( + type, + template, + concreteMethod.getName(), + concreteMethod.getCDParameterList().stream() + .map( + parameter -> + symbols.resolveConcreteCdType( + JavaSourceNames.printNormalizedType(parameter.getMCType()))) + .toList(), + concreteMethod.getCDParameterList().stream() + .map(ASTCDParameter::getName) + .toList(), + concreteReturn, + concreteMethod.getModifier().isStatic(), + MethodBodySpec.empty()); + } + } + } + if (generated && !retainsTemplate) { + handler.updater.removeMethod(type, template); + } + } + } + + private boolean matches(ASTFieldDeclaration template, ASTCDAttribute concrete) { + String templateName = template.getVariableDeclarator(0).getDeclarator().getName(); + return templateName.equals(concrete.getName()) + && JavaSourceNames.printNormalizedType(template.getMCType()) + .equals(JavaSourceNames.printNormalizedFieldType(concrete)); + } + + private boolean matches( + ASTMethodDeclaration template, + ASTCDMethod concrete, + ASTTypeDeclaration owner, + JavaAstElemCollector collector) { + return template.getName().equals(concrete.getName()) + && matchesMethodShape(template, concrete, owner, collector); + } + + private boolean matchesMethodShape( + ASTMethodDeclaration template, + ASTCDMethod concrete, + ASTTypeDeclaration owner, + JavaAstElemCollector collector) { + List templateParameters = + collector.getAllParameters(owner, template).stream() + .map(ASTFormalParameter::getMCType) + .map(JavaSourceNames::printNormalizedType) + .toList(); + List concreteParameters = + concrete.getCDParameterList().stream() + .map(ASTCDParameter::getMCType) + .map(JavaSourceNames::printNormalizedType) + .toList(); + return templateParameters.equals(concreteParameters) + && JavaSourceNames.normalizeType(JavaLoader.print(template.getMCReturnType())) + .equals(JavaSourceNames.printNormalizedReturnType(concrete)); + } + + private List concreteAttributesFor(ISymbol reference) { + List result = new ArrayList<>(); + if (reference.getAstNode() instanceof ASTCDAttribute attribute + && handler.checker != null + && handler.checker.getIncarnationMapping() != null) { + var incarnations = handler.checker.getIncarnationMapping().getIncarnations(attribute); + if (incarnations != null) { + for (var incarnation : incarnations) { + if (incarnation instanceof ASTCDAttribute concreteAttribute) { + result.add(concreteAttribute); + } + } + } + } + if (result.isEmpty() && handler.incarnationContext != null) { + for (var incarnation : handler.mappedIncarnations(reference)) { + if (incarnation.symbol().getAstNode() instanceof ASTCDAttribute concreteAttribute) { + result.add(concreteAttribute); + } + } + } + return result; + } + + private List concreteMethodsFor(ISymbol reference) { + List result = new ArrayList<>(); + if (reference.getAstNode() instanceof ASTCDMethod method + && handler.checker != null + && handler.checker.getIncarnationMapping() != null) { + var incarnations = handler.checker.getIncarnationMapping().getIncarnations(method); + if (incarnations != null) { + for (var incarnation : incarnations) { + if (incarnation instanceof ASTCDMethod concreteMethod) { + result.add(concreteMethod); + } + } + } + } + if (result.isEmpty() && handler.incarnationContext != null) { + for (var incarnation : handler.mappedIncarnations(reference)) { + if (incarnation.symbol().getAstNode() instanceof ASTCDMethod concreteMethod) { + result.add(concreteMethod); + } + } + } + return result; + } + + private boolean isOwnedBy(ASTCDAttribute attribute, ASTCDType owner) { + return handler.conIndex.ownerOf(attribute) + .map(candidate -> candidate == owner || candidate.getName().equals(owner.getName())) + .orElse(false); + } + + private boolean isOwnedBy(ASTCDMethod method, ASTCDType owner) { + return handler.conIndex.ownerOf(method) + .map(candidate -> candidate == owner || candidate.getName().equals(owner.getName())) + .orElse(false); + } + + private boolean isSelectedIncarnation(ISymbol reference, ISymbol concrete) { + Optional selected = handler.getSymbolFromContext(reference); + if (selected.isEmpty()) { + return false; + } + if (selected.get() == concrete || selected.get().getAstNode() == concrete.getAstNode()) { + return true; + } + if (handler.incarnationContext == null) { + return false; + } + Optional selectedKey = + handler.concreteKey(selected.get()); + Optional concreteKey = + handler.concreteKey(concrete); + return selectedKey.isPresent() && selectedKey.equals(concreteKey); + } + + private static String setterName(String attributeName) { + return attributeName == null || attributeName.isEmpty() + ? "set" + : "set" + JavaSourceNames.capitalize(attributeName); + } + + private record GeneratedAttribute(String name, String type) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/AdaptationConflictDetector.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/AdaptationConflictDetector.java new file mode 100644 index 00000000..d53dcb11 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/AdaptationConflictDetector.java @@ -0,0 +1,57 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.CodeAdaptationException; +import de.monticore.codeAdaption.handler.multiIncarnation.conflict.AdaptationConflictCheck; +import de.monticore.codeAdaption.handler.multiIncarnation.conflict.ConflictCollector; +import de.monticore.codeAdaption.handler.multiIncarnation.conflict.ConflictDetectionContext; +import de.monticore.codeAdaption.handler.multiIncarnation.conflict.DefaultConflictChecks; +import de.monticore.codeAdaption.utils.CDModelIndex; +import java.util.Map; +import java.util.Set; + +/** + * Runs manual adaptation conflict checks before Java output is cleaned or generated. + * + *

The detector is an orchestration facade over the built-in conflict-check strategies. + */ +public final class AdaptationConflictDetector { + + private AdaptationConflictDetector() {} + + /** + * Validates manual adaptation conflicts before Java output is cleaned or generated. + * + *

The detector covers type-kind mismatches, inheritance issues, duplicate or incompatible + * members, enum order mismatches, association ambiguities, underspecified {@code any} types, and + * unsupported forEach mappings. + */ + public static void validate( + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + Set mappings, + Map contexts, + Set confParams, + boolean useCommonParentForMultipleIncarnations) { + ConflictDetectionContext context = + new ConflictDetectionContext( + referenceIndex, + concreteIndex, + mappings, + contexts, + confParams, + useCommonParentForMultipleIncarnations); + ConflictCollector conflicts = new ConflictCollector(); + for (AdaptationConflictCheck check : DefaultConflictChecks.all()) { + check.check(context, conflicts); + } + + if (conflicts.hasConflicts()) { + throw new CodeAdaptationException( + "Manual code adaptation detected conflicts before output generation:" + + System.lineSeparator() + + "- " + + String.join(System.lineSeparator() + "- ", conflicts.formattedConflicts())); + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContext.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContext.java new file mode 100644 index 00000000..96a08074 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContext.java @@ -0,0 +1,107 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import de.monticore.symboltable.ISymbol; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * Immutable incarnation mappings for one adaptation mapping. Stable keys are the only lookup + * identity; MontiCore symbols are retained only as payload for AST operations. + */ +public class IncarnationContext { + /** + * Stable concrete identity plus the MontiCore symbol needed for AST operations. + * + * @param key stable key of the concrete CD element, for example the type key {@code CreditCard} + * @param symbol concrete MontiCore symbol whose AST node is used during adaptation + */ + public record MappedElement(StableElementKey key, ISymbol symbol) { + public MappedElement { + Objects.requireNonNull(key); + Objects.requireNonNull(symbol); + } + } + + private final String mappingName; + private final Map> mappings; + private final Map groupingByIncarnation; + + /** + * Creates an immutable context for one mapping. + * + *

Example: for mapping {@code "shop"}, {@code mappings} may associate reference type + * {@code Payment} with concrete types {@code CreditCard} and {@code Invoice}. If both concrete + * types must be used through {@code PaymentMethod}, {@code groupingByIncarnation} contains + * {@code CreditCard -> PaymentMethod} and {@code Invoice -> PaymentMethod}. + * + * @param mappingName name of the CD stereotype that identifies this mapping, such as {@code shop} + * @param mappings stable reference-element keys to one or more concrete incarnations; entries may + * represent types, fields, or signature-aware methods, for example {@code Payment -> + * [CreditCard, Invoice]} + * @param groupingByIncarnation concrete type-incarnation keys to the common concrete type through + * which shared Java declarations should reference their incarnation group; for example {@code + * CreditCard -> PaymentMethod}; may be {@code null} when no grouping is required + */ + public IncarnationContext( + String mappingName, + Map> mappings, + Map groupingByIncarnation) { + this.mappingName = Objects.requireNonNull(mappingName); + this.mappings = immutableMappings(mappings); + this.groupingByIncarnation = + Collections.unmodifiableMap( + new LinkedHashMap<>( + groupingByIncarnation == null ? Map.of() : groupingByIncarnation)); + } + + /** Returns the mapping stereotype name represented by this context. */ + public String getMappingName() { + return mappingName; + } + + /** + * Returns all immutable stable-key incarnation mappings, for example {@code Payment -> + * [CreditCard, Invoice]}. + */ + public Map> getMappings() { + return mappings; + } + + /** Returns the concrete incarnations of one stable reference key. */ + public List getIncarnations(StableElementKey referenceKey) { + return referenceKey == null ? List.of() : mappings.getOrDefault(referenceKey, List.of()); + } + + /** Returns the incarnation only when exactly one concrete target exists. */ + public Optional getUniqueIncarnation(StableElementKey referenceKey) { + List incarnations = getIncarnations(referenceKey); + return incarnations.size() == 1 ? Optional.of(incarnations.get(0)) : Optional.empty(); + } + + /** Returns the common grouping type selected for one concrete type incarnation. */ + public Optional getGroupingFor(StableElementKey incarnation) { + return Optional.ofNullable(groupingByIncarnation.get(incarnation)); + } + + /** + * Returns the complete immutable type-incarnation to grouping-type index, for example {@code + * CreditCard -> PaymentMethod} and {@code Invoice -> PaymentMethod}. + */ + public Map getGroupingMappings() { + return groupingByIncarnation; + } + + /** Copies both map and incarnation lists so callers cannot mutate context state after creation. */ + private static Map> immutableMappings( + Map> source) { + Map> copy = new LinkedHashMap<>(); + if (source != null) { + source.forEach((key, value) -> copy.put(key, value == null ? List.of() : List.copyOf(value))); + } + return Collections.unmodifiableMap(copy); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContextBuilder.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContextBuilder.java new file mode 100644 index 00000000..b1873854 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContextBuilder.java @@ -0,0 +1,164 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdconformance.CDConformanceChecker; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.se_rwth.commons.logging.Log; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** Builds incarnation contexts from checker results, optionally overlaid with stereotypes. */ +public class IncarnationContextBuilder { + private final CDConformanceChecker conformanceChecker; + private final IncarnationMappingSupport support; + + /** + * Creates a builder backed by one completed conformance check and indexes of the same model pair. + * + * @param conformanceChecker checker whose incarnation mapping supplies inferred relationships + * @param referenceIndex index of the reference CD + * @param concreteIndex index of the concrete CD checked against the reference CD + */ + public IncarnationContextBuilder( + CDConformanceChecker conformanceChecker, + CDModelIndex referenceIndex, + CDModelIndex concreteIndex) { + this.conformanceChecker = conformanceChecker; + this.support = new IncarnationMappingSupport(referenceIndex, concreteIndex); + } + + /** + * Builds a context from checker results and fills checker gaps from explicit mapping + * stereotypes. + * + * @param mapping stereotype name whose explicit mappings should be overlaid + * @return immutable context for the requested mapping + */ + public IncarnationContext buildContextForMapping(String mapping) { + return buildContextForMapping(mapping, true); + } + + /** + * Builds a context from inferred checker mappings and, when requested, explicit stereotypes. + * Explicit mappings are merged with rather than substituted for checker results; duplicate + * concrete targets are removed by {@link IncarnationMappingSupport}. + * + *

For example, if the checker maps {@code Payment -> CreditCard} and stereotype {@code + * <>} maps {@code Invoice}, enabling the overlay yields {@code Payment -> + * [CreditCard, Invoice]}; disabling it retains only {@code CreditCard}. + * + * @param mapping stereotype name represented by the resulting context + * @param overlayStereotypeMappings whether explicit stereotypes should fill or extend checker + * data + * @return immutable incarnation and grouping context + */ + public IncarnationContext buildContextForMapping( + String mapping, boolean overlayStereotypeMappings) { + Map> mappings = support.newMapping(); + if (conformanceChecker.getIncarnationMapping() != null) { + extractCheckerMappings(mappings); + } + if (overlayStereotypeMappings) { + Log.info( + "Overlaying stereotype context for mapping '" + mapping + "'", + "IncarnationContextBuilder"); + support.mergeMappings(mappings, extractStereotypeMappings(mapping)); + } + return support.assembleContext(mapping, mappings); + } + + /** + * Copies inferred type, field, and method incarnations from the checker into stable keys that + * remain usable after AST cloning. + */ + private void extractCheckerMappings( + Map> result) { + for (ASTCDType referenceType : support.referenceIndex().types()) { + var typeIncarnations = + conformanceChecker.getIncarnationMapping().getIncarnations(referenceType); + if (typeIncarnations != null) { + for (var incarnation : typeIncarnations) { + support.addMapping(result, StableElementKey.type(referenceType), incarnation.getSymbol()); + } + } + for (ASTCDAttribute referenceField : referenceType.getCDAttributeList()) { + var fieldIncarnations = + conformanceChecker.getIncarnationMapping().getIncarnations(referenceField); + if (fieldIncarnations == null) { + continue; + } + for (var incarnation : fieldIncarnations) { + support.addMapping( + result, StableElementKey.field(referenceType, referenceField), incarnation.getSymbol()); + } + } + for (ASTCDMethod referenceMethod : referenceType.getCDMethodList()) { + var methodIncarnations = + conformanceChecker.getIncarnationMapping().getIncarnations(referenceMethod); + if (methodIncarnations == null) { + continue; + } + for (var incarnation : methodIncarnations) { + support.addMapping( + result, StableElementKey.method(referenceType, referenceMethod), incarnation.getSymbol()); + } + } + } + } + + /** + * Extracts explicit mappings from stereotypes on concrete types and their members. Member + * references are resolved only within the reference owners mapped for the concrete type. + */ + private Map> extractStereotypeMappings( + String mapping) { + Map> result = support.newMapping(); + for (ASTCDType concreteType : support.concreteIndex().types()) { + Optional referenceTypeName = support.stereotypeValue(concreteType, mapping); + Optional referenceType = + referenceTypeName.flatMap(support::findReferenceType); + referenceType.ifPresent( + key -> support.addMapping(result, key, concreteType.getSymbol())); + + List referenceOwners = referenceOwners(concreteType, mapping, referenceTypeName); + for (ASTCDAttribute concreteField : concreteType.getCDAttributeList()) { + support + .stereotypeValue(concreteField, mapping) + .flatMap(name -> support.findReferenceField(referenceOwners, name)) + .ifPresent( + key -> support.addMapping(result, key, concreteField.getSymbol())); + } + for (ASTCDMethod concreteMethod : concreteType.getCDMethodList()) { + support + .stereotypeValue(concreteMethod, mapping) + .flatMap(name -> support.findReferenceMethod(referenceOwners, name)) + .ifPresent( + key -> support.addMapping(result, key, concreteMethod.getSymbol())); + } + } + return result; + } + + /** + * Finds the reference owners in which member stereotype names must be resolved, preferring the + * directly mapped reference type and then a mapped reference interface. + */ + private List referenceOwners( + ASTCDType concreteType, String mapping, Optional referenceTypeName) { + List owners = new ArrayList<>(); + referenceTypeName + .flatMap(support.referenceIndex()::type) + .ifPresent(owners::add); + if (owners.isEmpty()) { + support + .mappedInterfaceReference(concreteType, mapping) + .flatMap(support.referenceIndex()::type) + .ifPresent(owners::add); + } + return owners; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationMappingSupport.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationMappingSupport.java new file mode 100644 index 00000000..bc8f0a53 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationMappingSupport.java @@ -0,0 +1,405 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.utils.AdaptReference; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.symboltable.ISymbol; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * Shared reference indexing, stereotype interpretation, mapping merge, and grouping selection for + * the two incarnation-context construction modes. + */ +final class IncarnationMappingSupport { + private final CDModelIndex referenceIndex; + private final CDModelIndex concreteIndex; + private final ReferenceElementIndex referenceElements; + + IncarnationMappingSupport(CDModelIndex referenceIndex, CDModelIndex concreteIndex) { + this.referenceIndex = Objects.requireNonNull(referenceIndex, "referenceIndex"); + this.concreteIndex = Objects.requireNonNull(concreteIndex, "concreteIndex"); + this.referenceElements = new ReferenceElementIndex(referenceIndex); + } + + CDModelIndex referenceIndex() { + return referenceIndex; + } + + CDModelIndex concreteIndex() { + return concreteIndex; + } + + Map> newMapping() { + return new LinkedHashMap<>(); + } + + void addMapping( + Map> target, + StableElementKey reference, + ISymbol concrete) { + Optional concreteKey = StableElementKey.fromSymbol(concrete, concreteIndex); + if (reference == null || concreteKey.isEmpty()) { + return; + } + List incarnations = + target.computeIfAbsent(reference, ignored -> new ArrayList<>()); + boolean duplicate = + incarnations.stream().anyMatch(existing -> existing.key().equals(concreteKey.get())); + if (!duplicate) { + incarnations.add(new IncarnationContext.MappedElement(concreteKey.get(), concrete)); + } + } + + void mergeMappings( + Map> target, + Map> additions) { + for (Map.Entry> entry : additions.entrySet()) { + if (entry.getValue() == null) { + continue; + } + for (IncarnationContext.MappedElement incarnation : entry.getValue()) { + addMapping(target, entry.getKey(), incarnation.symbol()); + } + } + } + + IncarnationContext assembleContext( + String mapping, + Map> mappings) { + return new IncarnationContext(mapping, mappings, groupingTypes(mappings)); + } + + Optional stereotypeValue(ASTCDType type, String name) { + if (type == null || type.getModifier() == null || !type.getModifier().isPresentStereotype()) { + return Optional.empty(); + } + for (var value : type.getModifier().getStereotype().getValuesList()) { + if (name.equals(value.getName())) { + try { + return Optional.ofNullable(value.getValue()); + } catch (RuntimeException ignored) { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + Optional stereotypeValue(ASTCDAttribute attribute, String name) { + if (attribute == null + || attribute.getModifier() == null + || !attribute.getModifier().isPresentStereotype()) { + return Optional.empty(); + } + for (var value : attribute.getModifier().getStereotype().getValuesList()) { + if (name.equals(value.getName())) { + try { + return Optional.ofNullable(value.getValue()); + } catch (RuntimeException ignored) { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + Optional stereotypeValue(ASTCDMethod method, String name) { + if (method == null + || method.getModifier() == null + || !method.getModifier().isPresentStereotype()) { + return Optional.empty(); + } + for (var value : method.getModifier().getStereotype().getValuesList()) { + if (name.equals(value.getName())) { + try { + return Optional.ofNullable(value.getValue()); + } catch (RuntimeException ignored) { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + Optional findReferenceType(String name) { + return referenceElements.findType(name); + } + + Optional findReferenceField(List owners, String name) { + return referenceElements.findField(owners, name); + } + + Optional findReferenceMethod(List owners, String name) { + return referenceElements.findMethod(owners, name); + } + + Optional findSameNameField( + List owners, ASTCDAttribute concreteAttribute) { + List matches = new ArrayList<>(); + for (ASTCDType owner : owners) { + for (ASTCDAttribute candidate : owner.getCDAttributeList()) { + if (candidate.getName().equals(concreteAttribute.getName())) { + matches.add(StableElementKey.field(owner, candidate)); + } + } + } + return unique(matches); + } + + Optional findSameSignatureMethod( + List owners, ASTCDMethod concreteMethod) { + String signature = JavaSourceNames.methodSignature(concreteMethod); + List matches = new ArrayList<>(); + for (ASTCDType owner : owners) { + for (ASTCDMethod candidate : owner.getCDMethodList()) { + if (JavaSourceNames.methodSignature(candidate).equals(signature)) { + matches.add(StableElementKey.method(owner, candidate)); + } + } + } + return unique(matches); + } + + List mappedReferenceOwners( + Map> mappings, + ASTCDType concreteType) { + StableElementKey concreteKey = StableElementKey.type(concreteType); + List owners = new ArrayList<>(); + for (Map.Entry> entry : mappings.entrySet()) { + if (entry.getKey().getKind() != StableElementKey.Kind.TYPE) { + continue; + } + if (entry.getValue().stream() + .anyMatch(element -> element.key().equals(concreteKey))) { + referenceIndex.type(entry.getKey().getName()).ifPresent(owners::add); + } + } + return owners; + } + + Optional mappedInterfaceReference(ASTCDType concreteType, String mapping) { + for (String interfaceName : concreteIndex.directParentNames(concreteType.getName())) { + Optional possibleInterface = concreteIndex.type(interfaceName); + if (possibleInterface.isEmpty() || !isInterface(possibleInterface.get().getName())) { + continue; + } + Optional reference = stereotypeValue(possibleInterface.get(), mapping); + if (reference.isPresent() && findReferenceType(reference.get()).isPresent()) { + return reference; + } + } + return Optional.empty(); + } + + private Optional unique(List candidates) { + if (candidates.isEmpty()) { + return Optional.empty(); + } + T first = candidates.get(0); + for (int i = 1; i < candidates.size(); i++) { + if (!Objects.equals(candidates.get(i), first)) { + return Optional.empty(); + } + } + return Optional.of(first); + } + + /** + * Selects concrete interfaces that represent complete incarnation groups. + * + *

The returned map is keyed by each concrete incarnation and points to the common interface + * selected for its group. An interface is eligible only when its concrete class implementers + * exactly equal the incarnations in that group (the interface itself is ignored if it is also an + * incarnation). This exact-set rule prevents an unrelated implementer from becoming accepted by + * a rewritten field or parameter. + * + *

For example, {@code [CreditCard, Invoice]} may be grouped as {@code PaymentMethod} when + * those are exactly its concrete implementers, producing {@code CreditCard -> PaymentMethod} and + * {@code Invoice -> PaymentMethod}. If {@code Cash} also implements {@code PaymentMethod} but is + * not an incarnation of the same reference type, that interface is rejected. When several exact + * interfaces qualify, the nearest interface wins; its name breaks equal-distance ties + * deterministically. + */ + private Map groupingTypes( + Map> mappings) { + Map result = new LinkedHashMap<>(); + for (List incarnations : mappings.values()) { + Set targets = typeNames(incarnations); + if (targets.size() < 2) { + continue; + } + String best = null; + int bestDistance = Integer.MAX_VALUE; + for (ASTCDType iface : concreteIndex.interfaces()) { + Set expectedImplementers = new LinkedHashSet<>(targets); + expectedImplementers.remove(iface.getName()); + Set actualImplementers = new LinkedHashSet<>(); + for (ASTCDType type : concreteIndex.classes()) { + if (concreteIndex.isSubtypeOf(type.getName(), iface.getName())) { + actualImplementers.add(type.getName()); + } + } + if (!actualImplementers.equals(expectedImplementers)) { + continue; + } + int distance = maximumDistance(targets, iface.getName()); + if (distance < bestDistance + || (distance == bestDistance && (best == null || iface.getName().compareTo(best) < 0))) { + best = iface.getName(); + bestDistance = distance; + } + } + if (best != null) { + ASTCDType groupingType = concreteIndex.type(best).orElseThrow(); + IncarnationContext.MappedElement grouping = + new IncarnationContext.MappedElement( + StableElementKey.type(groupingType), groupingType.getSymbol()); + for (String target : targets) { + StableElementKey targetKey = StableElementKey.type(target); + IncarnationContext.MappedElement previous = result.putIfAbsent(targetKey, grouping); + if (previous != null && !previous.key().equals(grouping.key())) { + throw new IllegalStateException( + "Ambiguous grouping for concrete type '" + + target + + "': '" + + previous.key().getName() + + "' and '" + + grouping.key().getName() + + "'"); + } + } + } + } + return result; + } + + private Set typeNames(List elements) { + Set result = new LinkedHashSet<>(); + if (elements == null) { + return result; + } + for (IncarnationContext.MappedElement element : elements) { + StableElementKey key = element.key(); + if (key != null && key.getKind() == StableElementKey.Kind.TYPE) { + result.add(key.getName()); + } + } + return result; + } + + private int maximumDistance(Set descendants, String ancestor) { + int maximum = 0; + for (String descendant : descendants) { + int distance = inheritanceDistance(descendant, ancestor); + if (distance == Integer.MAX_VALUE) { + return distance; + } + maximum = Math.max(maximum, distance); + } + return maximum; + } + + private int inheritanceDistance(String descendant, String ancestor) { + if (descendant.equals(ancestor)) { + return 0; + } + ArrayDeque queue = new ArrayDeque<>(); + Map distances = new LinkedHashMap<>(); + queue.add(descendant); + distances.put(descendant, 0); + while (!queue.isEmpty()) { + String current = queue.removeFirst(); + int nextDistance = distances.get(current) + 1; + for (String parent : concreteIndex.directParentNames(current)) { + if (parent.equals(ancestor)) { + return nextDistance; + } + if (!distances.containsKey(parent)) { + distances.put(parent, nextDistance); + queue.addLast(parent); + } + } + } + return Integer.MAX_VALUE; + } + + private boolean isInterface(String name) { + return concreteIndex.interfaces().stream().anyMatch(type -> type.getName().equals(name)); + } + + /** Owner- and signature-aware index. Ambiguous simple-name requests deliberately yield empty. */ + private static final class ReferenceElementIndex { + private final CDModelIndex index; + + private ReferenceElementIndex(CDModelIndex index) { + this.index = index; + } + + Optional findType(String requested) { + return index.type(requested).map(StableElementKey::type); + } + + Optional findField(List owners, String requested) { + Optional parsed = AdaptReference.parse(requested); + if (parsed.isEmpty() || parsed.get().isMethod()) { + return Optional.empty(); + } + AdaptReference reference = parsed.get(); + List matches = new ArrayList<>(); + String fieldName = reference.memberName(); + String explicitOwner = reference.owner().orElse(""); + for (ASTCDType owner : searchOwners(owners, explicitOwner)) { + index.attribute(owner.getName(), fieldName).ifPresent(matches::add); + } + if (matches.size() != 1) { + return Optional.empty(); + } + ASTCDAttribute match = matches.get(0); + return index.ownerOf(match).map(owner -> StableElementKey.field(owner, match)); + } + + Optional findMethod(List owners, String requested) { + Optional parsed = AdaptReference.parse(requested); + if (parsed.isEmpty()) { + return Optional.empty(); + } + AdaptReference reference = parsed.get(); + String explicitOwner = reference.owner().orElse(""); + List matches = new ArrayList<>(); + for (ASTCDType owner : searchOwners(owners, explicitOwner)) { + for (ASTCDMethod method : index.methods(owner.getName(), reference.memberName())) { + if (!reference.isMethod() + || JavaSourceNames.methodSignature(method) + .equals(reference.methodSignature().orElseThrow())) { + matches.add(method); + } + } + } + if (matches.size() != 1) { + return Optional.empty(); + } + ASTCDMethod match = matches.get(0); + return index.ownerOf(match).map(owner -> StableElementKey.method(owner, match)); + } + + private List searchOwners(List owners, String explicitOwner) { + if (!explicitOwner.isEmpty()) { + Optional owner = index.type(explicitOwner); + return owner.isPresent() ? List.of(owner.get()) : List.of(); + } + return owners == null || owners.isEmpty() ? index.types() : owners; + } + + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/ManualIncarnationContextBuilder.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/ManualIncarnationContextBuilder.java new file mode 100644 index 00000000..9ca8982e --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/ManualIncarnationContextBuilder.java @@ -0,0 +1,285 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * Builds contexts from explicit stereotypes and deterministic name matches, without invoking + * cdconcretization or the conformance checker. + */ +public class ManualIncarnationContextBuilder { + private final Set confParams; + private final IncarnationMappingSupport support; + + /** + * Creates a manual builder for one reference/concrete model pair. + * + * @param referenceIndex index used to resolve referenced types and members + * @param concreteIndex index whose elements become concrete incarnations + * @param confParams mapping policies; {@link CDConfParameter#NAME_MAPPING} enables deterministic + * same-name fallback when no explicit stereotype is present + */ + public ManualIncarnationContextBuilder( + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + Set confParams) { + this.confParams = confParams; + this.support = new IncarnationMappingSupport(referenceIndex, concreteIndex); + } + + /** + * Builds one immutable mapping context without invoking conformance or concretization. + * Resolution order is explicit type/member stereotypes, optional same-name mapping, and finally + * expansion of reference-side {@code forEach} stereotypes. + * + * @param mapping stereotype name to read from concrete CD elements + * @return assembled incarnation and grouping context + */ + public IncarnationContext buildContextForMapping(String mapping) { + Map> mappings = support.newMapping(); + collectTypeMappings(mapping, mappings); + collectMemberMappings(mapping, mappings); + collectForEachMappings(mappings); + return support.assembleContext(mapping, mappings); + } + + /** Collects explicit or same-name mappings from reference type keys to concrete types. */ + private void collectTypeMappings( + String mapping, Map> mappings) { + for (ASTCDType concreteType : support.concreteIndex().types()) { + Optional explicit = support.stereotypeValue(concreteType, mapping); + if (explicit.isPresent()) { + support + .findReferenceType(explicit.get()) + .ifPresent(reference -> support.addMapping(mappings, reference, concreteType.getSymbol())); + } else if (confParams.contains(CDConfParameter.NAME_MAPPING)) { + support + .findReferenceType(concreteType.getName()) + .ifPresent(reference -> support.addMapping(mappings, reference, concreteType.getSymbol())); + } + } + } + + /** + * Collects field and method mappings inside the reference owners already selected for each + * concrete type. Explicit stereotypes take precedence over same-name/signature matching. + */ + private void collectMemberMappings( + String mapping, Map> mappings) { + for (ASTCDType concreteType : support.concreteIndex().types()) { + List referenceOwners = support.mappedReferenceOwners(mappings, concreteType); + if (referenceOwners.isEmpty()) { + // A member mapping is meaningful only after its concrete owner has a deterministic + // reference-type mapping. Searching all reference owners here would let a typo in the + // owner's explicit stereotype attach its members to unrelated same-named declarations. + continue; + } + for (ASTCDAttribute concreteField : concreteType.getCDAttributeList()) { + Optional explicit = support.stereotypeValue(concreteField, mapping); + if (explicit.isPresent()) { + support + .findReferenceField(referenceOwners, explicit.get()) + .ifPresent(reference -> support.addMapping(mappings, reference, concreteField.getSymbol())); + } else if (confParams.contains(CDConfParameter.NAME_MAPPING)) { + support + .findSameNameField(referenceOwners, concreteField) + .ifPresent(reference -> support.addMapping(mappings, reference, concreteField.getSymbol())); + } + } + for (ASTCDMethod concreteMethod : concreteType.getCDMethodList()) { + Optional explicit = support.stereotypeValue(concreteMethod, mapping); + if (explicit.isPresent()) { + support + .findReferenceMethod(referenceOwners, explicit.get()) + .ifPresent(reference -> support.addMapping(mappings, reference, concreteMethod.getSymbol())); + } else if (confParams.contains(CDConfParameter.NAME_MAPPING)) { + support + .findSameSignatureMethod(referenceOwners, concreteMethod) + .ifPresent(reference -> support.addMapping(mappings, reference, concreteMethod.getSymbol())); + } + } + } + } + + /** + * Expands manual {@code <>} mappings from already-known incarnations. A + * {@code forEach} reference may copy a type/member mapping directly or derive concrete accessor + * method names from a mapped field. + */ + private void collectForEachMappings( + Map> mappings) { + for (ASTCDType referenceType : support.referenceIndex().types()) { + support + .stereotypeValue(referenceType, "forEach") + .flatMap(support::findReferenceType) + .ifPresent( + target -> copyIncarnations(mappings, target, StableElementKey.type(referenceType))); + + for (ASTCDAttribute referenceField : referenceType.getCDAttributeList()) { + support.stereotypeValue(referenceField, "forEach").ifPresent( + name -> { + Optional target = + support.findReferenceField(List.of(referenceType), name); + if (target.isEmpty()) { + target = support.findReferenceMethod(List.of(referenceType), name); + } + target.ifPresent( + key -> copyIncarnations( + mappings, key, StableElementKey.field(referenceType, referenceField))); + }); + } + for (ASTCDMethod referenceMethod : referenceType.getCDMethodList()) { + Optional targetName = support.stereotypeValue(referenceMethod, "forEach"); + if (targetName.isEmpty()) { + continue; + } + Optional targetMethod = + support.findReferenceMethod(List.of(referenceType), targetName.get()); + if (targetMethod.isPresent()) { + copyIncarnations( + mappings, + targetMethod.get(), + StableElementKey.method(referenceType, referenceMethod)); + continue; + } + support + .findReferenceField(List.of(referenceType), targetName.get()) + .ifPresent( + target -> collectForEachMethods(mappings, target, referenceType, referenceMethod)); + } + } + } + + /** + * Finds concrete methods corresponding to a {@code forEach} method whose target is a mapped + * field, accepting the supported exact and camel-case name substitutions. + */ + private void collectForEachMethods( + Map> mappings, + StableElementKey targetField, + ASTCDType referenceOwner, + ASTCDMethod referenceMethod) { + List targetIncarnations = mappings.get(targetField); + if (targetIncarnations == null) { + return; + } + for (IncarnationContext.MappedElement incarnation : targetIncarnations) { + if (!(incarnation.symbol().getAstNode() instanceof ASTCDAttribute concreteField)) { + continue; + } + Optional owner = support.concreteIndex().ownerOf(concreteField); + if (owner.isEmpty()) { + continue; + } + Set names = + forEachMethodNames( + referenceMethod.getName(), targetField.getName(), concreteField.getName()); + for (ASTCDMethod concreteMethod : owner.get().getCDMethodList()) { + if (names.contains(concreteMethod.getName()) + && concreteMethod.getCDParameterList().size() + == referenceMethod.getCDParameterList().size()) { + support.addMapping( + mappings, + StableElementKey.method(referenceOwner, referenceMethod), + concreteMethod.getSymbol()); + } + } + } + } + + /** Copies all concrete incarnations of one stable reference key to another reference key. */ + private void copyIncarnations( + Map> mappings, + StableElementKey target, + StableElementKey reference) { + List incarnations = mappings.get(target); + if (incarnations == null) { + return; + } + for (IncarnationContext.MappedElement incarnation : incarnations) { + support.addMapping(mappings, reference, incarnation.symbol()); + } + } + + /** + * Produces candidate concrete method names by replacing a complete reference-name segment while + * preserving its capitalization form. + */ + private Set forEachMethodNames( + String methodName, String referenceTarget, String concreteTarget) { + Set result = new LinkedHashSet<>(); + if (methodName.equals(referenceTarget)) { + result.add(concreteTarget); + } + if (methodName.equals(JavaSourceNames.capitalize(referenceTarget))) { + result.add(JavaSourceNames.capitalize(concreteTarget)); + } + replaceNameSegment(methodName, referenceTarget, concreteTarget).ifPresent(result::add); + replaceNameSegment( + methodName, + JavaSourceNames.capitalize(referenceTarget), + JavaSourceNames.capitalize(concreteTarget)) + .ifPresent(result::add); + replaceNameSegment( + methodName, + JavaSourceNames.uncapitalize(referenceTarget), + JavaSourceNames.uncapitalize(concreteTarget)) + .ifPresent(result::add); + return result; + } + + /** + * Replaces only a complete camel-case name segment. For example, replacing {@code User} in + * {@code findUserById} yields {@code findCustomerById}, but replacing {@code Us} yields no match. + */ + private static Optional replaceNameSegment( + String name, String referenceSegment, String concreteSegment) { + if (name == null + || referenceSegment == null + || referenceSegment.isEmpty() + || concreteSegment == null + || concreteSegment.isEmpty()) { + return Optional.empty(); + } + List segments = splitNameSegments(name); + StringBuilder rewritten = new StringBuilder(name.length() + concreteSegment.length()); + boolean changed = false; + for (String segment : segments) { + if (segment.equals(referenceSegment)) { + rewritten.append(concreteSegment); + changed = true; + } else { + rewritten.append(segment); + } + } + return changed ? Optional.of(rewritten.toString()) : Optional.empty(); + } + + /** Splits a camel-case or acronym-containing Java identifier into capitalization segments. */ + private static List splitNameSegments(String name) { + List result = new ArrayList<>(); + int start = 0; + for (int i = 1; i < name.length(); i++) { + char previous = name.charAt(i - 1); + char current = name.charAt(i); + if (Character.isUpperCase(current) + && (Character.isLowerCase(previous) + || (i + 1 < name.length() && Character.isLowerCase(name.charAt(i + 1))))) { + result.add(name.substring(start, i)); + start = i; + } + } + result.add(name.substring(start)); + return result; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/StableElementKey.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/StableElementKey.java new file mode 100644 index 00000000..95ddc06d --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/StableElementKey.java @@ -0,0 +1,255 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.symboltable.ISymbol; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +/** + * Stable identity for mapped CD elements. Symbols are still kept as metadata, but lookups should + * use these keys so independently loaded ASTs can resolve the same mapped element. + */ +public final class StableElementKey { + /** Supported CD element identity kinds. */ + public enum Kind { + /** CD class, interface, or enum identity. */ + TYPE, + /** Owner, name, and normalized type identity. */ + FIELD, + /** Owner, name, normalized parameters, and optional return type identity. */ + METHOD + } + + private final Kind kind; + private final String ownerType; + private final String name; + private final String fieldKind; + private final List parameterTypes; + private final List parameterTypeIdentities; + private final List parameterTypeSources; + private final String returnType; + + private StableElementKey( + Kind kind, + String ownerType, + String name, + String fieldKind, + List parameterTypes, + String returnType) { + this.kind = Objects.requireNonNull(kind); + this.ownerType = normalize(ownerType); + this.name = normalize(name); + this.fieldKind = normalizeTypeIdentity(fieldKind); + this.parameterTypes = + parameterTypes == null + ? List.of() + : Collections.unmodifiableList( + parameterTypes.stream().map(StableElementKey::normalizeType).toList()); + this.parameterTypeIdentities = + parameterTypes == null + ? List.of() + : Collections.unmodifiableList( + parameterTypes.stream().map(StableElementKey::normalizeParameterIdentity).toList()); + this.parameterTypeSources = + parameterTypes == null + ? List.of() + : Collections.unmodifiableList( + parameterTypes.stream().map(StableElementKey::normalizeTypeSource).toList()); + this.returnType = normalizeTypeIdentity(returnType); + } + + /** Creates a stable key for a CD type AST. */ + public static StableElementKey type(ASTCDType type) { + return type(type.getName()); + } + + /** Creates a stable key for a type name. */ + public static StableElementKey type(String typeName) { + return new StableElementKey(Kind.TYPE, null, typeName, null, List.of(), null); + } + + /** Creates an owner-aware stable key for a CD attribute AST. */ + public static StableElementKey field(ASTCDType owner, ASTCDAttribute attribute) { + return field( + owner.getName(), + attribute.getName(), + JavaSourceNames.printQualifiedType(attribute.getMCType())); + } + + /** Creates an owner-aware field key from normalized identity components. */ + public static StableElementKey field(String ownerType, String fieldName, String fieldKind) { + return new StableElementKey(Kind.FIELD, ownerType, fieldName, fieldKind, List.of(), null); + } + + /** Creates an owner- and signature-aware stable key for a CD method AST. */ + public static StableElementKey method(ASTCDType owner, ASTCDMethod method) { + List parameters = new ArrayList<>(); + for (ASTCDParameter parameter : method.getCDParameterList()) { + parameters.add(JavaSourceNames.printQualifiedType(parameter.getMCType())); + } + String returnType = JavaSourceNames.printQualifiedReturnType(method); + return method(owner.getName(), method.getName(), parameters, returnType); + } + + /** Creates a method key without return-type identity. */ + public static StableElementKey method(String ownerType, String methodName, List parameterTypes) { + return method(ownerType, methodName, parameterTypes, null); + } + + /** Creates a method key with optional return-type identity. */ + public static StableElementKey method( + String ownerType, String methodName, List parameterTypes, String returnType) { + return new StableElementKey(Kind.METHOD, ownerType, methodName, null, parameterTypes, returnType); + } + + /** Builds a stable key for a symbol using the owning CD index for member ownership. */ + public static Optional fromSymbol(ISymbol symbol, CDModelIndex index) { + if (symbol == null || index == null) { + return Optional.empty(); + } + if (symbol.getAstNode() instanceof ASTCDType type) { + return Optional.of(type(type)); + } + if (symbol.getAstNode() instanceof ASTCDAttribute attribute) { + return index.ownerOf(attribute).map(owner -> field(owner, attribute)); + } + if (symbol.getAstNode() instanceof ASTCDMethod method) { + return index.ownerOf(method).map(owner -> method(owner, method)); + } + return Optional.empty(); + } + + public Kind getKind() { + return kind; + } + + public Optional getOwnerType() { + return Optional.ofNullable(ownerType); + } + + public String getName() { + return name; + } + + public Optional getFieldKind() { + return Optional.ofNullable(fieldKind); + } + + /** + * Returns normalized simple-name parameter types for Java mutation and lookup consumers. + * Equality and hashing use a separate qualification-preserving identity. + */ + public List getParameterTypes() { + return parameterTypes; + } + + /** + * Returns the supplied parameter spellings with qualification intact. Stable-key equality uses a + * normalized form of these source identities so overloads such as {@code alpha.Role} and {@code + * beta.Role} remain distinct. + */ + public List getParameterTypeSources() { + return parameterTypeSources; + } + + public Optional getReturnType() { + return Optional.ofNullable(returnType); + } + + /** Returns the same member identity anchored to a resolved Java declaring type. */ + public StableElementKey withOwnerType(String resolvedOwnerType) { + if (kind == Kind.TYPE) { + throw new IllegalStateException("A type key has no declaring owner"); + } + return new StableElementKey( + kind, + resolvedOwnerType, + name, + fieldKind, + parameterTypeSources, + returnType); + } + + /** Returns whether both keys identify the same element without considering method return type. */ + public boolean sameSignatureIgnoringReturn(StableElementKey other) { + return other != null + && kind == other.kind + && Objects.equals(ownerType, other.ownerType) + && Objects.equals(name, other.name) + && Objects.equals(parameterTypeIdentities, other.parameterTypeIdentities); + } + + /** Returns the deterministic owner/name/type signature used in diagnostics and sorting. */ + public String signature() { + if (kind == Kind.TYPE) { + return name; + } + if (kind == Kind.FIELD) { + return ownerType + "." + name + ":" + fieldKind; + } + return ownerType + "." + name + "(" + String.join(",", parameterTypeIdentities) + ")"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof StableElementKey other)) { + return false; + } + return kind == other.kind + && Objects.equals(ownerType, other.ownerType) + && Objects.equals(name, other.name) + && Objects.equals(fieldKind, other.fieldKind) + && Objects.equals(parameterTypeIdentities, other.parameterTypeIdentities) + && Objects.equals(returnType, other.returnType); + } + + @Override + public int hashCode() { + return Objects.hash(kind, ownerType, name, fieldKind, parameterTypeIdentities, returnType); + } + + @Override + public String toString() { + return signature() + (returnType == null ? "" : ":" + returnType); + } + + private static String normalize(String value) { + if (value == null || value.isBlank()) { + return null; + } + return value.trim(); + } + + private static String normalizeType(String value) { + String normalized = normalize(value); + if (normalized == null) { + return null; + } + return JavaSourceNames.normalizeType(normalized); + } + + private static String normalizeTypeSource(String value) { + String normalized = normalize(value); + return normalized == null ? "Object" : normalized; + } + + private static String normalizeParameterIdentity(String value) { + return JavaSourceNames.canonicalType(normalizeTypeSource(value)); + } + + private static String normalizeTypeIdentity(String value) { + String normalized = normalize(value); + return normalized == null ? null : JavaSourceNames.canonicalType(normalized); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AdaptationConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AdaptationConflictCheck.java new file mode 100644 index 00000000..811c0220 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AdaptationConflictCheck.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +/** Interface for manual adaptation conflict checks. */ +public interface AdaptationConflictCheck { + + void check(ConflictDetectionContext context, ConflictCollector conflicts); +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AmbiguousStereotypeConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AmbiguousStereotypeConflictCheck.java new file mode 100644 index 00000000..13042425 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AmbiguousStereotypeConflictCheck.java @@ -0,0 +1,46 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdconformance.CDConfParameter; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +final class AmbiguousStereotypeConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (String mapping : context.mappings()) { + for (ASTCDType concreteType : context.concreteTypes().values()) { + Optional referenceTypeName = context.stereotypeValue(concreteType, mapping); + if (referenceTypeName.isEmpty() && context.confParams().contains(CDConfParameter.NAME_MAPPING)) { + referenceTypeName = Optional.of(concreteType.getName()); + } + ASTCDType referenceType = + context.referenceTypes().get(referenceTypeName.map(context::simpleName).orElse("")); + if (referenceType == null) { + continue; + } + Map methodsByName = new HashMap<>(); + for (ASTCDMethod method : referenceType.getCDMethodList()) { + methodsByName.merge(method.getName(), 1L, Long::sum); + } + for (ASTCDMethod concreteMethod : concreteType.getCDMethodList()) { + Optional stereotype = context.stereotypeValue(concreteMethod, mapping); + if (stereotype.isPresent() + && !stereotype.get().contains("(") + && methodsByName.getOrDefault(context.simpleName(stereotype.get()), 0L) > 1) { + conflicts.conflict( + mapping, + "ambiguous overloaded method stereotype", + concreteType.getName() + "." + concreteMethod.getName() + + " maps to overloaded reference method '" + + stereotype.get() + + "'"); + } + } + } + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AssociationConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AssociationConflictCheck.java new file mode 100644 index 00000000..9825dd58 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/AssociationConflictCheck.java @@ -0,0 +1,152 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cdassociation._ast.ASTCDAssocSide; +import de.monticore.cdassociation._ast.ASTCDAssociation; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import java.util.Set; + +final class AssociationConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (String mapping : context.mappings()) { + IncarnationContext incarnationContext = context.contexts().get(mapping); + if (incarnationContext == null) { + continue; + } + for (ASTCDAssociation referenceAssociation : context.referenceIndex().associations()) { + validateAssociation(context, conflicts, mapping, incarnationContext, referenceAssociation); + } + } + } + + private void validateAssociation( + ConflictDetectionContext context, + ConflictCollector conflicts, + String mapping, + IncarnationContext incarnationContext, + ASTCDAssociation referenceAssociation) { + String refLeft = context.simpleName(referenceAssociation.getLeftQualifiedName().getQName()); + String refRight = context.simpleName(referenceAssociation.getRightQualifiedName().getQName()); + Set leftTargets = context.concreteTypeNamesFor(incarnationContext, refLeft); + Set rightTargets = context.concreteTypeNamesFor(incarnationContext, refRight); + if (leftTargets.isEmpty() || rightTargets.isEmpty()) { + return; + } + for (ASTCDAssociation concreteAssociation : context.concreteIndex().associations()) { + String conLeft = context.simpleName(concreteAssociation.getLeftQualifiedName().getQName()); + String conRight = context.simpleName(concreteAssociation.getRightQualifiedName().getQName()); + boolean same = leftTargets.contains(conLeft) && rightTargets.contains(conRight); + boolean reverse = leftTargets.contains(conRight) && rightTargets.contains(conLeft); + if (same && reverse) { + conflicts.conflict( + mapping, + "ambiguous association direction", + context.associationName(concreteAssociation) + " can match " + + context.associationName(referenceAssociation) + + " in both directions"); + } else if (same) { + validateAssociationSides( + context, + conflicts, + mapping, + referenceAssociation.getLeft(), + concreteAssociation.getLeft(), + referenceAssociation.getRight(), + concreteAssociation.getRight(), + referenceAssociation, + concreteAssociation); + } else if (reverse) { + validateAssociationSides( + context, + conflicts, + mapping, + referenceAssociation.getLeft(), + concreteAssociation.getRight(), + referenceAssociation.getRight(), + concreteAssociation.getLeft(), + referenceAssociation, + concreteAssociation); + } + } + validateAssociationRoleConflicts( + context, conflicts, mapping, referenceAssociation, leftTargets, rightTargets); + } + + private void validateAssociationSides( + ConflictDetectionContext context, + ConflictCollector conflicts, + String mapping, + ASTCDAssocSide referenceLeft, + ASTCDAssocSide concreteLeft, + ASTCDAssocSide referenceRight, + ASTCDAssocSide concreteRight, + ASTCDAssociation referenceAssociation, + ASTCDAssociation concreteAssociation) { + validateCardinality( + context, conflicts, mapping, referenceLeft, concreteLeft, referenceAssociation, concreteAssociation); + validateCardinality( + context, conflicts, mapping, referenceRight, concreteRight, referenceAssociation, concreteAssociation); + } + + private void validateCardinality( + ConflictDetectionContext context, + ConflictCollector conflicts, + String mapping, + ASTCDAssocSide referenceSide, + ASTCDAssocSide concreteSide, + ASTCDAssociation referenceAssociation, + ASTCDAssociation concreteAssociation) { + if (referenceSide.isPresentCDCardinality() + && concreteSide.isPresentCDCardinality() + && !referenceSide.getCDCardinality().deepEquals(concreteSide.getCDCardinality())) { + conflicts.conflict( + mapping, + "association cardinality conflict", + context.associationName(concreteAssociation) + " has a cardinality incompatible with " + + context.associationName(referenceAssociation)); + } + } + + private void validateAssociationRoleConflicts( + ConflictDetectionContext context, + ConflictCollector conflicts, + String mapping, + ASTCDAssociation referenceAssociation, + Set leftTargets, + Set rightTargets) { + for (ConflictDetectionContext.RoleFieldCandidate referenceRole : + context.roleFieldCandidates(referenceAssociation)) { + Set ownerTargets = + referenceRole.owner().equals(context.simpleName(referenceAssociation.getLeftQualifiedName().getQName())) + ? leftTargets + : rightTargets; + Set targetTargets = + referenceRole.owner().equals(context.simpleName(referenceAssociation.getLeftQualifiedName().getQName())) + ? rightTargets + : leftTargets; + if (ownerTargets.isEmpty()) { + continue; + } + for (ASTCDAssociation other : context.concreteIndex().associations()) { + for (ConflictDetectionContext.RoleFieldCandidate concreteRole : + context.roleFieldCandidates(other)) { + if (!referenceRole.role().equals(concreteRole.role())) { + continue; + } + if (ownerTargets.contains(concreteRole.owner()) + && targetTargets.contains(concreteRole.target())) { + continue; + } + if (ownerTargets.contains(concreteRole.owner())) { + conflicts.conflict( + mapping, + "association role field conflict", + "role '" + referenceRole.role() + "' may create a duplicate Java field on " + + concreteRole.owner()); + } + } + } + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConcreteMemberConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConcreteMemberConflictCheck.java new file mode 100644 index 00000000..5b3d90c0 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConcreteMemberConflictCheck.java @@ -0,0 +1,78 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.utils.CDTypeRelations; +import java.util.ArrayDeque; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +final class ConcreteMemberConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (ASTCDType type : context.concreteTypes().values()) { + Map fields = new LinkedHashMap<>(); + for (ASTCDAttribute field : type.getCDAttributeList()) { + String previous = fields.putIfAbsent(field.getName(), context.printType(field)); + if (previous != null && !previous.equals(context.printType(field))) { + conflicts.conflict( + "structure", + "duplicate field", + type.getName() + "." + field.getName() + " has incompatible types"); + } + } + validateInheritedFieldConflicts(context, conflicts, type, fields); + + Map methods = new LinkedHashMap<>(); + for (ASTCDMethod method : type.getCDMethodList()) { + String previous = methods.putIfAbsent(context.methodSignature(method), context.returnType(method)); + if (previous != null && !previous.equals(context.returnType(method))) { + conflicts.conflict( + "structure", + "duplicate method", + type.getName() + "." + context.methodSignature(method) + " has incompatible return types"); + } + } + } + } + + private void validateInheritedFieldConflicts( + ConflictDetectionContext context, + ConflictCollector conflicts, + ASTCDType type, + Map ownFields) { + ArrayDeque queue = new ArrayDeque<>(); + CDTypeRelations.firstSuperclassName(type).map(context::simpleName).ifPresent(queue::add); + for (String interfaceName : CDTypeRelations.interfaceNames(type)) { + queue.add(context.simpleName(interfaceName)); + } + Set visited = new HashSet<>(); + while (!queue.isEmpty()) { + ASTCDType parent = context.concreteTypes().get(queue.removeFirst()); + if (parent == null || !visited.add(parent.getName())) { + continue; + } + for (ASTCDAttribute inherited : parent.getCDAttributeList()) { + String ownType = ownFields.get(inherited.getName()); + if (ownType != null && !ownType.equals(context.printType(inherited))) { + conflicts.conflict( + "structure", + "inherited field conflict", + type.getName() + "." + inherited.getName() + + " conflicts with inherited " + + parent.getName() + + "." + + inherited.getName()); + } + } + CDTypeRelations.firstSuperclassName(parent).map(context::simpleName).ifPresent(queue::add); + for (String interfaceName : CDTypeRelations.interfaceNames(parent)) { + queue.add(context.simpleName(interfaceName)); + } + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConcreteTypeStructureConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConcreteTypeStructureConflictCheck.java new file mode 100644 index 00000000..1e3bdab2 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConcreteTypeStructureConflictCheck.java @@ -0,0 +1,99 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; +import de.monticore.codeAdaption.utils.CDTypeRelations; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +final class ConcreteTypeStructureConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (ASTCDType type : context.concreteTypes().values()) { + if (type instanceof ASTCDClass cdClass && cdClass.getSuperclassList().size() > 1) { + conflicts.conflict( + "structure", + "illegal multiple class supertypes", + type.getName() + " extends more than one class"); + } + validateSuperTypeKinds(context, conflicts, type); + } + validateInheritanceCycles(context, conflicts); + } + + private void validateSuperTypeKinds( + ConflictDetectionContext context, ConflictCollector conflicts, ASTCDType type) { + if (type instanceof ASTCDClass) { + CDTypeRelations.firstSuperclassName(type) + .map(context::simpleName) + .map(context.concreteTypes()::get) + .filter(ASTCDInterface.class::isInstance) + .ifPresent( + superType -> + conflicts.conflict( + "structure", + "class extends interface", + type.getName() + " extends interface " + superType.getName())); + for (String interfaceName : CDTypeRelations.interfaceNames(type)) { + ASTCDType implemented = context.concreteTypes().get(context.simpleName(interfaceName)); + if (implemented != null && !(implemented instanceof ASTCDInterface)) { + conflicts.conflict( + "structure", + "class implements non-interface", + type.getName() + " implements " + implemented.getName()); + } + } + } else if (type instanceof ASTCDInterface) { + for (String interfaceName : CDTypeRelations.interfaceNames(type)) { + ASTCDType extended = context.concreteTypes().get(context.simpleName(interfaceName)); + if (extended != null && !(extended instanceof ASTCDInterface)) { + conflicts.conflict( + "structure", + "interface extends non-interface", + type.getName() + " extends " + extended.getName()); + } + } + } + } + + private void validateInheritanceCycles(ConflictDetectionContext context, ConflictCollector conflicts) { + Map> edges = new LinkedHashMap<>(); + for (ASTCDType type : context.concreteTypes().values()) { + Set parents = new LinkedHashSet<>(); + CDTypeRelations.firstSuperclassName(type).map(context::simpleName).ifPresent(parents::add); + for (String interfaceName : CDTypeRelations.interfaceNames(type)) { + parents.add(context.simpleName(interfaceName)); + } + parents.removeIf(parent -> !context.concreteTypes().containsKey(parent)); + edges.put(type.getName(), parents); + } + for (String typeName : edges.keySet()) { + if (hasCycle(typeName, edges, new LinkedHashSet<>(), new HashSet<>())) { + conflicts.conflict("structure", "inheritance cycle", "cycle reaches " + typeName); + } + } + } + + private boolean hasCycle( + String current, Map> edges, Set active, Set done) { + if (active.contains(current)) { + return true; + } + if (!done.add(current)) { + return false; + } + active.add(current); + for (String parent : edges.getOrDefault(current, Set.of())) { + if (hasCycle(parent, edges, active, done)) { + return true; + } + } + active.remove(current); + return false; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConflictCollector.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConflictCollector.java new file mode 100644 index 00000000..9534e2dc --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConflictCollector.java @@ -0,0 +1,32 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** Collect conflict messages. */ +public final class ConflictCollector { + + private final List conflicts = new ArrayList<>(); + + public void conflict(String mapping, String kind, String detail) { + conflicts.add( + "[" + + mapping + + "] " + + kind + + ": " + + detail + + ". Run with useConcretization=true or make the manual mapping explicit."); + } + + public boolean hasConflicts() { + return !conflicts.isEmpty(); + } + + public List formattedConflicts() { + List formatted = new ArrayList<>(conflicts); + Collections.sort(formatted); + return formatted; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConflictDetectionContext.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConflictDetectionContext.java new file mode 100644 index 00000000..38b252a7 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ConflictDetectionContext.java @@ -0,0 +1,231 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdassociation._ast.ASTCDAssociation; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.CDTypeRelations; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** Shared helper API for adaptation conflict checks. */ +public final class ConflictDetectionContext { + + private final Set mappings; + private final Map contexts; + private final Set confParams; + private final boolean useCommonParentForMultipleIncarnations; + private final CDModelIndex referenceIndex; + private final CDModelIndex concreteIndex; + private final Map referenceTypes = new LinkedHashMap<>(); + private final Map concreteTypes = new LinkedHashMap<>(); + + public ConflictDetectionContext( + CDModelIndex referenceIndex, + CDModelIndex concreteIndex, + Set mappings, + Map contexts, + Set confParams, + boolean useCommonParentForMultipleIncarnations) { + this.mappings = mappings; + this.contexts = contexts; + this.confParams = confParams; + this.useCommonParentForMultipleIncarnations = useCommonParentForMultipleIncarnations; + this.referenceIndex = referenceIndex; + this.concreteIndex = concreteIndex; + referenceIndex.types().forEach(type -> referenceTypes.put(type.getName(), type)); + concreteIndex.types().forEach(type -> concreteTypes.put(type.getName(), type)); + } + + public Set mappings() { + return mappings; + } + + public Map contexts() { + return contexts; + } + + public Set confParams() { + return confParams; + } + + public boolean useCommonParentForMultipleIncarnations() { + return useCommonParentForMultipleIncarnations; + } + + public CDModelIndex referenceIndex() { + return referenceIndex; + } + + public CDModelIndex concreteIndex() { + return concreteIndex; + } + + public Map referenceTypes() { + return referenceTypes; + } + + public Map concreteTypes() { + return concreteTypes; + } + + public Set concreteTypeNamesFor(IncarnationContext context, String referenceTypeName) { + Set result = new LinkedHashSet<>(); + for (var incarnation : context.getIncarnations(StableElementKey.type(referenceTypeName))) { + result.add(incarnation.key().getName()); + } + if (result.isEmpty() && concreteTypes.containsKey(referenceTypeName)) { + result.add(referenceTypeName); + } + return result; + } + + public Set concreteParentNames(ASTCDType concrete) { + Set parents = new LinkedHashSet<>(); + CDTypeRelations.firstSuperclassName(concrete).map(this::simpleName).ifPresent(parents::add); + for (String interfaceName : CDTypeRelations.interfaceNames(concrete)) { + parents.add(simpleName(interfaceName)); + } + parents.removeIf(parent -> !concreteIndex.hasType(parent)); + return parents; + } + + public Optional stereotypeValue(ASTCDType cdType, String mapping) { + if (cdType.getModifier() == null || !cdType.getModifier().isPresentStereotype()) { + return Optional.empty(); + } + for (var stereotype : cdType.getModifier().getStereotype().getValuesList()) { + if (mapping.equals(stereotype.getName())) { + try { + return Optional.ofNullable(stereotype.getValue()); + } catch (Exception ignored) { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + public Optional stereotypeValue(ASTCDMethod cdMethod, String mapping) { + if (cdMethod.getModifier() == null || !cdMethod.getModifier().isPresentStereotype()) { + return Optional.empty(); + } + for (var stereotype : cdMethod.getModifier().getStereotype().getValuesList()) { + if (mapping.equals(stereotype.getName())) { + try { + return Optional.ofNullable(stereotype.getValue()); + } catch (Exception ignored) { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + public Optional stereotypeValue(ASTCDAttribute cdAttribute, String mapping) { + if (cdAttribute.getModifier() == null || !cdAttribute.getModifier().isPresentStereotype()) { + return Optional.empty(); + } + for (var stereotype : cdAttribute.getModifier().getStereotype().getValuesList()) { + if (mapping.equals(stereotype.getName())) { + try { + return Optional.ofNullable(stereotype.getValue()); + } catch (Exception ignored) { + return Optional.empty(); + } + } + } + return Optional.empty(); + } + + public Optional forEachValue(Object element) { + if (element instanceof ASTCDType type) { + return stereotypeValue(type, "forEach"); + } + if (element instanceof ASTCDAttribute attribute) { + return stereotypeValue(attribute, "forEach"); + } + if (element instanceof ASTCDMethod method) { + return stereotypeValue(method, "forEach"); + } + return Optional.empty(); + } + + public String typeKind(ASTCDType type) { + if (type instanceof ASTCDClass) { + return "class"; + } + if (type instanceof ASTCDInterface) { + return "interface"; + } + if (type instanceof ASTCDEnum) { + return "enum"; + } + return type.getClass().getSimpleName(); + } + + public String printType(ASTCDAttribute attribute) { + return JavaSourceNames.printNormalizedFieldType(attribute); + } + + public String methodSignature(ASTCDMethod method) { + return JavaSourceNames.methodSignature(method); + } + + public String returnType(ASTCDMethod method) { + return JavaSourceNames.printNormalizedReturnType(method); + } + + public boolean isAny(String type) { + return "any".equals(type == null ? "" : type.trim()); + } + + public String printedType(Object astType) { + if (astType instanceof de.monticore.types.mcbasictypes._ast.ASTMCType type) { + return JavaLoader.print(type); + } + return astType == null ? "" : astType.toString(); + } + + public String associationName(ASTCDAssociation association) { + if (association.isPresentName()) { + return association.getName(); + } + return association.getLeftQualifiedName().getQName() + + "--" + + association.getRightQualifiedName().getQName(); + } + + public String simpleName(String name) { + return JavaSourceNames.simpleName(name); + } + + public List roleFieldCandidates(ASTCDAssociation association) { + List result = new ArrayList<>(); + String left = simpleName(association.getLeftQualifiedName().getQName()); + String right = simpleName(association.getRightQualifiedName().getQName()); + if (association.getLeft().isPresentCDRole()) { + result.add(new RoleFieldCandidate(right, association.getLeft().getCDRole().getName(), left)); + } + if (association.getRight().isPresentCDRole()) { + result.add(new RoleFieldCandidate(left, association.getRight().getCDRole().getName(), right)); + } + return result; + } + + public record RoleFieldCandidate(String owner, String role, String target) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/DefaultConflictChecks.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/DefaultConflictChecks.java new file mode 100644 index 00000000..2aa549c7 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/DefaultConflictChecks.java @@ -0,0 +1,21 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import java.util.List; + +/** Registry for the conflict checks. */ +public final class DefaultConflictChecks { + + private DefaultConflictChecks() {} + + public static List all() { + return List.of( + new ManualMappingConflictCheck(), + new ConcreteTypeStructureConflictCheck(), + new ConcreteMemberConflictCheck(), + new EnumConflictCheck(), + new AssociationConflictCheck(), + new UnderspecifiedTypeConflictCheck(), + new AmbiguousStereotypeConflictCheck(), + new ForEachConflictCheck()); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/EnumConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/EnumConflictCheck.java new file mode 100644 index 00000000..63c4eef1 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/EnumConflictCheck.java @@ -0,0 +1,62 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +final class EnumConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (IncarnationContext incarnationContext : context.contexts().values()) { + for (Map.Entry> entry : + incarnationContext.getMappings().entrySet()) { + if (entry.getKey().getKind() != StableElementKey.Kind.TYPE) { + continue; + } + ASTCDType reference = context.referenceTypes().get(entry.getKey().getName()); + if (!(reference instanceof ASTCDEnum referenceEnum)) { + continue; + } + for (IncarnationContext.MappedElement target : entry.getValue()) { + ASTCDType concrete = context.concreteTypes().get(target.key().getName()); + if (concrete instanceof ASTCDEnum concreteEnum) { + validateEnumOrder(context, conflicts, incarnationContext.getMappingName(), referenceEnum, concreteEnum); + } + } + } + } + } + + private void validateEnumOrder( + ConflictDetectionContext context, + ConflictCollector conflicts, + String mapping, + ASTCDEnum referenceEnum, + ASTCDEnum concreteEnum) { + Map concretePositions = new HashMap<>(); + for (int i = 0; i < concreteEnum.getCDEnumConstantList().size(); i++) { + concretePositions.put(concreteEnum.getCDEnumConstant(i).getName(), i); + } + int lastSeen = -1; + for (var referenceConstant : referenceEnum.getCDEnumConstantList()) { + Integer concretePosition = concretePositions.get(referenceConstant.getName()); + if (concretePosition == null) { + continue; + } + if (concretePosition < lastSeen) { + conflicts.conflict( + mapping, + "enum order conflict", + concreteEnum.getName() + " orders reference constants differently from " + + referenceEnum.getName()); + return; + } + lastSeen = concretePosition; + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ForEachConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ForEachConflictCheck.java new file mode 100644 index 00000000..5c9a6483 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ForEachConflictCheck.java @@ -0,0 +1,63 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import java.util.Optional; + +final class ForEachConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (String mapping : context.mappings()) { + IncarnationContext incarnationContext = context.contexts().get(mapping); + if (incarnationContext == null) { + continue; + } + for (ASTCDType referenceType : context.referenceTypes().values()) { + validateForEachMapping( + context, conflicts, mapping, incarnationContext, StableElementKey.type(referenceType), referenceType); + for (ASTCDAttribute attribute : referenceType.getCDAttributeList()) { + validateForEachMapping( + context, + conflicts, + mapping, + incarnationContext, + StableElementKey.field(referenceType, attribute), + attribute); + } + for (ASTCDMethod method : referenceType.getCDMethodList()) { + validateForEachMapping( + context, + conflicts, + mapping, + incarnationContext, + StableElementKey.method(referenceType, method), + method); + } + } + } + } + + private void validateForEachMapping( + ConflictDetectionContext context, + ConflictCollector conflicts, + String mapping, + IncarnationContext incarnationContext, + StableElementKey key, + Object element) { + Optional forEachValue = context.forEachValue(element); + if (forEachValue.isEmpty()) { + return; + } + if (incarnationContext.getIncarnations(key).isEmpty()) { + conflicts.conflict( + mapping, + "missing forEach incarnation", + key.signature() + " declares forEach='" + forEachValue.get() + + "' but no concrete incarnation could be derived"); + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ManualMappingConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ManualMappingConflictCheck.java new file mode 100644 index 00000000..368d7dc8 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/ManualMappingConflictCheck.java @@ -0,0 +1,209 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +final class ManualMappingConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (IncarnationContext incarnationContext : context.contexts().values()) { + validateExplicitStereotypes(context, conflicts, incarnationContext); + for (Map.Entry> entry : + incarnationContext.getMappings().entrySet()) { + StableElementKey reference = entry.getKey(); + for (IncarnationContext.MappedElement mapped : entry.getValue()) { + StableElementKey concrete = mapped.key(); + if (reference.getKind() == StableElementKey.Kind.TYPE) { + validateTypeMapping(context, conflicts, incarnationContext, reference, concrete); + } + } + if (reference.getKind() == StableElementKey.Kind.FIELD && entry.getValue().size() > 1) { + validateFieldTargets( + conflicts, + incarnationContext.getMappingName(), + reference, + entry.getValue().stream().map(IncarnationContext.MappedElement::key).toList()); + } + if (reference.getKind() == StableElementKey.Kind.METHOD && entry.getValue().size() > 1) { + validateMethodTargets( + conflicts, + incarnationContext.getMappingName(), + reference, + entry.getValue().stream().map(IncarnationContext.MappedElement::key).toList()); + } + } + } + } + + /** + * Rejects active explicit stereotypes that the manual builder could not resolve. + * + *

The builder deliberately returns only valid stable-key relationships. Without this inverse + * check, a misspelled reference target would silently disappear from the context and a + * no-concrete-code run could still publish generated model shells without the intended adapted + * implementation. + */ + private void validateExplicitStereotypes( + ConflictDetectionContext context, + ConflictCollector conflicts, + IncarnationContext incarnationContext) { + String mapping = incarnationContext.getMappingName(); + for (ASTCDType concreteType : context.concreteIndex().types()) { + context + .stereotypeValue(concreteType, mapping) + .filter( + ignored -> + !hasConcreteTarget( + incarnationContext, StableElementKey.type(concreteType.getName()))) + .ifPresent( + target -> + conflicts.conflict( + mapping, + "unresolved explicit type stereotype", + concreteType.getName() + " names unknown reference target '" + target + "'")); + + for (var concreteField : concreteType.getCDAttributeList()) { + context + .stereotypeValue(concreteField, mapping) + .filter( + ignored -> + !hasConcreteTarget( + incarnationContext, + StableElementKey.field(concreteType, concreteField))) + .ifPresent( + target -> + conflicts.conflict( + mapping, + "unresolved explicit field stereotype", + concreteType.getName() + + "." + + concreteField.getName() + + " names unknown or owner-incompatible reference target '" + + target + + "'")); + } + + for (var concreteMethod : concreteType.getCDMethodList()) { + context + .stereotypeValue(concreteMethod, mapping) + .filter( + ignored -> + !hasConcreteTarget( + incarnationContext, + StableElementKey.method(concreteType, concreteMethod))) + .ifPresent( + target -> + conflicts.conflict( + mapping, + "unresolved explicit method stereotype", + concreteType.getName() + + "." + + concreteMethod.getName() + + " names unknown or owner-incompatible reference target '" + + target + + "'")); + } + } + } + + private boolean hasConcreteTarget( + IncarnationContext incarnationContext, StableElementKey concreteKey) { + return incarnationContext.getMappings().values().stream() + .flatMap(List::stream) + .map(IncarnationContext.MappedElement::key) + .anyMatch(concreteKey::equals); + } + + private void validateTypeMapping( + ConflictDetectionContext context, + ConflictCollector conflicts, + IncarnationContext incarnationContext, + StableElementKey referenceKey, + StableElementKey concreteKey) { + ASTCDType reference = context.referenceTypes().get(referenceKey.getName()); + ASTCDType concrete = context.concreteTypes().get(concreteKey.getName()); + if (reference == null || concrete == null) { + return; + } + if (!context.typeKind(reference).equals(context.typeKind(concrete))) { + if (canUseCommonParentForTypeMismatch(context, incarnationContext, referenceKey, reference, concrete)) { + return; + } + conflicts.conflict( + incarnationContext.getMappingName(), + "type-kind mismatch", + reference.getName() + " is a " + context.typeKind(reference) + " but " + + concrete.getName() + " is a " + context.typeKind(concrete)); + } + } + + private boolean canUseCommonParentForTypeMismatch( + ConflictDetectionContext context, + IncarnationContext incarnationContext, + StableElementKey referenceKey, + ASTCDType reference, + ASTCDType concrete) { + if (!context.useCommonParentForMultipleIncarnations()) { + return false; + } + if (reference instanceof ASTCDClass && concrete instanceof ASTCDInterface) { + long implementers = + context.concreteIndex().classes().stream() + .filter(type -> context.concreteIndex().isSubtypeOf(type.getName(), concrete.getName())) + .count(); + return implementers > 1; + } + if (!(reference instanceof ASTCDInterface) || !(concrete instanceof ASTCDClass)) { + return false; + } + return incarnationContext.getIncarnations(referenceKey).size() > 1 + && incarnationContext + .getGroupingFor(StableElementKey.type(concrete.getName())) + .isPresent(); + } + + private void validateFieldTargets( + ConflictCollector conflicts, + String mapping, + StableElementKey reference, + List targets) { + Map byOwnerAndName = new LinkedHashMap<>(); + for (StableElementKey target : targets) { + String key = target.getOwnerType().orElse("") + "." + target.getName(); + String type = target.getFieldKind().orElse("Object"); + String previous = byOwnerAndName.putIfAbsent(key, type); + if (previous != null && !previous.equals(type)) { + conflicts.conflict( + mapping, + "field target conflict", + reference.signature() + " maps to incompatible concrete fields " + targets); + } + } + } + + private void validateMethodTargets( + ConflictCollector conflicts, + String mapping, + StableElementKey reference, + List targets) { + Map bySignature = new LinkedHashMap<>(); + for (StableElementKey target : targets) { + String signature = target.signature(); + String returnType = target.getReturnType().orElse("void"); + String previous = bySignature.putIfAbsent(signature, returnType); + if (previous != null && !previous.equals(returnType)) { + conflicts.conflict( + mapping, + "method target conflict", + reference.signature() + " maps to incompatible concrete methods " + targets); + } + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/UnderspecifiedTypeConflictCheck.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/UnderspecifiedTypeConflictCheck.java new file mode 100644 index 00000000..d4209474 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/handler/multiIncarnation/conflict/UnderspecifiedTypeConflictCheck.java @@ -0,0 +1,105 @@ +package de.monticore.codeAdaption.handler.multiIncarnation.conflict; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.JavaLoader; + +final class UnderspecifiedTypeConflictCheck implements AdaptationConflictCheck { + + @Override + public void check(ConflictDetectionContext context, ConflictCollector conflicts) { + for (String mapping : context.mappings()) { + IncarnationContext incarnationContext = context.contexts().get(mapping); + if (incarnationContext == null) { + continue; + } + for (ASTCDType referenceType : context.referenceTypes().values()) { + boolean ownerMapped = + !incarnationContext.getIncarnations(StableElementKey.type(referenceType.getName())).isEmpty(); + if (!ownerMapped) { + continue; + } + for (ASTCDAttribute attribute : referenceType.getCDAttributeList()) { + if (context.isAny(JavaLoader.print(attribute.getMCType())) + && incarnationContext + .getIncarnations(StableElementKey.field(referenceType, attribute)) + .isEmpty() + && !hasMappedForEachDerivative( + context, incarnationContext, referenceType, attribute)) { + conflicts.conflict( + mapping, + "underspecified attribute type", + referenceType.getName() + "." + attribute.getName() + + " uses any without a concrete incarnation"); + } + } + for (ASTCDMethod method : referenceType.getCDMethodList()) { + boolean methodMapped = + !incarnationContext.getIncarnations(StableElementKey.method(referenceType, method)).isEmpty(); + if (context.isAny(JavaLoader.print(method.getMCReturnType())) + && !methodMapped + && !hasMappedForEachDerivative(context, incarnationContext, referenceType, method)) { + conflicts.conflict( + mapping, + "underspecified method return type", + referenceType.getName() + "." + method.getName() + + " returns any without a concrete incarnation"); + } + for (ASTCDParameter parameter : method.getCDParameterList()) { + if (context.isAny(JavaLoader.print(parameter.getMCType())) && !methodMapped) { + conflicts.conflict( + mapping, + "underspecified method parameter type", + referenceType.getName() + "." + method.getName() + + " has parameter " + + parameter.getName() + + " of type any"); + } + } + } + } + } + } + + private boolean hasMappedForEachDerivative( + ConflictDetectionContext context, + IncarnationContext incarnationContext, + ASTCDType owner, + ASTCDMethod target) { + String qualifiedTarget = owner.getName() + "." + target.getName(); + for (ASTCDAttribute attribute : owner.getCDAttributeList()) { + if (context.forEachValue(attribute) + .filter(value -> value.equals(target.getName()) || value.endsWith(qualifiedTarget)) + .isPresent() + && !incarnationContext + .getIncarnations(StableElementKey.field(owner, attribute)) + .isEmpty()) { + return true; + } + } + return false; + } + + private boolean hasMappedForEachDerivative( + ConflictDetectionContext context, + IncarnationContext incarnationContext, + ASTCDType owner, + ASTCDAttribute target) { + String qualifiedTarget = owner.getName() + "." + target.getName(); + for (ASTCDAttribute attribute : owner.getCDAttributeList()) { + if (context.forEachValue(attribute) + .filter(value -> value.equals(target.getName()) || value.equals(qualifiedTarget)) + .isPresent() + && !incarnationContext + .getIncarnations(StableElementKey.field(owner, attribute)) + .isEmpty()) { + return true; + } + } + return false; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/CodeMatching.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/CodeMatching.java index f557fe9f..998226f2 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/CodeMatching.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/CodeMatching.java @@ -4,26 +4,31 @@ import java.util.ArrayList; import java.util.List; -/*** - * Save matching information, each Matching is attached to an element (type,field,method,..) of - * the reference code. - * It saves the necessary implementation to build the concrete name of this element. +/** + * Describes how one handwritten Java element is related to elements of the reference CD. + * + *

The {@linkplain #getReferences() references} are reference-CD symbols, not concrete + * incarnations. During adaptation, they are resolved to concrete incarnations and inserted, in + * list order, into {@linkplain #getTemplate() the naming template}. For example, references + * {@code [Entity, Entity.id]} and template {@code find${}By${cap_first}} can produce {@code + * findPersonByNumber} when {@code Entity -> Person} and {@code id -> number}. + * + *

A matching may instead represent an explicit {@code @Adapt(ignore = true)} instruction. In + * that case {@link #mustBePerform()} returns {@code false}: the element remains relevant to + * filtering, but no adaptation is performed on it. */ public class CodeMatching { - /*** - * is true when the code element has to be ignored during the adaptation. - */ + /** Whether the matched Java element is intentionally excluded from adaptation. */ private boolean ignore = false; + private boolean explicitAnnotation; - /*** - * CD-references of the element - */ + /** Reference-CD symbols consumed by the naming templates, in placeholder order. */ private final List references = new ArrayList<>(); - /*** - * must be field with the concrete as argument reference to build the new name - */ + /** Template for adapting an existing handwritten name. */ private String template; + /** Optional template used when generating a new declaration. */ + private String generateTemplate; public CodeMatching(boolean ignore) { this.ignore = ignore; @@ -31,14 +36,21 @@ public CodeMatching(boolean ignore) { public CodeMatching() {} + /** Returns the mutable, placeholder-ordered list of reference-CD symbols. */ public List getReferences() { return references; } + /** Returns the template used to adapt an existing handwritten name. */ public String getTemplate() { return template; } + /** Returns the optional generation template, or {@code null} when none was configured. */ + public String getGenerateTemplate() { + return generateTemplate; + } + public void addReference(ISymbol reference) { this.references.add(reference); } @@ -47,11 +59,25 @@ public void setTemplate(String template) { this.template = template; } + public void setGenerateTemplate(String generateTemplate) { + this.generateTemplate = generateTemplate; + } + public void setIgnore(boolean ignore) { this.ignore = ignore; } + /** Returns whether this matching requests adaptation rather than explicit exclusion. */ public boolean mustBePerform() { return !ignore; } + + public void setExplicitAnnotation(boolean explicitAnnotation) { + this.explicitAnnotation = explicitAnnotation; + } + + /** Returns whether this matching originated from an explicit {@code @Adapt} annotation. */ + public boolean isExplicitAnnotation() { + return explicitAnnotation; + } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/MatcherHelper.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/MatcherHelper.java index f5d44b5d..b13f6373 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/MatcherHelper.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/MatcherHelper.java @@ -1,30 +1,38 @@ package de.monticore.codeAdaption.matcher; import static de.monticore.codeAdaption.utils.Constants.*; -import static de.monticore.codeAdaption.utils.Constants.PLACE_HOLDER_REGEX; import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.cdbasis._ast.ASTCDType; import de.monticore.codeAdaption.matcher.annotMatcher.AnnotElementCollector; +import de.monticore.codeAdaption.utils.AdaptAnnotationNames; import de.monticore.codeAdaption.utils.AdapterUtils; import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.monticore.javalight._ast.*; import de.monticore.statements.mccommonstatements._ast.ASTJavaModifier; import de.monticore.statements.mcstatementsbasis._ast.ASTMCModifier; import de.monticore.symboltable.ISymbol; +import de.se_rwth.commons.logging.Log; import de.monticore.types.mcbasictypes._ast.ASTMCType; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; -/*** - * helper functions of the matcher. - */ -public class MatcherHelper { +/** Creates matching results and applies the naming templates shared by matcher strategies. */ +public final class MatcherHelper { + private MatcherHelper() {} + + /** + * Creates an executable matching from a name template and its ordered reference symbols. + * + * @param template template later expanded with concrete incarnation names + * @param references symbols consumed by the template in encounter order + * @return non-ignored matching ready for name generation + */ public static CodeMatching mkMatching(String template, List references) { CodeMatching matching = new CodeMatching(); @@ -34,6 +42,10 @@ public static CodeMatching mkMatching(String template, List references) return matching; } + /** + * Decodes one {@code @Adapt} annotation into a matching and resolves its textual references in + * the supplied reference CD. Unresolvable references are warned about and omitted. + */ public static CodeMatching mkMatchingFromAnnotation( ASTMCModifier annotation, ASTCDCompilationUnit cd) { // init visitor and traverser @@ -44,19 +56,29 @@ public static CodeMatching mkMatchingFromAnnotation( // build matching CodeMatching matching = new CodeMatching(); + matching.setExplicitAnnotation(true); matching.setTemplate(collector.getTemplate()); + matching.setGenerateTemplate(collector.getGenTemplate()); matching.setIgnore(collector.isIgnore()); // resolve and add reference to the matching for (String ref : collector.getReferences()) { Optional symbol = AdapterUtils.resolveCDSymbol(ref, cd); - assert symbol.isPresent(); - matching.addReference(symbol.get()); + if (symbol.isPresent()) { + matching.addReference(symbol.get()); + } else { + Log.warn(String.format("MatcherHelper: could not resolve annotation reference '%s' in reference CD. Skipping this reference.", ref)); + } } return matching; } + /** + * Creates an infix-based matching when at least one candidate symbol occurs in the source name. + * Redundant references are removed before the replacement template and positional symbol order + * are calculated. + */ public static Optional mkMatchingFromInfixRef( List references, String srcName) { @@ -70,104 +92,170 @@ public static Optional mkMatchingFromInfixRef( return Optional.empty(); } + /** + * Replaces occurrences of reference names in a source identifier with matcher placeholders while + * preserving whether the occurrence starts with upper or lower case. + * + *

For example, reference {@code Person} turns {@code PersonRepository} into + * {@code ${}Repository} and {@code personRepository} into {@code ${uncap_first}Repository}. + */ public static String mkTemplateFormInfix(String name, List infixList) { String template = name; - for (ISymbol infixSymbol : infixList) { + List orderedInfixes = new ArrayList<>(infixList); + orderedInfixes.sort( + Comparator.comparingInt((ISymbol symbol) -> symbol.getName().length()).reversed()); + for (ISymbol infixSymbol : orderedInfixes) { String infix = infixSymbol.getName(); - if (name.contains(infix)) { - template = template.replace(infix, SIMPLE_PLACE_HOLDER); - } - if (name.contains(capFirst(infix))) { - template = template.replace(capFirst(infix), CAP_FIRST_PLACE_HOLDER); + if (infix.isEmpty()) { + continue; } - if (name.contains(uncapFirst(infix))) { - template = template.replace(uncapFirst(infix), UNCAP_FIRST_PLACE_HOLDER); + StringBuilder replaced = new StringBuilder(template.length()); + int position = 0; + while (position < template.length()) { + if (position + infix.length() > template.length() + || !template.regionMatches(true, position, infix, 0, infix.length())) { + replaced.append(template.charAt(position)); + position++; + continue; + } + String matched = template.substring(position, position + infix.length()); + String placeholder = + matched.equals(uncapFirst(infix)) && !matched.equals(infix) + ? UNCAP_FIRST_PLACE_HOLDER + : matched.equals(capFirst(infix)) && !matched.equals(infix) + ? CAP_FIRST_PLACE_HOLDER + : SIMPLE_PLACE_HOLDER; + replaced.append(placeholder); + position += infix.length(); } + template = replaced.toString(); } return template; } + /** + * Selects non-overlapping reference-name occurrences and returns their symbols in source order. + * Longer candidate names win when two candidates overlap. + * + *

For {@code UserRepositoryFactory} with candidates {@code User}, {@code Repository}, and + * {@code UserRepository}, the result starts with {@code UserRepository}; the overlapping + * {@code User} and {@code Repository} candidates are omitted. + */ public static List cleanReferences(String name, List infixList) { - List refs = - infixList.stream().map(ref -> ref.getName().toLowerCase()).collect(Collectors.toList()); - Map refMap = new LinkedHashMap<>(); - - for (int i = 0; i < refs.size(); i++) { - refMap.put(name.toLowerCase().indexOf(refs.get(i)), infixList.get(i)); - } - - List result = new ArrayList<>(); - for (int i = 0; i < name.length(); i++) { - if (refMap.containsKey(i)) { - result.add(refMap.get(i)); + String lowerName = name.toLowerCase(Locale.ROOT); + Map> refsByPosition = new TreeMap<>(); + List longestFirst = new ArrayList<>(infixList); + longestFirst.sort( + Comparator.comparingInt((ISymbol symbol) -> symbol.getName().length()).reversed()); + // Prefer longer symbols and reserve their character ranges. Ordinary substring helpers can + // locate occurrences, but they do not provide this deterministic non-overlap policy. + boolean[] occupied = new boolean[name.length()]; + for (ISymbol reference : longestFirst) { + String infix = reference.getName().toLowerCase(Locale.ROOT); + if (infix.isEmpty()) { + continue; + } + int from = 0; + while (from <= lowerName.length() - infix.length()) { + int index = lowerName.indexOf(infix, from); + if (index < 0) { + break; + } + boolean overlaps = false; + for (int i = index; i < index + infix.length(); i++) { + overlaps |= occupied[i]; + } + if (!overlaps) { + refsByPosition.computeIfAbsent(index, ignored -> new ArrayList<>()).add(reference); + Arrays.fill(occupied, index, index + infix.length(), true); + } + from = index + infix.length(); } } + List result = new ArrayList<>(); + refsByPosition.values().forEach(result::addAll); return result; } + /** Returns the adapter annotation among JavaDSL Java modifiers, if present. */ public static Optional getInfoJavaAnnot(List mods) { for (ASTMCModifier mod : mods) { if (mod instanceof ASTAnnotation - && ((ASTAnnotation) mod).getAnnotationName().getQName().equals(ANNOT_NAME)) { + && AdaptAnnotationNames.matches( + ((ASTAnnotation) mod).getAnnotationName().getQName())) { return Optional.of((ASTAnnotation) mod); } } return Optional.empty(); } + /** Returns the adapter annotation among JavaLight modifiers, if present. */ public static Optional getInfoAnnotation(List mods) { for (ASTMCModifier mod : mods) { if (mod instanceof ASTAnnotation - && ((ASTAnnotation) mod).getAnnotationName().getQName().equals(ANNOT_NAME)) { + && AdaptAnnotationNames.matches(((ASTAnnotation) mod).getAnnotationName().getQName())) { return Optional.of((ASTAnnotation) mod); } } return Optional.empty(); } - /*** - * fill template with the reference. - * @param template the template. - * @param refSymbol the template arguments - * @return the generated String. + /** + * Replaces matcher placeholders with reference-symbol names from left to right. + * + *

{@code ${}} inserts the symbol name, {@code ${cap_first}} capitalizes its first character, + * and {@code ${uncap_first}} lowercases its first character. Every placeholder consumes one + * symbol. Placeholders without a corresponding symbol remain unchanged. + * + * @param template the validation or generation name template + * @param refSymbol reference symbols consumed in placeholder order + * @return the expanded template, or the original value for null/empty inputs */ public static String fillTemplate(String template, List refSymbol) { - StringBuilder builder = new StringBuilder(template); - - // Create a pattern to match everything between "${" and "}" + if (template == null || template.isEmpty() || refSymbol == null || refSymbol.isEmpty()) { + return template; + } Pattern pattern = Pattern.compile(PLACE_HOLDER_REGEX); Matcher matcher = pattern.matcher(template); - - for (ISymbol feld : refSymbol) { - if (matcher.find()) { - String pHolder = matcher.group(1); - - String replacement; - if (pHolder.equals(CAP_FIRST)) { - replacement = capFirst(feld.getName()); - } else if (pHolder.equals(UNCAP_FIRST)) { - replacement = uncapFirst(feld.getName()); - } else { - replacement = feld.getName(); - } - builder = - new StringBuilder(builder.toString().replaceFirst(PLACE_HOLDER_REGEX, replacement)); + StringBuffer result = new StringBuffer(); + int referenceIndex = 0; + while (matcher.find()) { + if (referenceIndex >= refSymbol.size()) { + matcher.appendReplacement(result, Matcher.quoteReplacement(matcher.group(0))); + continue; + } + ISymbol field = refSymbol.get(referenceIndex++); + String pHolder = matcher.group(1); + String replacement; + if (pHolder.equals(CAP_FIRST)) { + replacement = capFirst(field.getName()); + } else if (pHolder.equals(UNCAP_FIRST)) { + replacement = uncapFirst(field.getName()); + } else { + replacement = field.getName(); } + matcher.appendReplacement(result, Matcher.quoteReplacement(replacement)); } - - return builder.toString(); + matcher.appendTail(result); + return result.toString(); } + /** Returns {@code str} with its first character upper-cased, preserving null and empty values. */ public static String capFirst(String str) { - return Character.toUpperCase(str.charAt(0)) + str.substring(1); + return JavaSourceNames.capitalize(str); } + /** Returns {@code str} with its first character lower-cased, preserving null and empty values. */ public static String uncapFirst(String str) { - return Character.toLowerCase(str.charAt(0)) + str.substring(1); + return JavaSourceNames.uncapitalize(str); } + /** + * Resolves CD type symbols whose names occur both in a variable name and its declared Java type. + * This avoids treating a type mentioned only in a generic declaration as a variable-name match. + */ public static List resolveReferencesFromType( String varName, ASTMCType type, ASTCDCompilationUnit cd) { @@ -183,23 +271,33 @@ public static List resolveReferencesFromType( return refList; } + /** + * Removes duplicate symbols and shorter names contained in longer reference names. The returned + * list is a copy and the input collection is never mutated. + */ public static List cleanReferences(List references) { if (references.isEmpty() || references.size() == 1) { - return references; + return new ArrayList<>(references); } - Set temps = new LinkedHashSet<>(references); - for (ISymbol symbol : temps) { - for (ISymbol symbol1 : temps) { - if (!symbol.equals(symbol1) && (matchInfix(symbol.getName(), symbol1.getName()))) { - references.remove(symbol1); + // Remove duplicates first, then remove shorter reference names embedded in longer references; + // for example, User is redundant when UserRepository is already a reference. + List result = new ArrayList<>(new LinkedHashSet<>(references)); + List snapshot = new ArrayList<>(result); + for (ISymbol symbol : snapshot) { + for (ISymbol other : snapshot) { + if (!symbol.equals(other) + && symbol.getName().length() > other.getName().length() + && matchInfix(symbol.getName(), other.getName())) { + result.remove(other); } } } - return references; + return result; } + /** Performs a locale-independent, case-insensitive infix test. */ public static boolean matchInfix(String element, String infix) { - return element.toLowerCase().contains(infix.toLowerCase()); + return element.toLowerCase(Locale.ROOT).contains(infix.toLowerCase(Locale.ROOT)); } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTMemberMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTMemberMatcher.java index f5553f57..1399be0a 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTMemberMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTMemberMatcher.java @@ -1,5 +1,7 @@ package de.monticore.codeAdaption.matcher.NameMatcher; +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.cdbasis._ast.ASTCDType; import de.monticore.codeAdaption.matcher.CodeMatching; @@ -8,6 +10,7 @@ import de.monticore.codeAdaption.matcher.TypeMatcher; import de.monticore.codeAdaption.utils.AdapterUtils; import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; import de.monticore.java.javadsl._ast.ASTFieldDeclaration; import de.monticore.java.javadsl._ast.ASTTypeDeclaration; import de.monticore.javalight._ast.ASTMethodDeclaration; @@ -16,22 +19,30 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; -/*** - * match Field and method in the code with attribute and method in the class diagram - * having the same name. +/** + * Matches handwritten members by names within the reference-CD types represented by their Java + * owner. + * + *

Fields require one equal-name attribute. Methods require the same name and normalized + * parameter types, so {@code update()} and {@code update(String)} remain distinct. Declared + * supertypes use embedded type-name matching because their printed Java type may be qualified or + * generic rather than a bare simple name. */ public class NameTMemberMatcher implements TMemberMatcher { protected TypeMatcher typeMatcher; protected ASTCDCompilationUnit cd; + private List referenceTypes; public NameTMemberMatcher(ASTCDCompilationUnit cd) { - this.cd = cd; + setReferenceCD(cd); } @Override public void setReferenceCD(ASTCDCompilationUnit cd) { this.cd = cd; + this.referenceTypes = List.copyOf(AdapterUtils.getAllCDTypes(cd)); } @Override @@ -47,7 +58,8 @@ public TypeMatcher getTypeMatcher() { @Override public Optional getMatchedMethod( ASTTypeDeclaration type, ASTMethodDeclaration method) { - List references = resolveMethodReferencesOf(type, method.getName(), String::equals); + List parameterTypes = getMethodParameterTypes(method); + List references = resolveMethodReferencesBySignature(type, method.getName(), parameterTypes); if (references.size() == 1) { return Optional.of(MatcherHelper.mkMatching("${}", references)); @@ -56,9 +68,56 @@ public Optional getMatchedMethod( return Optional.empty(); } - /*** - * match a field in referring classes with the same name. - * example :field "id" in "EntityBuilder" refers to "id" in Entity. + private List getMethodParameterTypes(ASTMethodDeclaration method) { + if (!method.getFormalParameters().isPresentFormalParameterListing()) { + return new ArrayList<>(); + } + return method.getFormalParameters().getFormalParameterListing().getFormalParameterList().stream() + .map(p -> JavaSourceNames.normalizeType(JavaLoader.print(p.getMCType()))) + .collect(Collectors.toList()); + } + + private List resolveMethodReferencesBySignature( + ASTTypeDeclaration type, String methodName, List parameterTypes) { + + List references = new ArrayList<>(); + Optional matching = getTypeMatcher().getMatchedType(type); + + if (matching.isEmpty()) { + return references; + } + + for (ISymbol symbol : matching.get().getReferences()) { + if (symbol.getAstNode() instanceof ASTCDType) { + ASTCDType cdType = (ASTCDType) symbol.getAstNode(); + for (ASTCDMethod cdMethod : cdType.getCDMethodList()) { + if (cdMethod.getName().equals(methodName)) { + List cdParameterTypes = cdMethod.getCDParameterList().stream() + .map(ASTCDParameter::getMCType) + .map(JavaLoader::print) + .map(JavaSourceNames::normalizeType) + .collect(Collectors.toList()); + + if (parameterTypes.equals(cdParameterTypes)) { + references.add(cdMethod.getSymbol()); + } + } + } + } + } + return references; + } + + private int getMethodParameterCount(ASTMethodDeclaration method) { + if (!method.getFormalParameters().isPresentFormalParameterListing()) { + return 0; + } + return method.getFormalParameters().getFormalParameterListing().getFormalParameterList().size(); + } + + /** + * Matches one equal-name reference attribute within the Java owner's matched reference types. + * For example, field {@code id} in {@code EntityBuilder} may refer to attribute {@code Entity.id}. */ @Override public Optional getMatchedField( @@ -78,7 +137,7 @@ public Optional getMatchedSupertype(ASTTypeDeclaration type, ASTMC List references = new ArrayList<>(); String srcName = JavaLoader.print(supertype); // collect type reference in the class diagram - for (ASTCDType astcdType : AdapterUtils.getAllCDTypes(cd)) { + for (ASTCDType astcdType : referenceTypes) { if (srcName.toLowerCase().contains(astcdType.getName().toLowerCase())) { references.add(astcdType.getSymbol()); } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTypeMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTypeMatcher.java index b683b6fc..3ea7aceb 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTypeMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/NameMatcher/NameTypeMatcher.java @@ -11,12 +11,14 @@ import java.util.Optional; import java.util.Set; -/*** - * match a type in the code with a type with a type with the same name - * in the reference class diagram. +/** + * Matches a handwritten type to the reference-CD type having the same case-sensitive simple name. + * For example, Java type {@code Entity} matches CD type {@code Entity}, while {@code + * EntityRepository} is left to the infix strategy. */ public class NameTypeMatcher implements TypeMatcher { protected ASTCDCompilationUnit cd; + private List referenceTypes; protected Set typeDeclarationSet; @@ -31,20 +33,19 @@ public void setAllTypeDeclarations(Set typeDeclarationSet) { } public NameTypeMatcher(ASTCDCompilationUnit cd) { - this.cd = cd; + setReferenceCD(cd); } @Override public void setReferenceCD(ASTCDCompilationUnit cd) { this.cd = cd; + this.referenceTypes = List.copyOf(AdapterUtils.getAllCDTypes(cd)); } - /*** - * match a type to a type with the same name in the class diagram. - */ + /** Returns an identity-template matching for one equal Java/CD type name. */ @Override public Optional getMatchedType(ASTTypeDeclaration element) { - for (ASTCDType type : AdapterUtils.getAllCDTypes(cd)) { + for (ASTCDType type : referenceTypes) { if (element.getName().equals(type.getName())) { return Optional.of(MatcherHelper.mkMatching("${}", List.of(type.getSymbol()))); } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TMemberMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TMemberMatcher.java index f921a331..e20fe41d 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TMemberMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TMemberMatcher.java @@ -12,19 +12,27 @@ import java.util.*; import java.util.function.BiFunction; +/** Strategy for owner-aware matching of Java fields, methods, and supertype references. */ public interface TMemberMatcher { + /** Sets the reference CD used by subsequent matching requests. */ void setReferenceCD(ASTCDCompilationUnit cd); + /** Sets the type matcher used to resolve the reference owner of a Java member. */ void setTypeMatcher(TypeMatcher matcher); + /** Returns the type matcher that establishes member ownership. */ TypeMatcher getTypeMatcher(); + /** Matches a Java method beneath its Java/reference owner. */ Optional getMatchedMethod(ASTTypeDeclaration type, ASTMethodDeclaration method); + /** Matches a Java field beneath its Java/reference owner. */ Optional getMatchedField(ASTTypeDeclaration type, ASTFieldDeclaration field); + /** Matches a declared Java superclass or interface reference. */ Optional getMatchedSupertype(ASTTypeDeclaration type, ASTMCType supertype); + /** Resolves matching CD attributes from every reference type represented by the Java owner. */ default List resolveFieldReferencesOf( ASTTypeDeclaration type, String name, BiFunction match) { @@ -47,8 +55,18 @@ default List resolveFieldReferencesOf( return references; } + /** Resolves matching CD methods without restricting overload arity. */ default List resolveMethodReferencesOf( ASTTypeDeclaration type, String name, BiFunction match) { + return resolveMethodReferencesOf(type, name, match, -1); // -1 - ignore parameter count + } + + /** + * Resolves matching CD methods, optionally requiring an exact parameter count when {@code + * paramCount >= 0}. + */ + default List resolveMethodReferencesOf( + ASTTypeDeclaration type, String name, BiFunction match, int paramCount) { List references = new ArrayList<>(); Optional matching = getTypeMatcher().getMatchedType(type); @@ -57,17 +75,22 @@ default List resolveMethodReferencesOf( return references; } - // resolve attribute reference in referencing types + // resolve method reference in referencing types for (ISymbol symbol : matching.get().getReferences()) { - for (ASTCDMethod att : ((ASTCDType) symbol.getAstNode()).getCDMethodList()) { - if (match.apply(name, att.getName())) { - references.add(att.getSymbol()); + for (ASTCDMethod cdMethod : ((ASTCDType) symbol.getAstNode()).getCDMethodList()) { + if (match.apply(name, cdMethod.getName())) { + // If paramCount is specified, also check parameter count for overloaded methods + if (paramCount >= 0 && cdMethod.getCDParameterList().size() != paramCount) { + continue; // Skip methods with different parameter counts + } + references.add(cdMethod.getSymbol()); } } } return references; } + /** Resolves matched reference types represented directly by the Java owner. */ default List resolveTypeReferencesOf( ASTTypeDeclaration type, String name, BiFunction match) { diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TypeMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TypeMatcher.java index 7299d70d..fbc100d3 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TypeMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/TypeMatcher.java @@ -5,17 +5,22 @@ import java.util.Optional; import java.util.Set; -/*** - * this interface validates and cleans a reference code for a given reference model. - * it also collects the necessary update operation for an adaptation. - */ - +/** Strategy for mapping Java type declarations to elements of one reference class diagram. */ public interface TypeMatcher { + /** Sets the reference CD used by subsequent matching requests. */ void setReferenceCD(ASTCDCompilationUnit cd); + /** Returns all Java type declarations currently visible to cross-type matching. */ Set getAllTypeDeclarations(); + /** Replaces the Java type declarations visible to cross-type matching. */ void setAllTypeDeclarations(Set typeDeclarations); + /** + * Matches one Java type. + * + * @return a matching (including explicit ignore results), or empty when this strategy does not + * apply + */ Optional getMatchedType(ASTTypeDeclaration type); } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/VariableMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/VariableMatcher.java index 72346a1c..a834fc7d 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/VariableMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/VariableMatcher.java @@ -7,12 +7,16 @@ import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; import java.util.Optional; +/** Strategy for matching Java local variables and formal parameters within a matched method. */ public interface VariableMatcher { + /** Sets the reference CD used by subsequent matching requests. */ void setReferenceCD(ASTCDCompilationUnit cd); + /** Matches one local variable in its owning Java type and method. */ Optional getMatchedLocalVariable( ASTTypeDeclaration type, ASTMethodDeclaration method, ASTLocalVariableDeclaration locVar); + /** Matches one formal parameter in its owning Java type and method. */ Optional getMatchedFormalParameter( ASTTypeDeclaration type, ASTMethodDeclaration method, ASTFormalParameter param); } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotElementCollector.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotElementCollector.java index 26bb3111..57289fcd 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotElementCollector.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotElementCollector.java @@ -1,8 +1,6 @@ package de.monticore.codeAdaption.matcher.annotMatcher; import de.monticore.codeAdaption.utils.Constants; -import de.monticore.expressions.expressionsbasis._ast.ASTExpression; -import de.monticore.expressions.expressionsbasis._ast.ASTLiteralExpression; import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.monticore.javalight._ast.ASTElementValueOrExpr; @@ -14,13 +12,18 @@ import java.util.ArrayList; import java.util.List; -/*** - * this class visits an ASTAnnotation or ASTJavaAnnotation and collects information. - * (template, ignore-tag and references) +/** + * Decodes the literal attributes of one JavaDSL {@code @Adapt} annotation. + * + *

{@code ref} values are collected in declaration order because template placeholders consume + * them from left to right. {@code template} and {@code genTemplate} must each contain exactly one + * string literal, while {@code ignore} must contain exactly one boolean literal. Structural and + * reference-resolution validation is performed later by {@code ValidAnnotation}. */ public class AnnotElementCollector implements JavaLightVisitor2 { private String template; + private String genTemplate; private final List references = new ArrayList<>(); private boolean ignore; @@ -28,28 +31,65 @@ public class AnnotElementCollector implements JavaLightVisitor2 { public void visit(ASTElementValuePair node) { if (node.getName().equals(Constants.TEMPLATE)) { - ASTExpression val = node.getElementValueOrExpr().getExpression(); - template = ((ASTStringLiteral) ((ASTLiteralExpression) val).getLiteral()).getValue(); + template = readSingleString(node.getElementValueOrExpr(), Constants.TEMPLATE); } if (node.getName().equals(Constants.IGNORE)) { - ASTExpression val = node.getElementValueOrExpr().getExpression(); - ignore = ((ASTBooleanLiteral) ((ASTLiteralExpression) val).getLiteral()).getValue(); + ignore = readSingleBoolean(node.getElementValueOrExpr(), Constants.IGNORE); } if (node.getName().equals(Constants.REFERENCE)) { - ASTElementValueOrExpr val = node.getElementValueOrExpr(); - MCCommonLiteralsVisitor2 visitor = - new MCCommonLiteralsVisitor2() { - @Override - public void visit(ASTStringLiteral node) { - references.add(node.getValue()); - } - }; - JavaDSLTraverser traverser = JavaDSLMill.traverser(); - traverser.add4MCCommonLiterals(visitor); - val.accept(traverser); + references.addAll(readStrings(node.getElementValueOrExpr())); } + + if (node.getName().equals(Constants.GENERATE_TEMPLATE)) { + genTemplate = readSingleString(node.getElementValueOrExpr(), Constants.GENERATE_TEMPLATE); + } + } + + private static String readSingleString(ASTElementValueOrExpr value, String attribute) { + List strings = readStrings(value); + if (strings.size() != 1) { + throw new IllegalArgumentException( + "@Adapt attribute '" + attribute + "' requires exactly one string literal"); + } + return strings.get(0); + } + + private static boolean readSingleBoolean(ASTElementValueOrExpr value, String attribute) { + List booleans = new ArrayList<>(); + MCCommonLiteralsVisitor2 visitor = + new MCCommonLiteralsVisitor2() { + @Override + public void visit(ASTBooleanLiteral node) { + booleans.add(node.getValue()); + } + }; + traverse(value, visitor); + if (booleans.size() != 1) { + throw new IllegalArgumentException( + "@Adapt attribute '" + attribute + "' requires exactly one boolean literal"); + } + return booleans.get(0); + } + + private static List readStrings(ASTElementValueOrExpr value) { + List strings = new ArrayList<>(); + MCCommonLiteralsVisitor2 visitor = + new MCCommonLiteralsVisitor2() { + @Override + public void visit(ASTStringLiteral node) { + strings.add(node.getValue()); + } + }; + traverse(value, visitor); + return strings; + } + + private static void traverse(ASTElementValueOrExpr value, MCCommonLiteralsVisitor2 visitor) { + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4MCCommonLiterals(visitor); + value.accept(traverser); } public List getReferences() { @@ -60,6 +100,10 @@ public String getTemplate() { return template; } + public String getGenTemplate() { + return genTemplate; + } + public boolean isIgnore() { return ignore; } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTMemberMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTMemberMatcher.java index 0710c2bf..bd772e45 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTMemberMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTMemberMatcher.java @@ -12,9 +12,11 @@ import de.monticore.types.mcbasictypes._ast.ASTMCType; import java.util.Optional; -/*** - * match an Attributes and method in the reference code to elements - * in the reference class Diagram form annotations. +/** + * Matches handwritten fields and methods through explicit {@code @Adapt} annotations. + * + *

Declared Java supertypes cannot carry annotations at their type-use position in the supported + * JavaDSL model, so this strategy deliberately returns no supertype match. */ public class AnnotTMemberMatcher implements TMemberMatcher { diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTypeMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTypeMatcher.java index cda9dc4c..d191b550 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTypeMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotTypeMatcher.java @@ -11,9 +11,10 @@ import java.util.Optional; import java.util.Set; -/*** - * match a TypeDeclaration(class, enum, interface) in the reference code to elements in - * the reference class Diagram form annotations. +/** + * Matches a handwritten class, interface, or enum through its explicit {@code @Adapt} annotation. + * Annotation references are resolved against the reference CD and retain their declared order for + * template expansion. */ public class AnnotTypeMatcher implements TypeMatcher { protected ASTCDCompilationUnit cd; diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotVariableMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotVariableMatcher.java index 8723215f..5a26a79e 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotVariableMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/annotMatcher/AnnotVariableMatcher.java @@ -11,9 +11,9 @@ import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; import java.util.Optional; -/*** - * match a methods parameters and local variables in the reference code to elements - * in the reference class Diagram form annotations. +/** + * Matches handwritten formal parameters and local variables through explicit {@code @Adapt} + * annotations whose references are resolved against the reference CD. */ public class AnnotVariableMatcher implements VariableMatcher { protected ASTCDCompilationUnit cd; @@ -23,7 +23,9 @@ public AnnotVariableMatcher(ASTCDCompilationUnit cd) { } @Override - public void setReferenceCD(ASTCDCompilationUnit cd) {} + public void setReferenceCD(ASTCDCompilationUnit cd) { + this.cd = cd; + } @Override public Optional getMatchedLocalVariable( diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTMemberMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTMemberMatcher.java index e96714d0..370aef57 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTMemberMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTMemberMatcher.java @@ -11,9 +11,12 @@ import java.util.List; import java.util.Optional; -/*** - * compose matching Strategy for Method and attributes. - * allow trying several matching strategies until a matching is found. +/** + * Applies field, method, and supertype matchers in configured precedence order and returns the + * first result for the requested member. + * + *

The same enclosing-type matcher is shared with every member strategy so owner-relative + * lookups resolve against the same reference-CD types. */ public class CompTMemberMatcher implements TMemberMatcher { protected TypeMatcher typeMatcher; diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTypeMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTypeMatcher.java index 92b06378..e5bc6d98 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTypeMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompTypeMatcher.java @@ -8,9 +8,11 @@ import java.util.Optional; import java.util.Set; -/*** - * compose matching Strategy for Types. - * allow trying several matching strategies until a matching is found. +/** + * Applies type-matching strategies in configured precedence order and returns the first result. + * + *

A present ignored matching is still a result and stops the chain. The validator therefore + * installs annotation/name/infix strategies first and an ignore-or-error policy last. */ public class CompTypeMatcher implements TypeMatcher { protected List matchers; diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompVariableMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompVariableMatcher.java index a6d23994..b98ef150 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompVariableMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/compMatcher/CompVariableMatcher.java @@ -10,9 +10,9 @@ import java.util.List; import java.util.Optional; -/*** - * compose matching Strategy for functions parameters and local variables. - * allow trying several matching strategies until a matching is found. +/** + * Applies formal-parameter and local-variable matchers in configured precedence order and returns + * the first result. Annotation and infix strategies run before the terminal ignore-or-error policy. */ public class CompVariableMatcher implements VariableMatcher { diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTMemberMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTMemberMatcher.java index fa4e1b7d..44fb7cef 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTMemberMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTMemberMatcher.java @@ -13,8 +13,12 @@ import de.se_rwth.commons.logging.Log; import java.util.Optional; -/*** - *throws an error when the previous strategies didn't find machining for attributes or a methods. +/** + * Terminal fallback that reports unmatched fields, methods, and external supertypes as validation + * errors. + * + *

A supertype declared by another Java source in the same input batch is accepted unchanged: it + * is a source-local Java relationship rather than a missing reference-CD match. */ public class ErrorTMemberMatcher implements TMemberMatcher { protected ASTCDCompilationUnit cd; @@ -41,7 +45,6 @@ public Optional getMatchedMethod( String pos = AdapterUtils.getPosition(method.get_SourcePositionStart()); Log.error(pos + " No Match found for the Method " + method.getName()); - assert false; return Optional.empty(); } @@ -52,7 +55,6 @@ public Optional getMatchedField( String name = field.getVariableDeclarator(0).getDeclarator().getName(); String pos = AdapterUtils.getPosition(field.get_SourcePositionStart()); Log.error(pos + " No Match found for the Field " + name); - assert false; return Optional.empty(); } @@ -68,7 +70,6 @@ public Optional getMatchedSupertype(ASTTypeDeclaration type, ASTMC String pos = AdapterUtils.getPosition(supertype.get_SourcePositionStart()); Log.error(pos + " No Match found for the Type " + name); - assert false; return Optional.empty(); } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTypeMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTypeMatcher.java index c9e5ac09..ccc41cac 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTypeMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorTypeMatcher.java @@ -9,8 +9,9 @@ import java.util.Optional; import java.util.Set; -/*** - *throws an error when the previous strategy didn't find machining for types +/** + * Terminal fallback that reports an unmatched Java type as a validation error. + * It is installed when {@code IGNORE_NON_MATCHED_TYPE} is not enabled. */ public class ErrorTypeMatcher implements TypeMatcher { protected ASTCDCompilationUnit cd; @@ -35,7 +36,6 @@ public void setAllTypeDeclarations(Set typeDeclarationSet) { public Optional getMatchedType(ASTTypeDeclaration type) { String pos = AdapterUtils.getPosition(type.get_SourcePositionStart()); Log.error(pos + " No Match found for the type " + type.getName()); - assert false; return Optional.empty(); } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorVariableMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorVariableMatcher.java index e77ea23c..0748c11b 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorVariableMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/errorMatcher/ErrorVariableMatcher.java @@ -11,9 +11,9 @@ import de.se_rwth.commons.logging.Log; import java.util.Optional; -/*** - *throws an error when the previous strategy didn't find machining for local-variables - * and method parameters. +/** + * Terminal fallback that reports unmatched local variables and formal parameters as validation + * errors. It is installed when {@code IGNORE_NON_MATCHED_VAR} is not enabled. */ public class ErrorVariableMatcher implements VariableMatcher { @Override @@ -26,7 +26,6 @@ public Optional getMatchedLocalVariable( String name = locVar.getVariableDeclarator(0).getDeclarator().getName(); String pos = AdapterUtils.getPosition(locVar.get_SourcePositionStart()); Log.error(pos + " No Match found for the Local Variable " + name); - assert false; return Optional.empty(); } @@ -37,7 +36,6 @@ public Optional getMatchedFormalParameter( String name = param.getDeclarator().getName(); String pos = AdapterUtils.getPosition(param.get_SourcePositionStart()); Log.error(pos + " No Match found for the formal parameter " + name); - assert false; return Optional.empty(); } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTMemberMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTMemberMatcher.java index 246049bf..3263dcf2 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTMemberMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTMemberMatcher.java @@ -10,10 +10,12 @@ import de.monticore.types.mcbasictypes._ast.ASTMCType; import java.util.Optional; -/*** - *return an empty-matching with the ignore flag set as true. - *when the previous strategy didn't find machining for attributes or methods. - *the adaption will ignore the corresponding element. +/** + * Terminal fallback that accepts unmatched fields, methods, and supertypes unchanged. + * + *

The ignored matching prevents a validation error and tells update phases not to transform the + * member. It is installed by {@code IGNORE_NON_MATCHED_TYPE_MEMBER} after all enabled member + * matching strategies. */ public class IgnoreTMemberMatcher implements TMemberMatcher { protected ASTCDCompilationUnit cd; diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTypeMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTypeMatcher.java index 35edf46a..2931d9e9 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTypeMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreTypeMatcher.java @@ -7,10 +7,13 @@ import java.util.Optional; import java.util.Set; -/*** - *Return an empty-matching with the ignore flag set as true. - *When the previous strategy didn't find machining for a type. - *The adaption will ignore the corresponding element. +/** + * Terminal fallback that accepts an unmatched Java type without adapting it. + * + *

The returned ignored matching prevents a validation error, but it does not invent a + * reference-CD incarnation. Consequently, an unmatched top-level type is not selected for + * mapping-specific adapted output. This matcher is installed by {@code IGNORE_NON_MATCHED_TYPE} + * after all enabled matching strategies. */ public class IgnoreTypeMatcher implements TypeMatcher { protected ASTCDCompilationUnit cd; diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreVariableMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreVariableMatcher.java index 9c58b1f3..6c803cb9 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreVariableMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/ignoreMatcher/IgnoreVariableMatcher.java @@ -9,10 +9,11 @@ import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; import java.util.Optional; -/*** - *Return an empty-matching with the ignore flag set as true. - *When the previous strategy didn't find machining for a local-variable or a method-parameter. - *The adaption will ignore the corresponding element. +/** + * Terminal fallback that accepts unmatched local variables and formal parameters unchanged. + * + *

It is installed by {@code IGNORE_NON_MATCHED_VAR} after all enabled variable matching + * strategies. */ public class IgnoreVariableMatcher implements VariableMatcher { @Override diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTMemberMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTMemberMatcher.java index 18746537..f1489c45 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTMemberMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTMemberMatcher.java @@ -14,9 +14,13 @@ import java.util.List; import java.util.Optional; -/*** - * match an Attributes and method in the reference code to elements - * in the reference class Diagram by analyzing the infix. +/** + * Matches handwritten fields, methods, and declared supertypes by reference-CD names embedded in + * their Java names or types. + * + *

For example, {@code getEntity()} may reference type {@code Entity}, while {@code getId()} may + * reference attribute {@code id}. Method candidates are additionally restricted by parameter + * count so overloads with different arity do not all become references. */ public class InfixTMemberMatcher implements TMemberMatcher { protected ASTCDCompilationUnit cd; @@ -41,27 +45,30 @@ public TypeMatcher getTypeMatcher() { return typeMatcher; } - /*** - * match method to a type or an attribute in the reference class diagram. - * eg: getId to id ; - * getEntity to Entity - */ + /** Matches a method name to embedded reference fields, types, and same-arity methods. */ @Override public Optional getMatchedMethod( ASTTypeDeclaration type, ASTMethodDeclaration method) { - // resolve references + // Get parameter count from the Java method for overloaded method matching + int paramCount = getMethodParameterCount(method); + + // resolve references with parameter count filtering for overloaded methods List references = resolveFieldReferencesOf(type, method.getName(), this::match); references.addAll(resolveTypeReferencesOf(type, method.getName(), this::match)); + references.addAll(resolveMethodReferencesOf(type, method.getName(), this::match, paramCount)); return MatcherHelper.mkMatchingFromInfixRef(references, method.getName()); } - /*** - * match field to a type or an attribute in the reference class diagram. - * eg: entityList to Entity - * longId to id - */ + private int getMethodParameterCount(ASTMethodDeclaration method) { + if (!method.getFormalParameters().isPresentFormalParameterListing()) { + return 0; + } + return method.getFormalParameters().getFormalParameterListing().getFormalParameterList().size(); + } + + /** Matches a field through reference names embedded in its name and declared type. */ @Override public Optional getMatchedField( ASTTypeDeclaration type, ASTFieldDeclaration field) { @@ -75,11 +82,7 @@ public Optional getMatchedField( return MatcherHelper.mkMatchingFromInfixRef(references, name); } - /*** - * match field to a type or an attribute in the reference class diagram. - * eg: entityList to Entity - * longId to id - */ + /** Matches a declared Java supertype to embedded reference-CD type names. */ @Override public Optional getMatchedSupertype(ASTTypeDeclaration type, ASTMCType superType) { String name = JavaLoader.print(superType); diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTypeMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTypeMatcher.java index 582d9976..62923503 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTypeMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixTypeMatcher.java @@ -13,9 +13,12 @@ import java.util.Optional; import java.util.Set; -/*** - * match a type in the reference code to elements - * in the reference class Diagram by analyzing the infix. +/** + * Matches a handwritten type when its name contains one or more reference-CD type names, + * case-insensitively. + * + *

For example, {@code EntityRepository} can reference {@code Entity}. When candidate names + * overlap, {@link MatcherHelper} gives longer matches precedence while building the template. */ public class InfixTypeMatcher implements TypeMatcher { protected ASTCDCompilationUnit cd; @@ -41,9 +44,7 @@ public void setReferenceCD(ASTCDCompilationUnit cd) { this.cd = cd; } - /*** - * match a type to a type in the class diagram with the same suffix. - */ + /** Returns an infix-derived matching for all reference-CD type names found in the Java name. */ @Override public Optional getMatchedType(ASTTypeDeclaration type) { List references = new ArrayList<>(); diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixVariableMatcher.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixVariableMatcher.java index 8f5b46d3..04cfcba1 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixVariableMatcher.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/matcher/infixMatcher/InfixVariableMatcher.java @@ -15,9 +15,12 @@ import java.util.List; import java.util.Optional; -/*** - * match a local-variable or a formal-parameter in the reference code to elements - * in the reference class Diagram by analyzing the infix. +/** + * Matches local variables and formal parameters from reference-CD names embedded in their Java + * identifier or declared type. + * + *

For example, {@code Entity currentEntity} may reference the CD type {@code Entity}; the + * resulting template preserves the surrounding identifier text. */ public class InfixVariableMatcher implements VariableMatcher { protected ASTCDCompilationUnit cd; diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/CodeUpdater.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/CodeUpdater.java index c804b8ae..fbc93475 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/CodeUpdater.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/CodeUpdater.java @@ -1,6 +1,7 @@ package de.monticore.codeAdaption.updater; import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; import de.monticore.java.javadsl._ast.ASTFieldDeclaration; import de.monticore.java.javadsl._ast.ASTTypeDeclaration; import de.monticore.javalight._ast.ASTMethodDeclaration; @@ -9,57 +10,76 @@ import de.monticore.types.mcbasictypes._ast.ASTMCType; import java.io.File; import java.nio.file.Path; +import java.util.List; +import java.util.Map; import java.util.Set; -/*** - * this class execute the update operation in the reference code. +/** + * Technology-neutral mutation facade used to transform one loaded handwritten Java model. + * + *

Implementations must update declarations and their uses consistently. Default methods fail + * explicitly when an implementation does not support a generation or cleanup capability. */ public interface CodeUpdater { - /*** - * set the path to the referenceCode. - * @param path the path. + /** + * Loads the Java source tree that subsequent operations mutate. + * + * @param path source directory */ void setCodePath(Path path); - /*** - * print the current state of the reference code as set of files. - * @return the code as files + /** + * Prints the current model into the configured output directory. + * + * @return generated Java files */ Set printCode(); - /*** - * change the name of a type in the reference code. By the declaration - * an all its reference an uses. - * @param srcType the source type to replace. - * @param newName the new name of the type. + /** + * Renames a type declaration and all resolvable references and uses. + * + * @param srcType handwritten Java declaration to rename + * @param newName concrete Java type name */ void updateType(ASTTypeDeclaration srcType, String newName); - /*** - * change the name of a method in the reference code. By the declaration - * an all its reference an uses. - * @param srcType the source-type that contains the method-declaration. - * @param srcMethod the source method. - * @param newName the new name of the type. + /** + * Renames a method declaration and the invocations resolved to that method. + * + * @param srcType handwritten Java type containing the method declaration + * @param srcMethod handwritten Java method to rename + * @param newName the new name of the method */ void updateMethod(ASTTypeDeclaration srcType, ASTMethodDeclaration srcMethod, String newName); - /*** - * change the name of a field in the reference code. By its declaration - * an all its references an uses. - * @param srcType the source-type that contains the field-declaration - * @param srcField the source Field. - * @param newName the new name of the field. + /** + * Renames a field declaration and all resolvable accesses. + * + * @param srcType handwritten Java type containing the field declaration + * @param srcField handwritten Java field to rename + * @param newName concrete field name */ void updateField(ASTTypeDeclaration srcType, ASTFieldDeclaration srcField, String newName); - /*** - * change the name of a local-variable in the reference code. By its declaration - * an all its references an uses. - * @param srcType the source-type that contains the local variable. - * @param srcMethod the source method tha contains le local variable. - * @param sourceVar the source local variable. - * @param newName the new name of the local variable. + /** + * Renames accesses to a field generated from a CD association role. + * + *

Unlike {@link #updateField}, this operation has no handwritten field declaration to rename: + * the declaration will be generated from the concrete CD. The source type scopes the rewrite so + * a role such as {@code children} on one owner does not affect another owner's same-named role. + */ + default void updateAssociationRole( + ASTTypeDeclaration srcType, String sourceRole, String concreteRole) { + throw unsupported("updateAssociationRole"); + } + + /** + * Renames a local variable declaration and all resolvable uses in its method. + * + * @param srcType handwritten Java type containing the local variable + * @param srcMethod handwritten Java method containing the local variable + * @param sourceVar local variable to rename + * @param newName concrete local-variable name */ void updateLocalVariable( ASTTypeDeclaration srcType, @@ -67,13 +87,13 @@ void updateLocalVariable( ASTLocalVariableDeclaration sourceVar, String newName); - /*** - * change the name of a local-variable in the reference code. By it declaration - * and all its references an uses - * @param srcType the source type that contains the local parameter - * @param srcMethod the source method that contains le the parameter - * @param srcParam the source parameter - * @param newName the new name of the parameter + /** + * Renames a method parameter declaration and all resolvable uses. + * + * @param srcType handwritten Java type containing the parameter + * @param srcMethod handwritten Java method containing the parameter + * @param srcParam formal parameter to rename + * @param newName concrete parameter name */ void updateMethodParameter( ASTTypeDeclaration srcType, @@ -81,17 +101,276 @@ void updateMethodParameter( ASTFormalParameter srcParam, String newName); - /*** - * Update all types that are not present in the reference code. - * @param cdType type in the reference class diagram to update in the reference code. - * @param newName new name of the type. + /** + * Rewrites uses of a reference-CD type that has no declaration in handwritten Java. + * + *

For example, handwritten code may declare {@code List} without declaring {@code Role} + * because that type is generated from the CD. If {@code Role} maps to {@code Permission}, this + * operation rewrites the type use even though {@link #updateType} has no Java declaration to + * target. + * + * @param cdType reference-CD type whose handwritten Java uses are rewritten + * @param newName concrete Java type name */ void updateCDType(ASTCDType cdType, String newName); - /*** - * set the output directory of the code by printing. + + /** + * Sets the destination used by {@link #printCode()}. + * * @param outputPath the output directory */ void setOutputDirectory(Path outputPath); + /** + * Renames one declared superclass or interface reference on a Java type. + * + * @param type Java type that owns the relationship + * @param supertype source supertype AST + * @param newName concrete supertype name + */ void updateSuperType(ASTTypeDeclaration type, ASTMCType supertype, String newName); + + /** + * Remove adapter-only metadata and perform implementation-specific post-processing on generated + * Java sources. + */ + default void cleanCode(Path codePath) { + throw unsupported("cleanCode"); + } + + /** + * Cleans generated Java and repairs explicit self values moved into TOP implementations. + * + * @param topToPublicSelfTypes qualified generated TOP type to its qualified public HWC subtype + */ + default void cleanCode(Path codePath, Map topToPublicSelfTypes) { + if (topToPublicSelfTypes == null || topToPublicSelfTypes.isEmpty()) { + cleanCode(codePath); + return; + } + throw unsupported("cleanCode with TOP self-type bindings"); + } + + /** + * Configures incarnation-type to grouping-type replacements for the current updater pass. + * + *

Keys identify individual concrete incarnations and values identify the common concrete type + * representing their incarnation group. For example, {@code {CreditCard=PaymentMethod, + * Invoice=PaymentMethod}} allows a shared {@code CreditCard} or {@code Invoice} parameter to be + * printed as {@code PaymentMethod}. Implementations apply these replacements during final model + * repair; they do not rename the incarnation declarations themselves. + * + * @param mappings incarnation simple name to grouping-type simple name; empty disables grouping + */ + default void setGroupingMappings(Map mappings) { + if (mappings != null && !mappings.isEmpty()) { + throw unsupported("setGroupingMappings"); + } + } + + /** + * Registers an owner-independent concrete method signature for legacy invocation repair. + * + *

For example, registering {@code send(String, boolean)} allows an adapted {@code send(label)} + * invocation to receive a compatible enclosing {@code boolean} parameter or a Java default value. + * Because no owner is supplied, repair is performed only when the method name has one + * unambiguous registered signature. New code should prefer {@link #registerMethodRewrite}. + * + * @param methodName concrete method name + * @param parameterTypes concrete parameter types in declaration order + */ + default void registerConcreteMethodSignature(String methodName, List parameterTypes) { + throw unsupported("registerConcreteMethodSignature"); + } + + /** + * Registers an owner- and signature-aware method rewrite for invocation repair. + * + *

The reference key identifies the method before adaptation; the concrete key identifies its + * renamed owner, name, and parameters afterward. For example, {@code Port.send(String) -> + * ShippingPort.ship(String, boolean)} both renames matching calls and supplies the additional + * argument without changing unrelated {@code send} overloads. + * + * @param referenceMethod owner- and signature-aware reference-CD method key + * @param concreteMethod owner- and signature-aware concrete-CD method key + */ + default void registerMethodRewrite(StableElementKey referenceMethod, StableElementKey concreteMethod) { + throw unsupported("registerMethodRewrite"); + } + + /** + * Registers a method rewrite anchored to the exact Java source owner. Implementations should use + * the source declaration to retain package identity even when the CD owner is unqualified. + */ + default void registerMethodRewrite( + ASTTypeDeclaration sourceOwner, + StableElementKey referenceMethod, + StableElementKey concreteMethod) { + registerMethodRewrite(referenceMethod, concreteMethod); + } + + /** + * Add or complete a field on the target type. Implementations may clone the template field when + * present; completed-CD projection passes {@code null} when no Java template owns the member. + */ + default void addField( + ASTTypeDeclaration targetType, + ASTFieldDeclaration templateField, + String newName, + String newType, + boolean isStatic) { + throw unsupported("addField"); + } + + /** + * Add or complete a method on the target type. Implementations may clone the template method when + * present; completed-CD projection passes {@code null} when no Java template owns the member. + */ + default void addMethod( + ASTTypeDeclaration targetType, + ASTMethodDeclaration templateMethod, + String newName, + List paramTypes, + List paramNames, + String returnType, + boolean isStatic, + MethodBodySpec methodBody) { + throw unsupported("addMethod"); + } + + /** + * Create a new top-level type by cloning the provided template type and giving it the provided name. + */ + default void addType(ASTTypeDeclaration templateType, String newName) { + throw unsupported("addType"); + } + + /** Adds a completed-CD superclass or interface relationship to an existing Java type. */ + default void addSuperType( + ASTTypeDeclaration targetType, String superTypeName, boolean interfaceType) { + throw unsupported("addSuperType"); + } + + /** Sets whether a generated or adapted Java type is abstract. */ + default void setTypeAbstract(ASTTypeDeclaration targetType, boolean isAbstract) { + throw unsupported("setTypeAbstract"); + } + + /** Adds an enum constant introduced by completed-CD projection. */ + default void addEnumConstant( + ASTTypeDeclaration targetType, String constantName, int expectedIndex) { + throw unsupported("addEnumConstant"); + } + + /** + * Remove a field from the given target type (by template AST field reference). + */ + default void removeField(ASTTypeDeclaration targetType, ASTFieldDeclaration field) { + throw unsupported("removeField"); + } + + /** + * Remove a method from the given target type (by template AST method reference). + */ + default void removeMethod(ASTTypeDeclaration targetType, ASTMethodDeclaration method) { + throw unsupported("removeMethod"); + } + + private UnsupportedOperationException unsupported(String operation) { + return new UnsupportedOperationException( + getClass().getName() + " does not support updater operation: " + operation); + } + + /** + * Immutable description of the small generated method-body forms supported by the updater. + * + *

For example, {@code assignFieldAndReturnThis("name", "value")} represents {@code this.name = + * value; return this;}, while {@code returnNew("Person", List.of("name", "age"))} represents + * {@code return new Person(name, age);}. + */ + final class MethodBodySpec { + /** Supported generated method-body shapes. */ + public enum Kind { + /** Preserve a cloned body or synthesize a safe empty/default body. */ + EMPTY, + /** Replace a cloned implementation with a compilable default body. */ + SAFE_DEFAULT, + /** Preserve an implementation or synthesize a public, safe interface-contract body. */ + INTERFACE_CONTRACT, + /** Assign one parameter to a field and return {@code this}. */ + ASSIGN_FIELD_AND_RETURN_THIS, + /** Return a newly constructed value using selected fields as arguments. */ + RETURN_NEW + } + + private final Kind kind; + private final String fieldName; + private final String parameterName; + private final String constructorType; + private final java.util.List constructorFieldArguments; + + private MethodBodySpec( + Kind kind, + String fieldName, + String parameterName, + String constructorType, + List constructorFieldArguments) { + this.kind = kind; + this.fieldName = fieldName; + this.parameterName = parameterName; + this.constructorType = constructorType; + this.constructorFieldArguments = + constructorFieldArguments == null + ? java.util.List.of() + : java.util.List.copyOf(constructorFieldArguments); + } + + /** Returns a specification that requests no structured replacement body. */ + public static MethodBodySpec empty() { + return new MethodBodySpec(Kind.EMPTY, null, null, null, java.util.List.of()); + } + + /** Returns a specification that discards a cloned body and uses Java default values. */ + public static MethodBodySpec safeDefault() { + return new MethodBodySpec(Kind.SAFE_DEFAULT, null, null, null, java.util.List.of()); + } + + /** Returns a specification for a concrete implementation of an interface contract. */ + public static MethodBodySpec interfaceContract() { + return new MethodBodySpec( + Kind.INTERFACE_CONTRACT, null, null, null, java.util.List.of()); + } + + /** Returns a builder-style field-assignment specification. */ + public static MethodBodySpec assignFieldAndReturnThis(String fieldName, String parameterName) { + return new MethodBodySpec( + Kind.ASSIGN_FIELD_AND_RETURN_THIS, fieldName, parameterName, null, java.util.List.of()); + } + + /** Returns a specification that constructs and returns the requested type. */ + public static MethodBodySpec returnNew( + String constructorType, java.util.List fieldArguments) { + return new MethodBodySpec(Kind.RETURN_NEW, null, null, constructorType, fieldArguments); + } + + public Kind kind() { + return kind; + } + + public String fieldName() { + return fieldName; + } + + public String parameterName() { + return parameterName; + } + + public String constructorType() { + return constructorType; + } + + public List constructorFieldArguments() { + return constructorFieldArguments; + } + } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/CodeUpdaterMill.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/CodeUpdaterMill.java new file mode 100644 index 00000000..25837db8 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/CodeUpdaterMill.java @@ -0,0 +1,38 @@ +package de.monticore.codeAdaption.updater; + +import de.monticore.codeAdaption.updater.spoonUpdater.SpoonUpdater; +import java.util.Objects; +import java.util.function.Supplier; + +/** Owns the updater lifecycle for one isolated adaptation pass. */ +public final class CodeUpdaterMill { + + private static Supplier updaterProvider = SpoonUpdater::new; + private static CodeUpdater updater; + + private CodeUpdaterMill() {} + + /** Restores the default Spoon-backed updater provider and clears the current updater. */ + public static synchronized void init() { + init(SpoonUpdater::new); + } + + /** Configures an interchangeable updater provider and clears the current updater. */ + public static synchronized void init(Supplier provider) { + updaterProvider = Objects.requireNonNull(provider, "provider"); + updater = null; + } + + /** Returns the updater for the current isolated adaptation pass. */ + public static synchronized CodeUpdater getUpdater() { + if (updater == null) { + updater = Objects.requireNonNull(updaterProvider.get(), "updaterProvider.get()"); + } + return updater; + } + + /** Discards the current updater while retaining the configured provider. */ + public static synchronized void reset() { + updater = null; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/regexUpdater/RegexUpdater.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/regexUpdater/RegexUpdater.java index 5e4e4406..e0a03b35 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/regexUpdater/RegexUpdater.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/regexUpdater/RegexUpdater.java @@ -13,6 +13,8 @@ import java.nio.file.Path; import java.util.Set; +// Fixme: RegExUpdater needs to be repaired +@Deprecated() public class RegexUpdater implements CodeUpdater { protected Set code; @@ -46,6 +48,12 @@ public void updateField( updateElement(srcName, newName); } + @Override + public void updateAssociationRole( + ASTTypeDeclaration srcType, String sourceRole, String concreteRole) { + updateElement(sourceRole, concreteRole); + } + @Override public void updateLocalVariable( ASTTypeDeclaration srcType, diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/ExecutableRewriteRegistry.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/ExecutableRewriteRegistry.java new file mode 100644 index 00000000..08571a4f --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/ExecutableRewriteRegistry.java @@ -0,0 +1,90 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Stores validated executable-signature and stable-key rewrite requests for one Spoon pass. */ +final class ExecutableRewriteRegistry { + private final Map> legacySignaturesByName = + new LinkedHashMap<>(); + private final Map rewritesByReference = new LinkedHashMap<>(); + + void clear() { + legacySignaturesByName.clear(); + rewritesByReference.clear(); + } + + void registerConcreteSignature(String methodName, List parameterTypes) { + ConcreteMethodSignature signature = + ConcreteMethodSignature.create(methodName, parameterTypes); + legacySignaturesByName + .computeIfAbsent(signature.methodName(), ignored -> new LinkedHashSet<>()) + .add(signature); + } + + void registerRewrite(StableElementKey reference, StableElementKey concrete) { + MethodRewrite rewrite = new MethodRewrite(reference, concrete); + rewritesByReference.put(rewrite.reference(), rewrite); + } + + boolean isEmpty() { + return legacySignaturesByName.isEmpty() && rewritesByReference.isEmpty(); + } + + boolean hasRewrites() { + return !rewritesByReference.isEmpty(); + } + + Collection rewrites() { + return List.copyOf(rewritesByReference.values()); + } + + List unambiguousLegacyParameters(String methodName) { + Set signatures = legacySignaturesByName.get(methodName); + return signatures == null || signatures.size() != 1 + ? null + : signatures.iterator().next().parameterTypes(); + } + + long concreteTargetsNamed(String methodName) { + return rewritesByReference.values().stream() + .map(MethodRewrite::concrete) + .filter(key -> methodName.equals(key.getName())) + .count(); + } + + /** Normalized legacy method signature registered without owner identity. */ + record ConcreteMethodSignature(String methodName, List parameterTypes) { + ConcreteMethodSignature { + if (methodName == null || methodName.isBlank()) { + throw new IllegalArgumentException("Method name must not be blank"); + } + if (parameterTypes == null) { + throw new IllegalArgumentException("Parameter types must not be null"); + } + parameterTypes = parameterTypes.stream().map(JavaSourceNames::normalizeType).toList(); + } + + static ConcreteMethodSignature create(String methodName, List parameterTypes) { + return new ConcreteMethodSignature(methodName, parameterTypes); + } + } + + /** Owner- and signature-aware mapping from a reference method to its concrete target. */ + record MethodRewrite(StableElementKey reference, StableElementKey concrete) { + MethodRewrite { + if (reference == null + || concrete == null + || reference.getKind() != StableElementKey.Kind.METHOD + || concrete.getKind() != StableElementKey.Kind.METHOD) { + throw new IllegalArgumentException("Method rewrites require two method keys"); + } + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonElementResolver.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonElementResolver.java new file mode 100644 index 00000000..44655c30 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonElementResolver.java @@ -0,0 +1,230 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import static de.monticore.codeAdaption.utils.JavaLoader.print; + +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; +import spoon.reflect.CtModel; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtType; + +final class SpoonElementResolver { + + private final Supplier model; + private final Map> typeMap = new LinkedHashMap<>(); + private final Map> methodMap = new LinkedHashMap<>(); + + SpoonElementResolver(Supplier model) { + this.model = model; + } + + void reset() { + typeMap.clear(); + methodMap.clear(); + } + + CtType getSpoonType(ASTTypeDeclaration mcType) { + if (typeMap.containsKey(mcType)) { + return typeMap.get(mcType); + } + CtModel currentModel = model.get(); + if (currentModel == null) { + throw new IllegalStateException("Cannot resolve a type before a Spoon model has been loaded"); + } + List> candidates = + currentModel.getAllTypes().stream() + .filter(candidate -> mcType.getName().equals(candidate.getSimpleName())) + .toList(); + CtType type = resolveTypeBySource(mcType, candidates); + typeMap.put(mcType, type); + return type; + } + + CtMethod getSpoonMethod(ASTTypeDeclaration mcType, ASTMethodDeclaration mcMethod) { + return findSpoonMethod(mcType, mcMethod) + .orElseThrow( + () -> + new IllegalStateException( + "No Spoon method corresponds to '" + + mcMethod.getName() + + "' in type '" + + mcType.getName() + + "'")); + } + + Optional> findSpoonMethod( + ASTTypeDeclaration mcType, ASTMethodDeclaration mcMethod) { + if (methodMap.containsKey(mcMethod)) { + return Optional.of(methodMap.get(mcMethod)); + } + + CtType spoonType = getSpoonType(mcType); + List> candidates = + spoonType.getMethods().stream() + .filter(candidate -> compare(mcMethod, candidate)) + .toList(); + Optional> method = + candidates.isEmpty() + ? Optional.empty() + : Optional.of( + requireUnique( + candidates, + "declared Spoon method '" + + mcMethod.getName() + + "' in '" + + spoonType.getQualifiedName() + + "'")); + method.ifPresent(value -> methodMap.put(mcMethod, value)); + return method; + } + + void cacheType(ASTTypeDeclaration mcType, CtType spoonType) { + typeMap.put(mcType, spoonType); + } + + boolean compare(ASTTypeDeclaration type, CtType spoonType) { + if (!type.getName().equals(spoonType.getSimpleName())) { + return false; + } + Optional expectedFile = sourceFile(type); + if (expectedFile.isEmpty()) { + return true; + } + return spoonSourceFile(spoonType) + .map( + actual -> + actual.equals(expectedFile.get()) + || commonTrailingSegments(actual, expectedFile.get()) >= 2) + .orElse(false); + } + + boolean compare(ASTMethodDeclaration mcMethod, CtMethod spoonMethod) { + if (!mcMethod.getName().equals(spoonMethod.getSimpleName())) { + return false; + } + if (!mcMethod.getFormalParameters().isPresentFormalParameterListing()) { + return spoonMethod.getParameters().isEmpty(); + } + List mcParams = + mcMethod.getFormalParameters().getFormalParameterListing().getFormalParameterList(); + if (spoonMethod.getParameters().size() != mcParams.size()) { + return false; + } + for (int i = 0; i < spoonMethod.getParameters().size(); i++) { + String expected = JavaSourceNames.normalizeType(print(mcParams.get(i).getMCType())); + String actual = normalizedSpoonType(spoonMethod.getParameters().get(i).getType()); + if (!expected.equals(actual)) { + return false; + } + } + + return true; + } + + private static String normalizedSpoonType(spoon.reflect.reference.CtTypeReference type) { + if (type == null) { + return "Object"; + } + return JavaSourceNames.normalizeType(type.toString()); + } + + private static Optional sourceFile(ASTTypeDeclaration type) { + return type.get_SourcePositionStart().getFileName().flatMap(SpoonElementResolver::pathOf); + } + + private static CtType resolveTypeBySource( + ASTTypeDeclaration mcType, List> candidates) { + if (candidates.isEmpty()) { + throw new IllegalStateException( + "No Spoon type corresponds to JavaDSL type '" + mcType.getName() + "'"); + } + Optional expected = sourceFile(mcType); + if (expected.isPresent()) { + List> exact = + candidates.stream() + .filter( + candidate -> + spoonSourceFile(candidate).map(expected.get()::equals).orElse(false)) + .toList(); + if (!exact.isEmpty()) { + return requireUnique(exact, "exact source match for '" + mcType.getName() + "'"); + } + + int greatestSuffix = + candidates.stream() + .map(SpoonElementResolver::spoonSourceFile) + .flatMap(Optional::stream) + .mapToInt(actual -> commonTrailingSegments(actual, expected.get())) + .max() + .orElse(0); + if (greatestSuffix >= 2) { + List> suffixMatches = + candidates.stream() + .filter( + candidate -> + spoonSourceFile(candidate) + .map(actual -> commonTrailingSegments(actual, expected.get())) + .orElse(0) + == greatestSuffix) + .toList(); + return requireUnique( + suffixMatches, + "best source-suffix match for '" + mcType.getName() + "' from " + expected.get()); + } + } + return requireUnique( + candidates, + "globally unique Spoon type named '" + + mcType.getName() + + "' (source " + + expected.map(Path::toString).orElse("unknown") + + ")"); + } + + private static Optional spoonSourceFile(CtType type) { + if (type.getPosition() == null || !type.getPosition().isValidPosition()) { + return Optional.empty(); + } + return Optional.of(type.getPosition().getFile().toPath().toAbsolutePath().normalize()); + } + + private static int commonTrailingSegments(Path first, Path second) { + int common = 0; + int firstIndex = first.getNameCount() - 1; + int secondIndex = second.getNameCount() - 1; + while (firstIndex >= 0 + && secondIndex >= 0 + && first.getName(firstIndex).toString().equals(second.getName(secondIndex).toString())) { + common++; + firstIndex--; + secondIndex--; + } + return common; + } + + private static Optional pathOf(String fileName) { + try { + return Optional.of(Path.of(fileName).toAbsolutePath().normalize()); + } catch (InvalidPathException ignored) { + return Optional.empty(); + } + } + + private static T requireUnique(List candidates, String description) { + int candidateCount = candidates.size(); + if (candidateCount != 1) { + throw new IllegalStateException( + "Expected exactly one " + description + " but found " + candidateCount); + } + return candidates.get(0); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonExecutableRepairService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonExecutableRepairService.java new file mode 100644 index 00000000..731683ad --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonExecutableRepairService.java @@ -0,0 +1,532 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import spoon.reflect.code.CtExpression; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.ModifierKind; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.filter.TypeFilter; + +/** + * Repairs executable references and signatures after declaration and type transformations. + * + *

This service runs after grouping and declaration rewrites. It resolves overloads by stable + * owner/signature keys and uses guarded no-classpath fallbacks where Spoon cannot resolve a + * declaration. + */ +final class SpoonExecutableRepairService { + private final SpoonWorkspace workspace; + private final SpoonElementResolver resolver; + private final ExecutableRewriteRegistry rewriteRegistry = new ExecutableRewriteRegistry(); + + /** Creates an executable repair phase over the currently loaded Spoon workspace. */ + SpoonExecutableRepairService(SpoonWorkspace workspace, SpoonElementResolver resolver) { + this.workspace = workspace; + this.resolver = resolver; + } + + /** Clears all method signatures and stable-key rewrites registered for the previous pass. */ + void reset() { + rewriteRegistry.clear(); + } + + /** + * Registers an owner-independent concrete signature used by legacy update calls. Duplicate + * normalized signatures are collapsed by the registry. + */ + void registerConcreteMethodSignature(String methodName, List parameterTypes) { + if (methodName != null && !methodName.isBlank() && parameterTypes != null) { + rewriteRegistry.registerConcreteSignature(methodName, parameterTypes); + } + } + + /** Registers an owner- and signature-aware reference-method to concrete-method rewrite. */ + void registerMethodRewrite(StableElementKey reference, StableElementKey concrete) { + rewriteRegistry.registerRewrite( + qualifyUniqueModelOwner(reference), qualifyUniqueModelOwner(concrete)); + } + + private StableElementKey qualifyUniqueModelOwner(StableElementKey key) { + String owner = key.getOwnerType().orElse(null); + if (owner == null || owner.contains(".")) { + return key; + } + List matches = + workspace.model().getAllTypes().stream() + .filter(type -> owner.equals(type.getSimpleName())) + .map(CtType::getQualifiedName) + .distinct() + .toList(); + return matches.size() == 1 ? key.withOwnerType(matches.get(0)) : key; + } + + /** + * Resolves a source method to its Spoon declaration and captures only the invocations that target + * that declaration. Capturing happens before the declaration is renamed so unresolved invocation + * names can be updated afterward without affecting unrelated overloads. + */ + MethodRename planMethodRename( + ASTTypeDeclaration sourceType, ASTMethodDeclaration sourceMethod) { + CtMethod target = resolver.getSpoonMethod(sourceType, sourceMethod); + String oldName = target.getSimpleName(); + CtType owner = target.getDeclaringType(); + List> invocations = new ArrayList<>(); + for (CtInvocation invocation : + workspace.model().getElements(new TypeFilter<>(CtInvocation.class))) { + if (invocation.getExecutable() != null + && oldName.equals(invocation.getExecutable().getSimpleName()) + && invokesMethodOn(invocation, target, owner)) { + invocations.add(invocation); + } + } + return new MethodRename(target, List.copyOf(invocations)); + } + + /** Applies a planned new name to the captured invocation executable references. */ + void renameInvocations(MethodRename rename, String newName) { + rename.invocations().forEach(invocation -> invocation.getExecutable().setSimpleName(newName)); + } + + /** + * Performs the final executable repair phase before printing: concrete invocation signatures are + * applied first, then illegal bodies are removed from abstract interface methods. + */ + void prepareForPrint(Map groupingMappings) { + applyConcreteMethodSignatures(groupingMappings); + enforceInterfaceMethodBodies(); + } + + /** + * Tests whether an invocation targets a selected method using, in order, resolved declaration + * identity, executable declaring-type identity, and a lexical owner/superclass fallback for + * Spoon's no-classpath mode. + */ + private boolean invokesMethodOn( + CtInvocation invocation, CtMethod target, CtType owner) { + try { + var declaration = invocation.getExecutable().getDeclaration(); + if (declaration != null) { + return declaration == target; + } + } catch (RuntimeException ignored) { + // Continue with type identity in no-classpath mode. + } + try { + CtTypeReference declaringType = invocation.getExecutable().getDeclaringType(); + if (declaringType != null && owner != null) { + return sameOwnerTypeName(declaringType.getQualifiedName(), owner.getQualifiedName()); + } + } catch (RuntimeException ignored) { + // Unresolved inherited calls are handled by the lexical owner fallback below. + } + CtType lexicalOwner = invocation.getParent(CtType.class); + if (lexicalOwner == null || owner == null) { + return false; + } + if (sameOwnerTypeName(lexicalOwner.getQualifiedName(), owner.getQualifiedName())) { + return true; + } + CtTypeReference superclass = lexicalOwner.getSuperclass(); + return superclass != null + && sameOwnerTypeName(superclass.getQualifiedName(), owner.getQualifiedName()); + } + + /** + * Reconciles every invocation with its registered concrete method signature. It renames resolved + * targets and adds missing arguments, but rejects calls that already supply too many arguments. + */ + private void applyConcreteMethodSignatures(Map groupingMappings) { + if (rewriteRegistry.isEmpty()) { + return; + } + for (CtInvocation invocation : + workspace.model().getElements(new TypeFilter<>(CtInvocation.class))) { + String methodName = + invocation.getExecutable() == null ? null : invocation.getExecutable().getSimpleName(); + StableElementKey target = + findConcreteRewriteTarget(invocation, methodName, groupingMappings); + List parameterTypes = + target == null + ? unambiguousLegacyParameters(methodName) + : target.getParameterTypeSources(); + if (parameterTypes == null) { + continue; + } + if (target != null && invocation.getExecutable() != null) { + invocation.getExecutable().setSimpleName(target.getName()); + } + if (invocation.getArguments().size() > parameterTypes.size()) { + throw new IllegalStateException( + "Concrete method '" + methodName + "' has fewer parameters than " + invocation); + } + for (int index = invocation.getArguments().size(); index < parameterTypes.size(); index++) { + invocation.addArgument(missingArgument(invocation, parameterTypes.get(index))); + } + } + } + + /** + * Supplies a missing concrete argument from the unique compatible enclosing parameter, or uses + * the Java default value when no unique parameter is available. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + private CtExpression missingArgument(CtInvocation invocation, String parameterType) { + CtMethod enclosingMethod = invocation.getParent(CtMethod.class); + if (enclosingMethod != null) { + CtTypeReference expected = expectedTypeReference(enclosingMethod, parameterType); + List> exact = + enclosingMethod.getParameters().stream() + .filter(parameter -> parameter.getType() != null) + .filter( + parameter -> + sameArgumentType(parameter.getType(), expected, parameterType)) + .toList(); + if (exact.size() == 1) { + return workspace.factory().Code().createVariableRead(exact.get(0).getReference(), false); + } + if (exact.isEmpty()) { + List> compatibleSubtypes = + enclosingMethod.getParameters().stream() + .filter(parameter -> isSubtype(parameter.getType(), expected)) + .toList(); + if (compatibleSubtypes.size() == 1) { + return workspace + .factory() + .Code() + .createVariableRead(compatibleSubtypes.get(0).getReference(), false); + } + } + } + return workspace.defaultExpression(parameterType); + } + + private CtTypeReference expectedTypeReference( + CtMethod enclosingMethod, String parameterType) { + if (parameterType != null && parameterType.contains(".")) { + Optional> qualifiedMatch = + workspace.model().getAllTypes().stream() + .filter(type -> parameterType.equals(type.getQualifiedName())) + .findFirst(); + if (qualifiedMatch.isPresent()) { + return qualifiedMatch.get().getReference(); + } + } + if (parameterType != null && !parameterType.contains(".")) { + String simpleName = JavaSourceNames.simpleName(parameterType); + List> modelMatches = + workspace.model().getAllTypes().stream() + .filter(type -> simpleName.equals(type.getSimpleName())) + .toList(); + if (modelMatches.size() == 1) { + return modelMatches.get(0).getReference(); + } + CtType owner = enclosingMethod.getParent(CtType.class); + if (owner != null && owner.getPackage() != null) { + String localName = owner.getPackage().getQualifiedName() + "." + simpleName; + Optional> localMatch = + modelMatches.stream() + .filter(type -> localName.equals(type.getQualifiedName())) + .findFirst(); + if (localMatch.isPresent()) { + return localMatch.get().getReference(); + } + } + } + return workspace.createTypeReference(parameterType); + } + + private boolean isSubtype(CtTypeReference actual, CtTypeReference expected) { + if (actual == null || expected == null || actual.isPrimitive() || expected.isPrimitive()) { + return false; + } + String actualName = resolvedQualifiedName(actual); + String expectedName = resolvedQualifiedName(expected); + if (expectedName.contains(".") && !actualName.contains(".")) { + return false; + } + if (actualName.contains(".") + && expectedName.contains(".") + && !actualName.equals(expectedName) + && actual.getSimpleName().equals(expected.getSimpleName())) { + return false; + } + try { + return actual.isSubtypeOf(expected); + } catch (RuntimeException ignored) { + return false; + } + } + + /** Does not equate distinct known qualified types that merely share a simple name. */ + private boolean sameArgumentType( + CtTypeReference actual, CtTypeReference expected, String expectedSource) { + if (actual == null || expected == null) { + return false; + } + String actualIdentity = canonicalWorkspaceType(actual.toString()); + String expectedIdentity = + canonicalWorkspaceType(expectedSource == null ? expected.toString() : expectedSource); + return actualIdentity.equals(expectedIdentity); + } + + private static String resolvedQualifiedName(CtTypeReference type) { + try { + CtType declaration = type.getTypeDeclaration(); + if (declaration != null && declaration.getQualifiedName() != null) { + return declaration.getQualifiedName(); + } + } catch (RuntimeException ignored) { + // Fall back to the reference's own no-classpath name. + } + String qualifiedName = type.getQualifiedName(); + return qualifiedName == null ? type.getSimpleName() : qualifiedName; + } + + /** + * Selects the unique stable-key rewrite matching invocation name, owner, and argument types. + * Null-literal argument types act as wildcards because Spoon cannot infer their target type. + */ + private StableElementKey findConcreteRewriteTarget( + CtInvocation invocation, + String methodName, + Map groupingMappings) { + if (methodName == null || !rewriteRegistry.hasRewrites()) { + return null; + } + String owner = ownerName(invocation); + List invocationTypes = invocationArgumentTypes(invocation); + List matches = new ArrayList<>(); + for (ExecutableRewriteRegistry.MethodRewrite rewrite : rewriteRegistry.rewrites()) { + StableElementKey reference = rewrite.reference(); + StableElementKey concrete = rewrite.concrete(); + if ((!methodName.equals(reference.getName()) && !methodName.equals(concrete.getName())) + || !ownerMatches(owner, reference, concrete, groupingMappings)) { + continue; + } + if (!signatureMatches(reference.getParameterTypes(), invocationTypes)) { + continue; + } + matches.add(concrete); + } + if (matches.size() == 1) { + return matches.get(0); + } + if (matches.size() > 1) { + StableElementKey first = matches.get(0); + if (matches.stream().allMatch(first::sameSignatureIgnoringReturn)) { + return first; + } + throw new IllegalStateException( + "Ambiguous method rewrite for '" + + methodName + + "' on owner '" + + owner + + "' with arguments " + + invocationTypes); + } + return null; + } + + /** Returns whether an invocation owner matches the reference, concrete, or grouping owner. */ + private boolean ownerMatches( + String owner, + StableElementKey reference, + StableElementKey concrete, + Map groupingMappings) { + if (owner == null + || (reference.getOwnerType().isEmpty() && concrete.getOwnerType().isEmpty())) { + return true; + } + String referenceOwner = reference.getOwnerType().orElse(null); + String concreteOwner = concrete.getOwnerType().orElse(null); + String groupedOwner = groupingMappings.get(concreteOwner); + if (groupedOwner == null && concreteOwner != null) { + groupedOwner = groupingMappings.get(JavaSourceNames.simpleName(concreteOwner)); + } + if (groupedOwner != null + && !groupedOwner.contains(".") + && concreteOwner != null + && concreteOwner.contains(".")) { + groupedOwner = + concreteOwner.substring(0, concreteOwner.lastIndexOf('.') + 1) + groupedOwner; + } + return sameOwnerTypeName(owner, referenceOwner) + || sameOwnerTypeName(owner, concreteOwner) + || sameOwnerTypeName(owner, groupedOwner); + } + + /** Compares normalized parameter types, treating unknown actual argument types as compatible. */ + private boolean signatureMatches(List expected, List actual) { + if (expected.size() != actual.size()) { + return false; + } + for (int index = 0; index < expected.size(); index++) { + String actualType = actual.get(index); + if (actualType != null + && !canonicalWorkspaceType(expected.get(index)) + .equals(canonicalWorkspaceType(actualType))) { + return false; + } + } + return true; + } + + /** + * Returns normalized invocation argument types; unresolved and Spoon {@code } values + * are represented as {@code null}. + */ + private static List invocationArgumentTypes(CtInvocation invocation) { + List result = new ArrayList<>(); + for (CtExpression argument : invocation.getArguments()) { + CtTypeReference type = argument.getType(); + String qualifiedName = type == null ? null : type.getQualifiedName(); + // Spoon represents null literals as the synthetic type "". + result.add( + qualifiedName == null || qualifiedName.isBlank() || qualifiedName.startsWith("<") + ? null + : JavaSourceNames.canonicalType(type.toString())); + } + return result; + } + + /** + * Returns legacy parameters only when one signature and at most one concrete target use the + * method name. + */ + private List unambiguousLegacyParameters(String methodName) { + List parameters = rewriteRegistry.unambiguousLegacyParameters(methodName); + if (parameters == null) { + return null; + } + return rewriteRegistry.concreteTargetsNamed(methodName) <= 1 ? parameters : null; + } + + /** + * Resolves an invocation owner from its executable, its explicit target, or finally the lexical + * enclosing type. + */ + private static String ownerName(CtInvocation invocation) { + String executableOwner = null; + try { + if (invocation.getExecutable() != null + && invocation.getExecutable().getDeclaringType() != null) { + executableOwner = invocation.getExecutable().getDeclaringType().getQualifiedName(); + } + } catch (RuntimeException ignored) { + // Spoon may not resolve declaring types in no-classpath mode. + } + String targetOwner = null; + try { + if (invocation.getTarget() != null && invocation.getTarget().getType() != null) { + targetOwner = invocation.getTarget().getType().getQualifiedName(); + } + } catch (RuntimeException ignored) { + // Fall through to the lexical owner. + } + if (isQualifiedOwner(targetOwner) && !isQualifiedOwner(executableOwner)) { + return targetOwner; + } + if (executableOwner != null && !executableOwner.isBlank()) { + return executableOwner; + } + if (targetOwner != null && !targetOwner.isBlank()) { + return targetOwner; + } + CtType parent = invocation.getParent(CtType.class); + return parent == null ? null : parent.getQualifiedName(); + } + + private static boolean isQualifiedOwner(String owner) { + return owner != null && owner.contains("."); + } + + /** Removes bodies from non-default, non-static, non-private interface methods. */ + private void enforceInterfaceMethodBodies() { + for (CtType type : workspace.model().getAllTypes()) { + if (!type.isInterface()) { + continue; + } + for (CtMethod method : type.getMethods()) { + if (!method.isDefaultMethod() + && !method.hasModifier(ModifierKind.STATIC) + && !method.hasModifier(ModifierKind.PRIVATE)) { + method.setBody(null); + } + } + } + } + + /** + * Compares executable owners exactly. An unqualified no-classpath owner is resolved only when the + * loaded model proves that its simple name identifies one declaration. + */ + private boolean sameOwnerTypeName(String first, String second) { + if (first == null || second == null) { + return false; + } + String normalizedFirst = first.replace('$', '.'); + String normalizedSecond = second.replace('$', '.'); + if (normalizedFirst.contains(".") && normalizedSecond.contains(".")) { + return normalizedFirst.equals(normalizedSecond); + } + String simpleFirst = JavaSourceNames.simpleName(normalizedFirst); + String simpleSecond = JavaSourceNames.simpleName(normalizedSecond); + if (!simpleFirst.equals(simpleSecond)) { + return false; + } + List modelOwners = + workspace.model().getAllTypes().stream() + .filter(type -> simpleFirst.equals(type.getSimpleName())) + .map(CtType::getQualifiedName) + .distinct() + .toList(); + if (modelOwners.size() != 1) { + return false; + } + String resolved = modelOwners.get(0).replace('$', '.'); + return (!normalizedFirst.contains(".") || resolved.equals(normalizedFirst)) + && (!normalizedSecond.contains(".") || resolved.equals(normalizedSecond)); + } + + /** Resolves unqualified type leaves only from unique model declarations or direct java.lang. */ + private String canonicalWorkspaceType(String source) { + return JavaSourceNames.canonicalType( + JavaSourceNames.replaceTypeNames( + source, + reference -> { + if (reference.qualified()) { + return Optional.empty(); + } + List modelTypes = + workspace.model().getAllTypes().stream() + .filter(type -> reference.simpleName().equals(type.getSimpleName())) + .map(CtType::getQualifiedName) + .distinct() + .toList(); + if (modelTypes.size() == 1) { + return Optional.of(modelTypes.get(0)); + } + String javaLang = "java.lang." + reference.simpleName(); + try { + Class.forName( + javaLang, false, SpoonExecutableRepairService.class.getClassLoader()); + return Optional.of(javaLang); + } catch (ClassNotFoundException | LinkageError ignored) { + return Optional.empty(); + } + })); + } + + /** Method declaration and invocations resolved to it before the rename. */ + record MethodRename(CtMethod target, List> invocations) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonGenerationService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonGenerationService.java new file mode 100644 index 00000000..ed58d86b --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonGenerationService.java @@ -0,0 +1,585 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import de.monticore.codeAdaption.updater.CodeUpdater.MethodBodySpec; +import de.monticore.java.javadsl._ast.ASTFieldDeclaration; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import spoon.reflect.code.CtAssignment; +import spoon.reflect.code.CtBlock; +import spoon.reflect.code.CtConstructorCall; +import spoon.reflect.code.CtExpression; +import spoon.reflect.code.CtLiteral; +import spoon.reflect.code.CtReturn; +import spoon.reflect.code.CtThisAccess; +import spoon.reflect.code.CtTypeAccess; +import spoon.reflect.code.CtVariableAccess; +import spoon.reflect.declaration.CtField; +import spoon.reflect.declaration.CtEnum; +import spoon.reflect.declaration.CtEnumValue; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.ModifierKind; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.reference.CtParameterReference; +import spoon.reflect.reference.CtVariableReference; +import spoon.refactoring.Refactoring; + +/** + * Creates, clones, completes, and removes Spoon declarations and constructs supported method + * bodies. + * + *

It does not rename existing declarations or rewrite references; those mutations belong to + * {@link SpoonTransformationService}. + */ +final class SpoonGenerationService { + private final SpoonWorkspace workspace; + private final SpoonElementResolver resolver; + + SpoonGenerationService(SpoonWorkspace workspace, SpoonElementResolver resolver) { + this.workspace = workspace; + this.resolver = resolver; + } + + void addField( + ASTTypeDeclaration targetType, + ASTFieldDeclaration templateField, + String newName, + String newType, + boolean isStatic) { + requireName(newName, "Field name"); + requireName(newType, "Field type"); + CtType spoonType = resolver.getSpoonType(targetType); + CtField existingField = spoonType.getField(newName); + if (existingField != null) { + existingField.setType(workspace.createTypeReference(newType)); + setStatic(existingField, isStatic); + return; + } + String templateName = + templateField == null + ? null + : templateField.getVariableDeclarator(0).getDeclarator().getName(); + CtField sourceField = templateName == null ? null : spoonType.getField(templateName); + CtField clone = + sourceField == null ? workspace.factory().Core().createField() : sourceField.clone(); + clone.setSimpleName(newName); + clone.setType(workspace.createTypeReference(newType)); + setStatic(clone, isStatic); + spoonType.addField(clone); + } + + void addType(ASTTypeDeclaration templateType, String newName) { + requireName(newName, "Type name"); + CtType source = resolver.getSpoonType(templateType); + CtType existing = + source.getPackage() == null ? null : source.getPackage().getType(newName); + if (existing != null && existing != source) { + return; + } + // Every adaptation selection owns an isolated source tree. Transforming that run's template + // avoids leaking the unadapted template beside the generated type and eliminates merge + // collisions between otherwise independent incarnation runs. + Refactoring.changeTypeName(source, newName); + resolver.cacheType(templateType, source); + } + + void addSuperType( + ASTTypeDeclaration targetType, String superTypeName, boolean interfaceType) { + requireName(superTypeName, "Supertype name"); + CtType target = resolver.getSpoonType(targetType); + CtTypeReference reference = workspace.createTypeReference(superTypeName); + if (interfaceType || target.isInterface()) { + boolean present = + target.getSuperInterfaces().stream() + .anyMatch(existing -> sameSuperInterface(existing, reference, target)); + if (!present) { + target.addSuperInterface(reference); + } + } else { + target.setSuperclass(reference); + } + } + + /** Compares interfaces by resolved Java identity. */ + private boolean sameSuperInterface( + CtTypeReference existing, CtTypeReference requested, CtType target) { + return superTypeIdentity(existing, target).equals(superTypeIdentity(requested, target)); + } + + private String superTypeIdentity(CtTypeReference reference, CtType target) { + try { + CtType declaration = reference.getTypeDeclaration(); + if (declaration != null && declaration.getQualifiedName() != null) { + return declaration.getQualifiedName().replace('$', '.'); + } + } catch (RuntimeException ignored) { + // Resolve no-classpath references from the loaded model below. + } + String qualifiedName = reference.getQualifiedName(); + if (qualifiedName != null && qualifiedName.contains(".")) { + return qualifiedName.replace('$', '.'); + } + List modelMatches = + workspace.model().getAllTypes().stream() + .filter(type -> reference.getSimpleName().equals(type.getSimpleName())) + .map(CtType::getQualifiedName) + .distinct() + .toList(); + if (modelMatches.size() == 1) { + return modelMatches.get(0).replace('$', '.'); + } + String packageName = + target.getPackage() == null ? "" : target.getPackage().getQualifiedName(); + return packageName.isEmpty() + ? reference.getSimpleName() + : packageName + "." + reference.getSimpleName(); + } + + /** Reconciles a generated Java class modifier with the authoritative target CD type. */ + void setTypeAbstract(ASTTypeDeclaration targetType, boolean isAbstract) { + CtType target = resolver.getSpoonType(targetType); + if (target.isInterface()) { + return; + } + if (isAbstract) { + target.addModifier(ModifierKind.ABSTRACT); + } else { + target.removeModifier(ModifierKind.ABSTRACT); + } + } + + void addEnumConstant( + ASTTypeDeclaration targetType, String constantName, int expectedIndex) { + requireName(constantName, "Enum constant name"); + CtType target = resolver.getSpoonType(targetType); + if (!(target instanceof CtEnum enumType)) { + throw new IllegalStateException(target.getQualifiedName() + " is not an enum"); + } + boolean present = + enumType.getEnumValues().stream() + .anyMatch(value -> constantName.equals(value.getSimpleName())); + if (!present) { + CtEnumValue value = workspace.factory().Core().createEnumValue(); + value.setSimpleName(constantName); + List> values = new ArrayList<>(enumType.getEnumValues()); + values.add(Math.min(Math.max(expectedIndex, 0), values.size()), value); + enumType.setEnumValues(values); + } + } + + void addMethod( + ASTTypeDeclaration targetType, + ASTMethodDeclaration templateMethod, + String newName, + List parameterTypes, + List parameterNames, + String returnType, + boolean isStatic, + MethodBodySpec methodBody) { + validateMethodInput(newName, parameterTypes, parameterNames); + Objects.requireNonNull(methodBody, "Method body specification must not be null"); + if (methodBody.kind() == MethodBodySpec.Kind.INTERFACE_CONTRACT && isStatic) { + throw new IllegalStateException("An interface contract implementation cannot be static"); + } + CtType spoonType = resolver.getSpoonType(targetType); + CtMethod existingMethod = findMethod(spoonType, newName, parameterTypes); + if (existingMethod != null) { + if (methodBody.kind() == MethodBodySpec.Kind.INTERFACE_CONTRACT) { + completeExistingInterfaceContract(existingMethod, returnType); + return; + } + existingMethod.setType(workspace.createTypeReference(returnType)); + setStatic(existingMethod, isStatic); + return; + } + if (methodBody.kind() == MethodBodySpec.Kind.INTERFACE_CONTRACT + && hasInheritedInterfaceImplementation( + spoonType, newName, parameterTypes, returnType)) { + return; + } + CtMethod clone = + cloneConfiguredMethod( + targetType, + templateMethod, + newName, + parameterTypes, + parameterNames, + returnType, + hasReplacementBody(methodBody)); + setStatic(clone, isStatic); + if (methodBody.kind() == MethodBodySpec.Kind.INTERFACE_CONTRACT) { + clone.addModifier(ModifierKind.PUBLIC); + clone.removeModifier(ModifierKind.ABSTRACT); + } + applyMethodBodySpec(targetType, clone, methodBody); + spoonType.addMethod(clone); + } + + /** + * Turns an existing declaration into a legal concrete interface implementation without + * replacing a handwritten body. Signature conflicts are rejected before visibility/body repair. + */ + private void completeExistingInterfaceContract( + CtMethod existingMethod, String contractReturnType) { + if (existingMethod.hasModifier(ModifierKind.STATIC)) { + throw new IllegalStateException( + "Static method " + + existingMethod.getSignature() + + " cannot implement an interface contract"); + } + CtTypeReference existingReturn = existingMethod.getType(); + CtTypeReference contractReturn = workspace.createTypeReference(contractReturnType); + if (!isCompatibleInterfaceReturn(existingReturn, contractReturn)) { + throw new IllegalStateException( + "Method " + + existingMethod.getSignature() + + " returns " + + typeName(existingReturn) + + " but interface contract requires " + + typeName(contractReturn)); + } + existingMethod.removeModifier(ModifierKind.PRIVATE); + existingMethod.removeModifier(ModifierKind.PROTECTED); + existingMethod.addModifier(ModifierKind.PUBLIC); + existingMethod.removeModifier(ModifierKind.ABSTRACT); + if (existingMethod.getBody() == null) { + existingMethod.setBody(safeDefaultBody(typeName(existingReturn))); + } + } + + /** Accepts identical returns and Java covariance, but never mixes primitive and reference types. */ + private boolean isCompatibleInterfaceReturn( + CtTypeReference actualReturn, CtTypeReference expectedReturn) { + if (actualReturn == null || expectedReturn == null) { + return false; + } + if (sameType(actualReturn, expectedReturn)) { + return true; + } + if (isPrimitiveOrVoid(actualReturn) || isPrimitiveOrVoid(expectedReturn)) { + return false; + } + if ("Object".equals(expectedReturn.getSimpleName())) { + return true; + } + try { + return actualReturn.isSubtypeOf(expectedReturn); + } catch (RuntimeException ignored) { + return false; + } + } + + private boolean sameType(CtTypeReference first, CtTypeReference second) { + String firstQualified = first.getQualifiedName(); + String secondQualified = second.getQualifiedName(); + if (Objects.equals(firstQualified, secondQualified)) { + return true; + } + return first.getSimpleName().equals(second.getSimpleName()) + && (first.isSimplyQualified() || second.isSimplyQualified()); + } + + private boolean isPrimitiveOrVoid(CtTypeReference type) { + return type.isPrimitive() || "void".equals(type.getSimpleName()); + } + + private String typeName(CtTypeReference type) { + if (type == null) { + return "Object"; + } + String qualifiedName = type.getQualifiedName(); + return qualifiedName == null || qualifiedName.isBlank() ? type.getSimpleName() : qualifiedName; + } + + private boolean requiresSignatureOnlyMethod( + ASTTypeDeclaration targetType, ASTMethodDeclaration templateMethod) { + CtType target = resolver.getSpoonType(targetType); + if (target.isInterface()) { + return true; + } + if (templateMethod == null) { + return false; + } + return resolver + .findSpoonMethod(targetType, templateMethod) + .map(method -> method.hasModifier(ModifierKind.ABSTRACT)) + .orElse(false); + } + + void removeField(ASTTypeDeclaration targetType, ASTFieldDeclaration field) { + String name = field.getVariableDeclarator(0).getDeclarator().getName(); + CtField target = resolver.getSpoonType(targetType).getField(name); + if (target != null) { + target.delete(); + } + } + + void removeMethod(ASTTypeDeclaration targetType, ASTMethodDeclaration method) { + resolver.findSpoonMethod(targetType, method).ifPresent(CtMethod::delete); + } + + private CtMethod cloneConfiguredMethod( + ASTTypeDeclaration targetType, + ASTMethodDeclaration templateMethod, + String newName, + List parameterTypes, + List parameterNames, + String returnType, + boolean replacementBody) { + CtType target = resolver.getSpoonType(targetType); + CtMethod clone = + templateMethod == null + ? workspace.factory().Core().createMethod() + : resolver + .findSpoonMethod(targetType, templateMethod) + .map(CtMethod::clone) + .orElseGet(() -> workspace.factory().Core().createMethod()); + clone.setSimpleName(newName); + int sharedParameters = Math.min(clone.getParameters().size(), parameterNames.size()); + for (int index = 0; index < sharedParameters; index++) { + updateParameter( + clone, + clone.getParameters().get(index), + parameterNames.get(index), + parameterTypes.get(index)); + } + if (clone.getParameters().size() < parameterNames.size()) { + for (int index = clone.getParameters().size(); index < parameterNames.size(); index++) { + CtParameter parameter = workspace.factory().Core().createParameter(); + parameter.setSimpleName(parameterNames.get(index)); + parameter.setType(workspace.createTypeReference(parameterTypes.get(index))); + clone.addParameter(parameter); + } + } else { + while (clone.getParameters().size() > parameterNames.size()) { + clone.removeParameter(clone.getParameters().get(clone.getParameters().size() - 1)); + } + } + if (returnType != null && !returnType.isBlank()) { + clone.setType(workspace.createTypeReference(returnType)); + } + boolean primitiveBodyReturnsNull = + !replacementBody + && clone.getType() != null + && clone.getType().isPrimitive() + && clone.getElements(new spoon.reflect.visitor.filter.TypeFilter<>(CtReturn.class)) + .stream() + .map(CtReturn::getReturnedExpression) + .anyMatch( + expression -> + expression == null + || (expression instanceof CtLiteral literal + && literal.getValue() == null) + || "null".equals(expression.toString()) + || "".equals(expression.toString())); + if (requiresSignatureOnlyMethod(targetType, templateMethod)) { + clone.setBody(null); + if (!target.isInterface()) { + clone.addModifier(ModifierKind.ABSTRACT); + } + } else if (primitiveBodyReturnsNull) { + clone.setBody(safeDefaultBody(returnType)); + } else if (clone.getBody() == null && !"void".equals(returnType)) { + CtBlock body = workspace.factory().Core().createBlock(); + CtReturn returnStatement = workspace.factory().Core().createReturn(); + returnStatement.setReturnedExpression((CtExpression) workspace.defaultExpression(returnType)); + body.addStatement(returnStatement); + clone.setBody(body); + } else if (clone.getBody() == null) { + clone.setBody(workspace.factory().Core().createBlock()); + } + return clone; + } + + private void updateParameter( + CtMethod method, CtParameter parameter, String newName, String newType) { + String oldName = parameter.getSimpleName(); + if (!oldName.equals(newName)) { + for (CtVariableAccess access : + method.getElements( + new spoon.reflect.visitor.filter.TypeFilter<>(CtVariableAccess.class))) { + if (access.getVariable() == null) { + continue; + } + CtVariableReference reference = access.getVariable(); + if (reference.getDeclaration() == parameter + || (reference instanceof CtParameterReference + && reference.getDeclaration() == null + && oldName.equals(reference.getSimpleName()))) { + reference.setSimpleName(newName); + } + } + parameter.setSimpleName(newName); + } + parameter.setType(workspace.createTypeReference(newType)); + } + + private CtBlock safeDefaultBody(String returnType) { + CtBlock body = workspace.factory().Core().createBlock(); + if (returnType != null && !returnType.isBlank() && !"void".equals(returnType)) { + CtReturn returnStatement = workspace.factory().Core().createReturn(); + returnStatement.setReturnedExpression((CtExpression) workspace.defaultExpression(returnType)); + body.addStatement(returnStatement); + } + return body; + } + + private static boolean hasReplacementBody(MethodBodySpec methodBody) { + return methodBody.kind() != MethodBodySpec.Kind.EMPTY + && methodBody.kind() != MethodBodySpec.Kind.INTERFACE_CONTRACT; + } + + /** Checks inherited declarations without ever mutating the declaring supertype. */ + private boolean hasInheritedInterfaceImplementation( + CtType type, String name, List parameterTypes, String returnType) { + CtTypeReference expectedReturn = workspace.createTypeReference(returnType); + for (CtMethod method : type.getAllMethods()) { + if (method.getDeclaringType() == type || !matchesSignature(method, name, parameterTypes)) { + continue; + } + if (method.hasModifier(ModifierKind.PUBLIC) + && !method.hasModifier(ModifierKind.STATIC) + && !method.hasModifier(ModifierKind.ABSTRACT) + && isCompatibleInterfaceReturn(method.getType(), expectedReturn)) { + return true; + } + } + return false; + } + + private CtMethod findMethod(CtType type, String name, List parameterTypes) { + for (CtMethod method : type.getMethods()) { + if (matchesSignature(method, name, parameterTypes)) { + return method; + } + } + return null; + } + + private boolean matchesSignature( + CtMethod method, String name, List parameterTypes) { + if (!name.equals(method.getSimpleName()) + || method.getParameters().size() != parameterTypes.size()) { + return false; + } + for (int index = 0; index < parameterTypes.size(); index++) { + CtTypeReference actualType = method.getParameters().get(index).getType(); + CtTypeReference expectedType = workspace.createTypeReference(parameterTypes.get(index)); + if (!sameType(actualType, expectedType)) { + return false; + } + } + return true; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void applyMethodBodySpec( + ASTTypeDeclaration targetType, CtMethod clone, MethodBodySpec methodBody) { + if (methodBody.kind() == MethodBodySpec.Kind.EMPTY + || methodBody.kind() == MethodBodySpec.Kind.INTERFACE_CONTRACT) { + return; + } + if (methodBody.kind() == MethodBodySpec.Kind.SAFE_DEFAULT) { + if (!clone.hasModifier(ModifierKind.ABSTRACT)) { + clone.setBody(safeDefaultBody(typeName(clone.getType()))); + } + return; + } + CtBlock body = workspace.factory().Core().createBlock(); + clone.setBody(body); + switch (methodBody.kind()) { + case ASSIGN_FIELD_AND_RETURN_THIS: + requireName(methodBody.fieldName(), "Assigned field name"); + requireName(methodBody.parameterName(), "Assigned parameter name"); + CtParameter parameter = + clone.getParameters().stream() + .filter(value -> value.getSimpleName().equals(methodBody.parameterName())) + .findFirst() + .orElseThrow( + () -> + new IllegalArgumentException( + "Generated body references missing parameter '" + + methodBody.parameterName() + + "'")); + CtAssignment assignment = workspace.factory().Core().createAssignment(); + assignment.setAssigned( + (CtExpression) + workspace + .factory() + .Code() + .createCodeSnippetExpression("this." + methodBody.fieldName())); + CtVariableReference variableReference = parameter.getReference(); + assignment.setAssignment( + (CtExpression) workspace.factory().Code().createVariableRead(variableReference, false)); + body.addStatement(assignment); + CtReturn setterReturn = workspace.factory().Core().createReturn(); + setterReturn.setReturnedExpression((CtExpression) createThisAccess(targetType)); + body.addStatement(setterReturn); + break; + case RETURN_NEW: + requireName(methodBody.constructorType(), "Constructor type"); + CtConstructorCall constructorCall = workspace.factory().Core().createConstructorCall(); + constructorCall.setType(workspace.createTypeReference(methodBody.constructorType())); + for (String fieldArgument : methodBody.constructorFieldArguments()) { + requireName(fieldArgument, "Constructor field argument"); + constructorCall.addArgument( + workspace.factory().Code().createCodeSnippetExpression("this." + fieldArgument)); + } + CtReturn buildReturn = workspace.factory().Core().createReturn(); + buildReturn.setReturnedExpression((CtExpression) constructorCall); + body.addStatement(buildReturn); + break; + default: + throw new IllegalArgumentException("Unsupported method body kind: " + methodBody.kind()); + } + } + + private CtThisAccess createThisAccess(ASTTypeDeclaration targetType) { + CtType target = resolver.getSpoonType(targetType); + CtThisAccess thisAccess = workspace.factory().Core().createThisAccess(); + CtTypeReference typeReference = + workspace.factory().Type().createReference(target.getSimpleName()); + try { + CtTypeAccess typeAccess = workspace.factory().Code().createTypeAccess(typeReference); + thisAccess.setTarget(typeAccess); + } catch (RuntimeException ignored) { + thisAccess.setType(typeReference); + } + return thisAccess; + } + + private static void validateMethodInput( + String methodName, List parameterTypes, List parameterNames) { + requireName(methodName, "Method name"); + Objects.requireNonNull(parameterTypes, "Parameter types must not be null"); + Objects.requireNonNull(parameterNames, "Parameter names must not be null"); + if (parameterTypes.size() != parameterNames.size()) { + throw new IllegalArgumentException( + "Parameter type/name count differs: " + + parameterTypes.size() + + " types and " + + parameterNames.size() + + " names"); + } + parameterTypes.forEach(type -> requireName(type, "Parameter type")); + parameterNames.forEach(name -> requireName(name, "Parameter name")); + } + + private static void requireName(String value, String description) { + if (value == null || value.isBlank()) { + throw new IllegalArgumentException(description + " must not be blank"); + } + } + + private static void setStatic(spoon.reflect.declaration.CtModifiable declaration, boolean value) { + if (value) { + declaration.addModifier(ModifierKind.STATIC); + } else { + declaration.removeModifier(ModifierKind.STATIC); + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonReferenceCodeDependencyAnalyzer.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonReferenceCodeDependencyAnalyzer.java new file mode 100644 index 00000000..2569cf4d --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonReferenceCodeDependencyAnalyzer.java @@ -0,0 +1,450 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import de.monticore.codeAdaption.CodeAdaptationException; +import de.monticore.codeAdaption.dependency.ReferenceCodeSelection; +import de.monticore.java.javadsl._ast.ASTImportDeclaration; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import spoon.Launcher; +import spoon.reflect.CtModel; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.ModifierKind; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.filter.TypeFilter; + +/** Spoon implementation behind {@code ReferenceCodeDependencySelector}. */ +public final class SpoonReferenceCodeDependencyAnalyzer { + private final Map graph; + private final Map> identitiesBySimpleName; + + public SpoonReferenceCodeDependencyAnalyzer( + Path sourceRoot, + Set parsedUnits, + Set referenceCDTypeKeys) { + Path normalizedRoot = sourceRoot.toAbsolutePath().normalize(); + if (!Files.isDirectory(normalizedRoot)) { + throw new IllegalArgumentException( + "Reference source directory does not exist: " + normalizedRoot); + } + + Set normalizedCDTypeKeys = normalizeSimpleNames(referenceCDTypeKeys); + Map contexts = sourceContexts(parsedUnits); + identitiesBySimpleName = indexSimpleNames(contexts.keySet()); + graph = + buildGraph(normalizedRoot, contexts, normalizedCDTypeKeys, identitiesBySimpleName); + } + + public ReferenceCodeSelection select(Set mappedRootTypeIdentities) { + Objects.requireNonNull(mappedRootTypeIdentities, "mappedRootTypeIdentities"); + Set roots = new TreeSet<>(); + for (String requestedRoot : new TreeSet<>(mappedRootTypeIdentities)) { + roots.add(resolveRoot(requestedRoot)); + } + + Set selected = new TreeSet<>(); + Deque work = new ArrayDeque<>(roots); + while (!work.isEmpty()) { + String identity = work.removeFirst(); + if (!selected.add(identity)) { + continue; + } + SourceTypeNode node = graph.get(identity); + if (node == null) { + throw new CodeAdaptationException( + "Selected reference source type is absent from the dependency graph: '" + + identity + + "'"); + } + if (!node.diagnostics().isEmpty()) { + throw new CodeAdaptationException(String.join(System.lineSeparator(), node.diagnostics())); + } + work.addAll(node.dependencies()); + } + + Map> selectedEdges = new TreeMap<>(); + Map> selectedCDKeys = new TreeMap<>(); + for (String identity : selected) { + SourceTypeNode node = graph.get(identity); + Set retainedDependencies = new TreeSet<>(node.dependencies()); + retainedDependencies.retainAll(selected); + selectedEdges.put(identity, retainedDependencies); + selectedCDKeys.put(identity, node.referenceCDTypeKeys()); + } + + Set helpers = new TreeSet<>(selected); + helpers.removeAll(roots); + return new ReferenceCodeSelection( + roots, helpers, selectedEdges, selectedCDKeys, List.of()); + } + + private String resolveRoot(String requestedRoot) { + String normalized = requestedRoot == null ? "" : requestedRoot.trim(); + if (graph.containsKey(normalized)) { + return normalized; + } + String simpleName = simpleName(normalized); + Set candidates = identitiesBySimpleName.getOrDefault(simpleName, Set.of()); + if (candidates.size() == 1) { + return candidates.iterator().next(); + } + if (candidates.isEmpty()) { + throw new CodeAdaptationException( + "Mapped reference Java root '" + requestedRoot + "' does not exist"); + } + throw new CodeAdaptationException( + "Mapped reference Java root '" + + requestedRoot + + "' is ambiguous; candidates are " + + candidates); + } + + private static Map buildGraph( + Path sourceRoot, + Map contexts, + Set referenceCDTypeKeys, + Map> identitiesBySimpleName) { + Launcher launcher = new Launcher(); + launcher.getEnvironment().setNoClasspath(true); + launcher.addInputResource(sourceRoot.toString()); + try { + launcher.buildModel(); + } catch (RuntimeException exception) { + throw new CodeAdaptationException( + "Could not analyze reference Java dependencies in '" + sourceRoot + "'", exception); + } + CtModel model = launcher.getModel(); + + Map> spoonTypes = new TreeMap<>(); + for (CtType type : model.getAllTypes()) { + if (type.isTopLevel() && contexts.containsKey(type.getQualifiedName())) { + spoonTypes.put(type.getQualifiedName(), type); + } + } + + Set missing = new TreeSet<>(contexts.keySet()); + missing.removeAll(spoonTypes.keySet()); + if (!missing.isEmpty()) { + throw new CodeAdaptationException( + "Spoon did not model reference Java top-level types " + missing); + } + + Map result = new TreeMap<>(); + for (Map.Entry> entry : spoonTypes.entrySet()) { + String owner = entry.getKey(); + UnitContext context = contexts.get(owner); + Set dependencies = new TreeSet<>(); + Set cdTypes = new TreeSet<>(); + Set diagnostics = new TreeSet<>(); + + List> references = + new ArrayList<>( + entry.getValue().getElements(new TypeFilter<>(CtTypeReference.class))); + references.sort(Comparator.comparing(SpoonReferenceCodeDependencyAnalyzer::referenceKey)); + for (CtTypeReference reference : references) { + String referencedSimpleName = reference.getSimpleName(); + if (referenceCDTypeKeys.contains(referencedSimpleName)) { + cdTypes.add(referencedSimpleName); + } + Resolution resolution = + resolveSourceType(reference, context, contexts.keySet(), identitiesBySimpleName); + if (resolution.diagnostic().isPresent()) { + diagnostics.add( + "Unresolved or ambiguous source-local type reference '" + + referenceKey(reference) + + "' in '" + + owner + + "': " + + resolution.diagnostic().get()); + } + resolution + .identity() + .filter(identity -> !identity.equals(owner)) + .ifPresent( + identity -> { + Optional visibilityDiagnostic = + topLevelVisibilityDiagnostic(owner, identity, contexts, spoonTypes); + if (visibilityDiagnostic.isPresent()) { + diagnostics.add(visibilityDiagnostic.get()); + } else { + dependencies.add(identity); + } + }); + } + result.put( + owner, + new SourceTypeNode( + Collections.unmodifiableSet(dependencies), + Collections.unmodifiableSet(cdTypes), + List.copyOf(diagnostics))); + } + return Collections.unmodifiableMap(new LinkedHashMap<>(result)); + } + + private static Optional topLevelVisibilityDiagnostic( + String owner, + String dependency, + Map contexts, + Map> spoonTypes) { + UnitContext ownerContext = contexts.get(owner); + UnitContext dependencyContext = contexts.get(dependency); + CtType dependencyType = spoonTypes.get(dependency); + if (ownerContext == null || dependencyContext == null || dependencyType == null) { + return Optional.empty(); + } + if (ownerContext.packageName().equals(dependencyContext.packageName()) + || dependencyType.hasModifier(ModifierKind.PUBLIC)) { + return Optional.empty(); + } + return Optional.of( + "Source-local top-level type '" + + dependency + + "' referenced from '" + + owner + + "' is not accessible: it is package-private in package '" + + dependencyContext.packageName() + + "', but the referencing type is in package '" + + ownerContext.packageName() + + "'"); + } + + private static Resolution resolveSourceType( + CtTypeReference reference, + UnitContext context, + Set sourceIdentities, + Map> identitiesBySimpleName) { + String simpleName = reference.getSimpleName(); + + if (simpleName == null || simpleName.isBlank()) { + return Resolution.unresolved(); + } + + if (!isSimplyQualified(reference)) { + Optional declarationOwner = resolvedDeclarationOwner(reference, sourceIdentities); + if (declarationOwner.isPresent()) { + return Resolution.resolved(declarationOwner.get()); + } + Optional qualifiedOwner = + topLevelOwner(normalizedQualifiedName(reference), sourceIdentities); + return qualifiedOwner.map(Resolution::resolved).orElseGet(Resolution::unresolved); + } + + Set matchingExplicitImports = + context.explicitImports().stream() + .filter(imported -> simpleName(imported).equals(simpleName)) + .collect(java.util.stream.Collectors.toCollection(TreeSet::new)); + if (!matchingExplicitImports.isEmpty()) { + Set explicitSourceCandidates = new TreeSet<>(matchingExplicitImports); + explicitSourceCandidates.retainAll(sourceIdentities); + if (!explicitSourceCandidates.isEmpty()) { + return uniqueOrAmbiguous(explicitSourceCandidates); + } + // A single-type import is an established external binding. It takes precedence over a + // coincidental source-local declaration with the same simple name. + return Resolution.unresolved(); + } + + String samePackage = qualify(context.packageName(), simpleName); + if (sourceIdentities.contains(samePackage)) { + return Resolution.resolved(samePackage); + } + + Set wildcardCandidates = new TreeSet<>(); + context.wildcardImports().stream() + .map(packageName -> qualify(packageName, simpleName)) + .filter(sourceIdentities::contains) + .forEach(wildcardCandidates::add); + if (!wildcardCandidates.isEmpty()) { + return uniqueOrAmbiguous(wildcardCandidates); + } + + String qualifiedName = normalizedQualifiedName(reference); + if (isQualifiedReference(reference) + && !samePackage.equals(qualifiedName) + && topLevelOwner(qualifiedName, sourceIdentities).isEmpty()) { + // Spoon resolved an external/java.lang type. It is not a source dependency even when an + // unrelated source unit happens to declare the same simple name. + return Resolution.unresolved(); + } + + Set globalCandidates = identitiesBySimpleName.getOrDefault(simpleName, Set.of()); + if (globalCandidates.size() > 1) { + return Resolution.ambiguous(globalCandidates); + } + if (globalCandidates.size() == 1) { + return Resolution.unresolvedSourceCandidate(globalCandidates.iterator().next()); + } + return Resolution.unresolved(); + } + + private static Optional resolvedDeclarationOwner( + CtTypeReference reference, Set sourceIdentities) { + try { + CtType declaration = reference.getTypeDeclaration(); + if (declaration == null) { + return Optional.empty(); + } + CtType topLevel = declaration; + while (topLevel.getDeclaringType() != null) { + topLevel = topLevel.getDeclaringType(); + } + return sourceIdentities.contains(topLevel.getQualifiedName()) + ? Optional.of(topLevel.getQualifiedName()) + : Optional.empty(); + } catch (RuntimeException ignored) { + return Optional.empty(); + } + } + + private static Resolution uniqueOrAmbiguous(Set candidates) { + return candidates.size() == 1 + ? Resolution.resolved(candidates.iterator().next()) + : Resolution.ambiguous(candidates); + } + + private static Optional topLevelOwner( + String qualifiedName, Set sourceIdentities) { + if (qualifiedName == null || qualifiedName.isBlank()) { + return Optional.empty(); + } + String normalized = qualifiedName.replace('$', '.'); + return sourceIdentities.stream() + .filter(identity -> normalized.equals(identity) || normalized.startsWith(identity + ".")) + .max(Comparator.comparingInt(String::length)); + } + + private static boolean isQualifiedReference(CtTypeReference reference) { + String qualified = normalizedQualifiedName(reference); + return qualified != null + && qualified.contains(".") + && !qualified.equals(reference.getSimpleName()); + } + + private static boolean isSimplyQualified(CtTypeReference reference) { + try { + return reference.isSimplyQualified(); + } catch (RuntimeException ignored) { + return !isQualifiedReference(reference); + } + } + + private static String normalizedQualifiedName(CtTypeReference reference) { + try { + return reference.getQualifiedName(); + } catch (RuntimeException ignored) { + return reference.getSimpleName(); + } + } + + private static String referenceKey(CtTypeReference reference) { + String qualifiedName = normalizedQualifiedName(reference); + return qualifiedName == null ? String.valueOf(reference.getSimpleName()) : qualifiedName; + } + + private static Map sourceContexts( + Set units) { + Map result = new TreeMap<>(); + for (ASTOrdinaryCompilationUnit unit : units) { + String packageName = + unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + Set explicitImports = new TreeSet<>(); + Set wildcardImports = new TreeSet<>(); + for (ASTImportDeclaration importDeclaration : unit.getImportDeclarationList()) { + String imported = importDeclaration.getMCQualifiedName().getQName(); + if (importDeclaration.isSTAR()) { + wildcardImports.add(imported); + } else { + explicitImports.add(imported); + } + } + UnitContext context = + new UnitContext( + packageName, + Collections.unmodifiableSet(explicitImports), + Collections.unmodifiableSet(wildcardImports)); + for (ASTTypeDeclaration type : unit.getTypeDeclarationList()) { + String identity = qualify(packageName, type.getName()); + if (result.putIfAbsent(identity, context) != null) { + throw new CodeAdaptationException( + "Reference Java declares top-level type '" + identity + "' more than once"); + } + } + } + return Collections.unmodifiableMap(new LinkedHashMap<>(result)); + } + + private static Map> indexSimpleNames(Collection identities) { + Map> index = new TreeMap<>(); + for (String identity : identities) { + index.computeIfAbsent(simpleName(identity), ignored -> new TreeSet<>()).add(identity); + } + Map> result = new LinkedHashMap<>(); + index.forEach( + (name, values) -> + result.put(name, Collections.unmodifiableSet(new LinkedHashSet<>(values)))); + return Collections.unmodifiableMap(result); + } + + private static Set normalizeSimpleNames(Set names) { + Set result = new TreeSet<>(); + names.stream().map(SpoonReferenceCodeDependencyAnalyzer::simpleName).forEach(result::add); + return Collections.unmodifiableSet(result); + } + + private static String qualify(String packageName, String simpleName) { + return packageName == null || packageName.isBlank() + ? simpleName + : packageName + "." + simpleName; + } + + private static String simpleName(String qualifiedName) { + if (qualifiedName == null) { + return ""; + } + int separator = Math.max(qualifiedName.lastIndexOf('.'), qualifiedName.lastIndexOf('$')); + return separator < 0 ? qualifiedName : qualifiedName.substring(separator + 1); + } + + private record UnitContext( + String packageName, Set explicitImports, Set wildcardImports) {} + + private record SourceTypeNode( + Set dependencies, Set referenceCDTypeKeys, List diagnostics) {} + + private record Resolution(Optional identity, Optional diagnostic) { + private static Resolution resolved(String identity) { + return new Resolution(Optional.of(identity), Optional.empty()); + } + + private static Resolution ambiguous(Set candidates) { + return new Resolution(Optional.empty(), Optional.of("candidates are " + candidates)); + } + + private static Resolution unresolved() { + return new Resolution(Optional.empty(), Optional.empty()); + } + + private static Resolution unresolvedSourceCandidate(String candidate) { + return new Resolution( + Optional.empty(), Optional.of("candidate '" + candidate + "' is not imported or visible")); + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonTransformationService.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonTransformationService.java new file mode 100644 index 00000000..a9856edf --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonTransformationService.java @@ -0,0 +1,493 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.java.javadsl._ast.ASTFieldDeclaration; +import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.logging.Log; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import spoon.refactoring.CtRenameGenericVariableRefactoring; +import spoon.refactoring.Refactoring; +import spoon.reflect.code.CtFieldAccess; +import spoon.reflect.code.CtLocalVariable; +import spoon.reflect.code.CtThisAccess; +import spoon.reflect.declaration.CtField; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtParameter; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.CtVariable; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.filter.TypeFilter; + +/** + * Applies declaration, variable, grouping, and type-reference mutations to an already loaded Spoon + * model. + * + *

Creation/removal remains in {@link SpoonGenerationService}; executable overload and invocation + * repair remains in {@link SpoonExecutableRepairService}. Keeping these phases separate avoids one + * stateful catch-all service and makes their ordering explicit. + */ +final class SpoonTransformationService { + private final SpoonWorkspace workspace; + private final SpoonElementResolver resolver; + private final SpoonExecutableRepairService executableRepairs; + private Map groupingMappings = Collections.emptyMap(); + private Map>> typeReferencesBySimpleName; + private Map> typeDeclarationsBySimpleName; + + SpoonTransformationService( + SpoonWorkspace workspace, + SpoonElementResolver resolver, + SpoonExecutableRepairService executableRepairs) { + this.workspace = workspace; + this.resolver = resolver; + this.executableRepairs = executableRepairs; + } + + void reset() { + groupingMappings = Collections.emptyMap(); + typeReferencesBySimpleName = null; + typeDeclarationsBySimpleName = null; + } + + /** + * Sets incarnation-name to grouping-type-name replacements for this isolated Spoon pass. + * Qualified keys target only that qualified incarnation; simple keys are applied only when the + * loaded model contains one unambiguous declaration with that simple name. + */ + void setGroupingMappings(Map mappings) { + groupingMappings = + mappings == null || mappings.isEmpty() + ? Collections.emptyMap() + : new LinkedHashMap<>(mappings); + } + + void prepareForPrint() { + applyGroupingToModel(); + executableRepairs.prepareForPrint(groupingMappings); + } + + void updateType(ASTTypeDeclaration source, String newName) { + CtType target = resolver.getSpoonType(source); + if (!target.getSimpleName().equals(newName)) { + Refactoring.changeTypeName(target, newName); + } + } + + void updateMethod( + ASTTypeDeclaration sourceType, ASTMethodDeclaration sourceMethod, String newName) { + SpoonExecutableRepairService.MethodRename rename = + executableRepairs.planMethodRename(sourceType, sourceMethod); + Refactoring.changeMethodName(rename.target(), newName); + executableRepairs.renameInvocations(rename, newName); + } + + void updateField(ASTTypeDeclaration sourceType, ASTFieldDeclaration sourceField, String newName) { + String sourceName = sourceField.getVariableDeclarator(0).getDeclarator().getName(); + CtVariable field = resolver.getSpoonType(sourceType).getField(sourceName); + if (field == null) { + Log.warn( + "Cannot rename field '" + + sourceName + + "' in " + + sourceType.getName() + + " because no Spoon target exists; completed-member projection may add it later."); + return; + } + renameVariable(field, newName); + } + + void updateAssociationRole( + ASTTypeDeclaration sourceType, String sourceRole, String concreteRole) { + if (sourceRole == null || concreteRole == null || sourceRole.equals(concreteRole)) { + return; + } + CtType owner = resolver.getSpoonType(sourceType); + CtField ownerField = owner.getField(sourceRole); + for (CtFieldAccess access : owner.getElements(new TypeFilter<>(CtFieldAccess.class))) { + if (access.getParent(CtType.class) == owner + && access.getVariable() != null + && sourceRole.equals(access.getVariable().getSimpleName()) + && targetsOwnerField(access, owner, ownerField)) { + access.getVariable().setSimpleName(concreteRole); + } + } + } + + /** + * Returns whether a field access denotes the selected owner's role rather than an equally named + * field on another receiver. + */ + private static boolean targetsOwnerField( + CtFieldAccess access, CtType owner, CtField ownerField) { + if (access.getTarget() == null || access.getTarget() instanceof CtThisAccess) { + return true; + } + CtField declaration = access.getVariable().getDeclaration(); + if (declaration != null) { + return declaration == ownerField + || (declaration.getDeclaringType() != null + && declaration.getDeclaringType().getQualifiedName().equals(owner.getQualifiedName())); + } + CtTypeReference declaringType = access.getVariable().getDeclaringType(); + if (declaringType != null + && declaringType.getQualifiedName() != null + && !declaringType.getQualifiedName().isBlank()) { + return declaringType.getQualifiedName().equals(owner.getQualifiedName()); + } + // Do not apply a lexical-name fallback to another receiver: its equally named field may belong + // to an unrelated type. + return false; + } + + void updateSuperType(ASTTypeDeclaration type, ASTMCType supertype, String newName) { + String sourceName = JavaLoader.print(supertype); + CtType spoonType = resolver.getSpoonType(type); + CtTypeReference superclass = spoonType.getSuperclass(); + if (superclass != null && superclass.getSimpleName().equals(sourceName)) { + rewriteTypeReferenceName(superclass, newName); + return; + } + for (CtTypeReference superInterface : spoonType.getSuperInterfaces()) { + if (superInterface.getSimpleName().equals(sourceName)) { + rewriteTypeReferenceName(superInterface, newName); + } + } + } + + void updateLocalVariable( + ASTTypeDeclaration sourceType, + ASTMethodDeclaration sourceMethod, + ASTLocalVariableDeclaration sourceVariable, + String newName) { + CtMethod method = resolver.getSpoonMethod(sourceType, sourceMethod); + String sourceName = sourceVariable.getVariableDeclarator(0).getDeclarator().getName(); + List> matches = method.getElements(Objects::nonNull); + matches.removeIf(variable -> !sourceName.equals(variable.getSimpleName())); + CtLocalVariable target = + selectByPosition(matches, sourceVariable.get_SourcePositionStart().getLine(), sourceName); + renameVariable(target, newName); + } + + void updateMethodParameter( + ASTTypeDeclaration sourceType, + ASTMethodDeclaration sourceMethod, + ASTFormalParameter sourceParameter, + String newName) { + CtMethod method = resolver.getSpoonMethod(sourceType, sourceMethod); + String sourceName = sourceParameter.getDeclarator().getName(); + List> parameters = + method.getParameters().stream() + .filter(parameter -> sourceName.equals(parameter.getSimpleName())) + .collect(Collectors.toList()); + if (!parameters.isEmpty()) { + renameVariable(selectUnique(parameters, sourceName), newName); + return; + } + List> locals = method.getElements(Objects::nonNull); + locals.removeIf(variable -> !sourceName.equals(variable.getSimpleName())); + CtLocalVariable target = + selectByPosition(locals, sourceParameter.get_SourcePositionStart().getLine(), sourceName); + renameVariable(target, newName); + } + + void updateCDType(ASTCDType type, String newName) { + indexTypeReferences(); + List> candidates = + typeReferencesBySimpleName.getOrDefault(type.getName(), List.of()); + if (candidates.isEmpty()) { + return; + } + Set modelDeclarations = + typeDeclarationsBySimpleName.getOrDefault(type.getName(), Set.of()); + if (modelDeclarations.size() > 1) { + throw new IllegalStateException( + "Ambiguous source type '" + + type.getName() + + "' is declared by multiple adapter types " + + modelDeclarations); + } + String modelIdentity = + modelDeclarations.isEmpty() ? null : modelDeclarations.iterator().next(); + String modelPackage = + modelIdentity == null || !modelIdentity.contains(".") + ? "" + : modelIdentity.substring(0, modelIdentity.lastIndexOf('.')); + List> selected = + candidates.stream() + .filter( + reference -> { + Optional identity = resolvedTypeIdentity(reference); + if (modelIdentity != null) { + if (identity.isPresent()) { + return modelIdentity.equals(identity.get()); + } + CtType owner = reference.getParent(CtType.class); + String ownerPackage = + owner == null || owner.getPackage() == null + ? "" + : owner.getPackage().getQualifiedName(); + return modelPackage.equals(ownerPackage); + } + if (identity.isEmpty()) { + return true; + } + // In no-classpath mode Spoon still gives unresolved same-package references a + // qualified name (for example Concrete.Role). Those are adapter identities and + // must be rewritten. A genuinely resolved foreign type such as java.util.Date + // has a different package and must remain untouched. + CtType owner = reference.getParent(CtType.class); + String ownerPackage = + owner == null || owner.getPackage() == null + ? "" + : owner.getPackage().getQualifiedName(); + String expectedIdentity = + ownerPackage.isEmpty() + ? type.getName() + : ownerPackage + "." + type.getName(); + return expectedIdentity.equals(identity.get()); + }) + .toList(); + if (selected.isEmpty()) { + return; + } + if (modelIdentity == null) { + Set ownerPackages = + selected.stream() + .map(reference -> reference.getParent(CtType.class)) + .filter(Objects::nonNull) + .map(owner -> owner.getPackage() == null ? "" : owner.getPackage().getQualifiedName()) + .collect(Collectors.toSet()); + if (ownerPackages.size() > 1) { + throw new IllegalStateException( + "Ambiguous unresolved type '" + + type.getName() + + "' occurs in multiple packages " + + ownerPackages); + } + } + for (CtTypeReference reference : selected) { + rewriteTypeReferenceName(reference, newName); + } + } + + private void indexTypeReferences() { + if (typeReferencesBySimpleName != null) { + return; + } + typeReferencesBySimpleName = new LinkedHashMap<>(); + for (CtTypeReference reference : + workspace.model().getElements(new TypeFilter<>(CtTypeReference.class))) { + typeReferencesBySimpleName + .computeIfAbsent(reference.getSimpleName(), ignored -> new ArrayList<>()) + .add(reference); + } + typeDeclarationsBySimpleName = new LinkedHashMap<>(); + for (CtType declaration : workspace.model().getAllTypes()) { + typeDeclarationsBySimpleName + .computeIfAbsent( + declaration.getSimpleName(), ignored -> new java.util.LinkedHashSet<>()) + .add(declaration.getQualifiedName()); + } + } + + /** + * Rewrites uses of individual incarnation types to their configured common grouping types. + * + *

For {@code {CreditCard=PaymentMethod, Invoice=PaymentMethod}}, a shared {@code + * List} may become {@code List}. The concrete incarnation declarations + * themselves are not renamed, builder declarations retain their concrete types, and a method + * parameter is left unchanged when grouping it would collapse two overloads to the same Java + * signature. + */ + private void applyGroupingToModel() { + if (groupingMappings.isEmpty()) { + return; + } + Set> concreteDeclarations = + workspace.model().getAllTypes().stream() + .filter(type -> mappingFor(type.getReference()) != null) + .collect(Collectors.toCollection(HashSet::new)); + for (CtTypeReference reference : + workspace.model().getElements(new TypeFilter<>(CtTypeReference.class))) { + String replacement = mappingFor(reference); + if (replacement == null) { + continue; + } + CtType owner = reference.getParent(CtType.class); + if (owner != null + && (concreteDeclarations.contains(owner) + || (owner.getSimpleName() != null && owner.getSimpleName().endsWith("Builder")))) { + continue; + } + if (wouldCollapseOverload(reference)) { + continue; + } + if (!replacement.equals(reference.getSimpleName())) { + rewriteTypeReferenceName(reference, replacement); + } + } + } + + /** + * Keeps concrete parameter types when replacing them by a grouping type would erase overload + * identity. This is required for overload sets introduced by CD completion, such as Visitor + * methods for several concrete node incarnations. + */ + private boolean wouldCollapseOverload(CtTypeReference reference) { + CtParameter parameter = reference.getParent(CtParameter.class); + if (parameter == null || parameter.getType() != reference) { + return false; + } + CtMethod method = parameter.getParent(CtMethod.class); + CtType owner = method == null ? null : method.getParent(CtType.class); + if (owner == null) { + return false; + } + String groupedSignature = groupedSignature(method); + return owner.getMethods().stream() + .filter(candidate -> candidate != method) + .anyMatch(candidate -> groupedSignature.equals(groupedSignature(candidate))); + } + + private String groupedSignature(CtMethod method) { + return method.getSimpleName() + + "(" + + method.getParameters().stream() + .map(CtParameter::getType) + .map( + type -> { + String grouping = mappingFor(type); + return grouping == null + ? type.getSimpleName() + : JavaSourceNames.simpleName(grouping); + }) + .collect(Collectors.joining(",")) + + ")"; + } + + /** + * Resolves the grouping replacement applicable to one Spoon type reference. + * + *

A qualified map key must match the reference's qualified identity. A simple key is accepted + * only when exactly one loaded declaration has that simple name and the reference resolves to it + * (or, when unresolved, is used from the same package). This prevents a mapping for {@code + * billing.Invoice} from rewriting an unrelated {@code shipping.Invoice}. + */ + private String mappingFor(CtTypeReference reference) { + String qualifiedName = reference.getQualifiedName(); + if (qualifiedName != null && groupingMappings.containsKey(qualifiedName)) { + return groupingMappings.get(qualifiedName); + } + String simpleName = reference.getSimpleName(); + if (!groupingMappings.containsKey(simpleName)) { + return null; + } + Set qualifiedKeys = + groupingMappings.keySet().stream() + .filter(key -> key.endsWith("." + simpleName)) + .collect(Collectors.toSet()); + if (!qualifiedKeys.isEmpty()) { + return qualifiedKeys.contains(qualifiedName) ? groupingMappings.get(qualifiedName) : null; + } + List> declarations = + workspace.model().getAllTypes().stream() + .filter(type -> simpleName.equals(type.getSimpleName())) + .toList(); + if (declarations.size() != 1) { + return null; + } + CtType declaration = declarations.get(0); + Optional identity = resolvedTypeIdentity(reference); + if (identity.isPresent() && !declaration.getQualifiedName().equals(identity.get())) { + return null; + } + if (identity.isEmpty()) { + CtType owner = reference.getParent(CtType.class); + String ownerPackage = + owner == null || owner.getPackage() == null ? "" : owner.getPackage().getQualifiedName(); + String declarationPackage = + declaration.getPackage() == null ? "" : declaration.getPackage().getQualifiedName(); + if (!ownerPackage.equals(declarationPackage)) { + return null; + } + } + return groupingMappings.get(simpleName); + } + + /** Rewrites a type reference while retaining valid package identity for simple replacements. */ + private void rewriteTypeReferenceName(CtTypeReference reference, String newName) { + if (reference == null || newName == null || newName.isBlank()) { + throw new IllegalArgumentException("Type reference and new name must be present"); + } + String simpleName = JavaSourceNames.simpleName(newName); + if (newName.contains(".")) { + CtTypeReference replacement = workspace.createTypeReference(newName); + reference.setPackage(replacement.getPackage()); + reference.setDeclaringType(replacement.getDeclaringType()); + } + // For a simple replacement, retain the original package identity. Turning entity.User into + // an unresolved simple Professor produces an invalid import that cleanup can only remove. + reference.setSimpleName(simpleName); + reference.setSimplyQualified(true); + } + + private static Optional resolvedTypeIdentity(CtTypeReference reference) { + try { + CtType declaration = reference.getTypeDeclaration(); + if (declaration != null && declaration.getQualifiedName() != null) { + return Optional.of(declaration.getQualifiedName()); + } + } catch (RuntimeException ignored) { + // no-classpath references often have no declaration; retain qualified metadata below. + } + String qualifiedName = reference.getQualifiedName(); + if (qualifiedName == null + || qualifiedName.isBlank() + || qualifiedName.equals(reference.getSimpleName())) { + return Optional.empty(); + } + return Optional.of(qualifiedName); + } + + private static T selectUnique(List matches, String name) { + if (matches.size() != 1) { + throw new IllegalStateException( + "Expected exactly one Spoon declaration named '" + name + "' but found " + matches.size()); + } + return matches.get(0); + } + + private static > T selectByPosition( + List matches, int sourceLine, String name) { + if (matches.size() == 1) { + return matches.get(0); + } + List sameLine = + matches.stream() + .filter(variable -> variable.getPosition().isValidPosition()) + .filter(variable -> variable.getPosition().getLine() == sourceLine) + .collect(Collectors.toList()); + return selectUnique(sameLine, name + " at line " + sourceLine); + } + + private static void renameVariable(CtVariable variable, String newName) { + CtRenameGenericVariableRefactoring refactoring = new CtRenameGenericVariableRefactoring(); + refactoring.setTarget(variable).setNewName(newName).refactor(); + } + +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonUpdater.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonUpdater.java index 9f26c4b5..0031e618 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonUpdater.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonUpdater.java @@ -1,272 +1,224 @@ package de.monticore.codeAdaption.updater.spoonUpdater; -import static de.monticore.codeAdaption.utils.JavaLoader.print; - import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; import de.monticore.codeAdaption.updater.CodeUpdater; -import de.monticore.codeAdaption.utils.JavaLoader; import de.monticore.java.javadsl._ast.ASTFieldDeclaration; +import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; import de.monticore.java.javadsl._ast.ASTTypeDeclaration; import de.monticore.javalight._ast.ASTMethodDeclaration; import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; -import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; import de.monticore.types.mcbasictypes._ast.ASTMCType; import java.io.File; import java.nio.file.Path; -import java.util.*; -import spoon.Launcher; -import spoon.refactoring.CtRenameGenericVariableRefactoring; -import spoon.refactoring.Refactoring; -import spoon.reflect.CtModel; -import spoon.reflect.code.CtLocalVariable; -import spoon.reflect.declaration.*; -import spoon.reflect.reference.CtTypeReference; +import java.util.List; +import java.util.Map; +import java.util.Set; +import spoon.reflect.declaration.CtType; +/** Spoon-backed facade for loading, transforming, generating and printing Java source code. */ public class SpoonUpdater implements CodeUpdater { - private File outputDir; - private Launcher launcher; - private CtModel spoonModel; - private final Map> typeMap = new LinkedHashMap<>(); - private final Map> methodMap = new LinkedHashMap<>(); + private final SpoonWorkspace workspace; + private final SpoonElementResolver elementResolver; + private final SpoonExecutableRepairService executableRepairs; + private final SpoonTransformationService transformations; + private final SpoonGenerationService generation; + + public SpoonUpdater() { + workspace = new SpoonWorkspace(); + elementResolver = new SpoonElementResolver(workspace::model); + executableRepairs = new SpoonExecutableRepairService(workspace, elementResolver); + transformations = + new SpoonTransformationService(workspace, elementResolver, executableRepairs); + generation = new SpoonGenerationService(workspace, elementResolver); + } @Override public void setCodePath(Path path) { - // init spoon environment - launcher = new Launcher(); - launcher.getEnvironment().setAutoImports(true); - launcher.getEnvironment().setShouldCompile(true); - - // add code to the environment - launcher.addInputResource(path.toAbsolutePath().toString()); - - // build model - launcher.buildModel(); - spoonModel = launcher.getModel(); + workspace.load(path); + elementResolver.reset(); + transformations.reset(); + executableRepairs.reset(); } @Override public Set printCode() { - launcher.setSourceOutputDirectory(outputDir); - launcher.prettyprint(); - return JavaLoader.readJavaFile(outputDir.toPath()); + transformations.prepareForPrint(); + return workspace.print(); } @Override - public void updateType(ASTTypeDeclaration source, String newName) { - CtType type = getSpoonType(source); - Refactoring.changeTypeName(type, newName); + public void cleanCode(Path codePath) { + workspace.clean(codePath); } @Override - public void updateMethod( - ASTTypeDeclaration srcType, ASTMethodDeclaration srcMethod, String newName) { - CtMethod method = getSpoonMethod(srcType, srcMethod); - Refactoring.changeMethodName(method, newName); + public void cleanCode(Path codePath, Map topToPublicSelfTypes) { + workspace.clean(codePath, topToPublicSelfTypes); } @Override - public void updateField( - ASTTypeDeclaration srcType, ASTFieldDeclaration srcField, String newName) { + public void setGroupingMappings(Map mappings) { + transformations.setGroupingMappings(mappings); + } - // get Spoon Variable - String srcName = srcField.getVariableDeclarator(0).getDeclarator().getName(); - CtVariable attribute = getSpoonType(srcType).getField(srcName); + @Override + public void registerConcreteMethodSignature(String methodName, List parameterTypes) { + executableRepairs.registerConcreteMethodSignature(methodName, parameterTypes); + } - // perform update - CtRenameGenericVariableRefactoring refactor = new CtRenameGenericVariableRefactoring(); - refactor.setTarget(attribute).setNewName(newName).refactor(); + @Override + public void registerMethodRewrite( + StableElementKey referenceMethod, StableElementKey concreteMethod) { + executableRepairs.registerMethodRewrite(referenceMethod, concreteMethod); } @Override - public void updateSuperType(ASTTypeDeclaration type, ASTMCType supertype, String newName) { - String srcName = JavaLoader.print(supertype); - CtType spoonType = getSpoonType(type); + public void registerMethodRewrite( + ASTTypeDeclaration sourceOwner, + StableElementKey referenceMethod, + StableElementKey concreteMethod) { + CtType sourceType = elementResolver.getSpoonType(sourceOwner); + String referenceOwnerIdentity = sourceType.getQualifiedName(); + String concreteOwnerIdentity = concreteOwnerIdentity(sourceType, concreteMethod); + executableRepairs.registerMethodRewrite( + referenceMethod.withOwnerType(referenceOwnerIdentity), + concreteMethod.withOwnerType(concreteOwnerIdentity)); + } - // case super class - CtTypeReference superType = spoonType.getSuperclass(); - if (superType != null && superType.getSimpleName().equals(srcName)) { - spoonType.getSuperclass().setSimpleName(newName); - return; + private static String concreteOwnerIdentity( + CtType sourceType, StableElementKey concreteMethod) { + String requestedOwner = concreteMethod.getOwnerType().orElse(sourceType.getSimpleName()); + if (requestedOwner.contains(".") || requestedOwner.contains("$")) { + return requestedOwner; } - - // case super interface - for (CtTypeReference superType2 : spoonType.getSuperInterfaces()) { - if (superType2.getSimpleName().equals(srcName)) { - superType2.setSimpleName(newName); - } + if (sourceType.getDeclaringType() != null) { + return sourceType.getDeclaringType().getQualifiedName() + "$" + requestedOwner; } + String packageName = + sourceType.getPackage() == null ? "" : sourceType.getPackage().getQualifiedName(); + return packageName.isEmpty() ? requestedOwner : packageName + "." + requestedOwner; } @Override - public void updateLocalVariable( - ASTTypeDeclaration srcType, - ASTMethodDeclaration srcMethod, - ASTLocalVariableDeclaration sourceVar, - String newName) { - - // get spoon local-variable - CtMethod spoonMethod = getSpoonMethod(srcType, srcMethod); - List> localVars = spoonMethod.getElements(Objects::nonNull); - String varName = sourceVar.getVariableDeclarator(0).getDeclarator().getName(); - Optional> var = - localVars.stream().filter(v -> v.getSimpleName().equals(varName)).findAny(); - assert var.isPresent(); - - // perform update - CtRenameGenericVariableRefactoring refactor = new CtRenameGenericVariableRefactoring(); - refactor.setTarget(var.get()).setNewName(newName).refactor(); + public void updateType(ASTTypeDeclaration source, String newName) { + transformations.updateType(source, newName); } @Override - public void updateMethodParameter( - ASTTypeDeclaration srcType, - ASTMethodDeclaration srcMethod, - ASTFormalParameter srcParam, - String newName) { - - // get spoon formal parameter of method - CtMethod spoonMethod = getSpoonMethod(srcType, srcMethod); - List> params = spoonMethod.getElements(Objects::nonNull); - String srcVarName = srcParam.getDeclarator().getName(); - Optional> param = - params.stream().filter(v -> v.getSimpleName().equals(srcVarName)).findAny(); - - if (param.isPresent()) { - // perform update - CtRenameGenericVariableRefactoring refactor = new CtRenameGenericVariableRefactoring(); - refactor.setTarget(param.get()).setNewName(newName).refactor(); + public void updateMethod( + ASTTypeDeclaration sourceType, ASTMethodDeclaration sourceMethod, String newName) { + transformations.updateMethod(sourceType, sourceMethod, newName); + } - } else { + @Override + public void updateField( + ASTTypeDeclaration sourceType, ASTFieldDeclaration sourceField, String newName) { + transformations.updateField(sourceType, sourceField, newName); + } - // case formal param in for loop - List> localVars = spoonMethod.getElements(Objects::nonNull); - Optional> localvar = - localVars.stream().filter(v -> v.getSimpleName().equals(srcVarName)).findAny(); - assert localvar.isPresent(); + @Override + public void updateAssociationRole( + ASTTypeDeclaration sourceType, String sourceRole, String concreteRole) { + transformations.updateAssociationRole(sourceType, sourceRole, concreteRole); + } - // case formal param in for loop - CtRenameGenericVariableRefactoring refactor = new CtRenameGenericVariableRefactoring(); - refactor.setTarget(localvar.get()).setNewName(newName).refactor(); - } + @Override + public void updateSuperType(ASTTypeDeclaration type, ASTMCType supertype, String newName) { + transformations.updateSuperType(type, supertype, newName); } @Override - public void updateCDType(ASTCDType cdType, String newName) { - List> refTypes = spoonModel.getElements(Objects::nonNull); + public void updateLocalVariable( + ASTTypeDeclaration sourceType, + ASTMethodDeclaration sourceMethod, + ASTLocalVariableDeclaration sourceVariable, + String newName) { + transformations.updateLocalVariable( + sourceType, sourceMethod, sourceVariable, newName); + } - for (CtTypeReference typeRef : refTypes) { - if (typeRef.getSimpleName().equals(cdType.getName())) { - typeRef.setSimpleName(newName); - } - } + @Override + public void updateMethodParameter( + ASTTypeDeclaration sourceType, + ASTMethodDeclaration sourceMethod, + ASTFormalParameter sourceParameter, + String newName) { + transformations.updateMethodParameter( + sourceType, sourceMethod, sourceParameter, newName); + } - // for () + @Override + public void updateCDType(ASTCDType type, String newName) { + transformations.updateCDType(type, newName); } @Override public void setOutputDirectory(Path outputPath) { - this.outputDir = outputPath.toFile(); + workspace.setOutputDirectory(outputPath); } - /*** - * Retrieves the spoonType from the Spoon Model based on the provided mcType. - * Saves the found spoonType in the type map. - * - * @param mcType The ASTTypeDeclaration representing the type to be searched - * for in the Spoon model. - * @return The corresponding CtType found in the Spoon model for the - * given mcType. - * @throws AssertionError if no matching CtType is found in the Spoon - * model (assert will fail). - */ - private CtType getSpoonType(ASTTypeDeclaration mcType) { - // cas already found - if (typeMap.containsKey(mcType)) { - return typeMap.get(mcType); - } - // search in the spoon model - Optional> type = - spoonModel.getAllTypes().stream().filter(t -> compare(mcType, t)).findFirst(); - assert type.isPresent(); - typeMap.put(mcType, type.get()); - return type.get(); + @Override + public void addField( + ASTTypeDeclaration targetType, + ASTFieldDeclaration templateField, + String newName, + String newType, + boolean isStatic) { + generation.addField(targetType, templateField, newName, newType, isStatic); } - /*** - * Retrieves the spoonMethod from the Spoon Model based on the provided - * mcType and mcMethod. Saves the found spoonMethod in the method map. - * - * @param mcType The ASTTypeDeclaration representing the type to which - * the method belongs. - * @param mcMethod The ASTMethodDeclaration representing the method to be - * searched for in the Spoon model. - * @return The corresponding CtMethod found in the Spoon model for the - * given mcType and mcMethod. - * @throws AssertionError if no matching CtMethod is found in the - * Spoon model (assert will fail). - */ - public CtMethod getSpoonMethod(ASTTypeDeclaration mcType, ASTMethodDeclaration mcMethod) { - // cas method was already found - if (methodMap.containsKey(mcMethod)) { - return methodMap.get(mcMethod); - } + @Override + public void addType(ASTTypeDeclaration templateType, String newName) { + generation.addType(templateType, newName); + } - // search method in the spoonType - CtType spoonType = getSpoonType(mcType); - Optional> method = - spoonType.getAllMethods().stream() - .filter(spMethod -> compare(mcMethod, spMethod)) - .findFirst(); + @Override + public void addSuperType( + ASTTypeDeclaration targetType, String superTypeName, boolean interfaceType) { + generation.addSuperType(targetType, superTypeName, interfaceType); + } - assert method.isPresent(); - methodMap.put(mcMethod, method.get()); - return method.get(); + @Override + public void setTypeAbstract(ASTTypeDeclaration targetType, boolean isAbstract) { + generation.setTypeAbstract(targetType, isAbstract); } - /** - * Compares a mcType and spoonType and returns true if both are identical. - * - * @param type The ASTTypeDeclaration representing the type to be compared. - * @param spoonType The CtType representing the spoon type to be compared. - * @return True if the file name of the mcType ends with the simple name of the spoonType followed - * by ".java"; otherwise false. - */ - protected boolean compare(ASTTypeDeclaration type, CtType spoonType) { - String fileName = type.get_SourcePositionStart().getFileName().orElse(type.getName()); - return fileName.replaceAll("\\\\", ".").endsWith(spoonType.getSimpleName() + ".java"); + @Override + public void addEnumConstant( + ASTTypeDeclaration targetType, String constantName, int expectedIndex) { + generation.addEnumConstant(targetType, constantName, expectedIndex); } - /** - * Compares a spoonMethod and mcMethod and returns true if both are identical. - * - * @param mcMethod The ASTMethodDeclaration representing the method to be compared. - * @param spoonMethod The CtMethod representing the spoon method to be compared. - * @return True if the names, parameter count, and parameter types of both methods match; - * otherwise false. - */ - protected boolean compare(ASTMethodDeclaration mcMethod, CtMethod spoonMethod) { - // compare names - if (!mcMethod.getName().endsWith(spoonMethod.getSimpleName())) { - return false; - } - // is present parameters ? - if (!mcMethod.getFormalParameters().isPresentFormalParameterListing()) { - return spoonMethod.getParameters().isEmpty(); - } - // same number of parameters ? - List mcParams = - mcMethod.getFormalParameters().getFormalParameterListing().getFormalParameterList(); - if (spoonMethod.getParameters().size() != mcParams.size()) { - return false; - } - // parameters have the same type ? - for (int i = 0; i < spoonMethod.getParameters().size(); i++) { - if (!(spoonMethod.getParameters().get(i).getType().getSimpleName()) - .equals(print(mcParams.get(i).getMCType()))) { - return false; - } - } + @Override + public void addMethod( + ASTTypeDeclaration targetType, + ASTMethodDeclaration templateMethod, + String newName, + List parameterTypes, + List parameterNames, + String returnType, + boolean isStatic, + MethodBodySpec methodBody) { + generation.addMethod( + targetType, + templateMethod, + newName, + parameterTypes, + parameterNames, + returnType, + isStatic, + methodBody); + } + + @Override + public void removeField(ASTTypeDeclaration targetType, ASTFieldDeclaration field) { + generation.removeField(targetType, field); + } - return true; + @Override + public void removeMethod(ASTTypeDeclaration targetType, ASTMethodDeclaration method) { + generation.removeMethod(targetType, method); } + } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonWorkspace.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonWorkspace.java new file mode 100644 index 00000000..9d235ce7 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonWorkspace.java @@ -0,0 +1,499 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import de.monticore.codeAdaption.utils.AdaptAnnotationNames; +import de.monticore.codeAdaption.utils.Constants; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.codeAdaption.utils.JavaSourcePostProcessor; +import de.se_rwth.commons.logging.Log; +import java.io.File; +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.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.io.FileUtils; +import spoon.Launcher; +import spoon.reflect.CtModel; +import spoon.reflect.code.CtExpression; +import spoon.reflect.code.CtTargetedExpression; +import spoon.reflect.code.CtThisAccess; +import spoon.reflect.declaration.CtAnnotation; +import spoon.reflect.declaration.CtField; +import spoon.reflect.declaration.CtMethod; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.ModifierKind; +import spoon.reflect.factory.Factory; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.filter.TypeFilter; +import spoon.support.compiler.VirtualFile; + +/** Owns the Spoon model, its factory and the generated-source filesystem lifecycle. */ +final class SpoonWorkspace { + private static final Pattern SOURCE_ANNOTATION_NAME = + Pattern.compile("^\\s*@\\s*([A-Za-z_$][\\w$]*(?:\\s*\\.\\s*[A-Za-z_$][\\w$]*)*)"); + private final Map>> parsedTypePrototypes = + new LinkedHashMap<>(); + private File outputDirectory; + private Launcher launcher; + private CtModel model; + + void load(Path codePath) { + try { + JavaSourcePostProcessor.processDirectory(codePath); + } catch (IOException e) { + throw new IllegalStateException( + "SpoonUpdater.setCodePath: Failed to prepare source code at '" + codePath + "'", e); + } + launcher = new Launcher(); + launcher.getEnvironment().setAutoImports(true); + launcher.getEnvironment().setNoClasspath(true); + launcher.addInputResource(codePath.toAbsolutePath().toString()); + launcher.buildModel(); + model = launcher.getModel(); + parsedTypePrototypes.clear(); + } + + CtModel model() { + if (model == null) { + throw new IllegalStateException("No Spoon model loaded; call setCodePath first"); + } + return model; + } + + Factory factory() { + if (launcher == null) { + throw new IllegalStateException("No Spoon workspace loaded; call setCodePath first"); + } + return launcher.getFactory(); + } + + void setOutputDirectory(Path outputPath) { + if (outputPath == null) { + throw new IllegalArgumentException("Output path must not be null"); + } + outputDirectory = outputPath.toFile(); + } + + Set print() { + if (outputDirectory == null) { + throw new IllegalStateException("No output directory configured"); + } + Path outputPath = outputDirectory.toPath(); + try { + if (Files.isDirectory(outputPath)) { + try (var paths = Files.walk(outputPath)) { + for (Path source : + paths.filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .toList()) { + Files.delete(source); + } + } + } + } catch (IOException exception) { + throw new IllegalStateException( + "Failed to clear stale Java sources before printing to " + outputPath, exception); + } + launcher.setSourceOutputDirectory(outputDirectory); + launcher.prettyprint(); + return JavaLoader.readJavaFile(outputPath); + } + + CtExpression defaultExpression(String typeName) { + String normalized = typeName == null ? "" : JavaSourceNames.normalizeType(typeName.trim()); + return switch (normalized) { + case "boolean" -> factory().Code().createLiteral(false); + case "byte" -> factory().Code().createLiteral((byte) 0); + case "short" -> factory().Code().createLiteral((short) 0); + case "int" -> factory().Code().createLiteral(0); + case "long" -> factory().Code().createLiteral(0L); + case "float" -> factory().Code().createLiteral(0.0f); + case "double" -> factory().Code().createLiteral(0.0d); + case "char" -> factory().Code().createLiteral('\0'); + default -> factory().Code().createLiteral(null); + }; + } + + CtTypeReference createTypeReference(String typeName) { + if (typeName == null || typeName.isBlank()) { + return factory().Type().createReference(Object.class); + } + String original = typeName.trim(); + Optional> parsed = parseTypePrototype(original); + if (parsed.isPresent()) { + return parsed.get().clone(); + } + String normalized = JavaSourceNames.normalizeType(original); + CtTypeReference primitive = primitiveTypeReference(normalized); + if (primitive != null) { + return primitive; + } + parsed = parseTypePrototype(normalized); + if (parsed.isPresent()) { + return parsed.get().clone(); + } + try { + return factory().Type().createReference(normalized); + } catch (RuntimeException ignored) { + return unresolvedTypeReference(normalized); + } + } + + void clean(Path codePath) { + clean(codePath, Map.of()); + } + + void clean(Path codePath, Map topToPublicSelfTypes) { + Path sourcePath = codePath.toAbsolutePath().normalize(); + if (!Files.isDirectory(sourcePath)) { + throw new IllegalArgumentException("Code path is not a directory: " + sourcePath); + } + Path parent = sourcePath.getParent(); + if (parent == null) { + throw new IllegalArgumentException("Cannot transactionally clean a filesystem root"); + } + Path transactionRoot = null; + boolean preserveTransaction = false; + try { + transactionRoot = + Files.createTempDirectory(parent, sourcePath.getFileName() + "_cleanup_"); + Path formattedPath = transactionRoot.resolve("formatted"); + Path stagedPath = transactionRoot.resolve("staged"); + Files.createDirectories(formattedPath); + FileUtils.copyDirectory(sourcePath.toFile(), stagedPath.toFile()); + Map originals = javaFilesByRelativePath(sourcePath); + Launcher cleanupLauncher = cleanupLauncher(sourcePath); + repairTopSelfReferences(cleanupLauncher, topToPublicSelfTypes); + List> annotations = + new ArrayList<>(cleanupLauncher.getModel().getElements(new TypeFilter<>(CtAnnotation.class))); + cleanAdaptAnnotations(cleanupLauncher, annotations); + for (CtType type : cleanupLauncher.getModel().getAllTypes()) { + if (!type.isInterface()) { + continue; + } + for (CtMethod method : type.getMethods()) { + if (!method.isDefaultMethod() + && !method.hasModifier(ModifierKind.STATIC) + && !method.hasModifier(ModifierKind.PRIVATE)) { + method.setBody(null); + } + } + } + cleanupLauncher.setSourceOutputDirectory(formattedPath.toFile()); + cleanupLauncher.prettyprint(); + Set copiedTargets = + copyFormattedFiles(formattedPath, stagedPath, sourcePath, originals); + deleteRehomedOriginals(sourcePath, stagedPath, originals.values(), copiedTargets); + JavaSourcePostProcessor.processDirectory(stagedPath); + commitStagedDirectory(sourcePath, stagedPath, transactionRoot.resolve("backup")); + Log.info("SpoonUpdater.cleanCode: Cleanup completed", "CodeAdapter"); + } catch (IOException | RuntimeException e) { + if (transactionRoot != null && Files.exists(transactionRoot.resolve("backup"))) { + preserveTransaction = true; + } + throw new IllegalStateException( + "SpoonUpdater.cleanCode: Failed to clean code at '" + sourcePath + "'", e); + } finally { + if (transactionRoot != null && !preserveTransaction) { + FileUtils.deleteQuietly(transactionRoot.toFile()); + } + } + } + + /** + * Preserves the public self type when an adapted implementation is moved from {@code X} to + * {@code XTOP}. Explicit {@code this} values become {@code (X) this}; member-access targets such + * as {@code this.field} stay untouched so field lookup remains anchored in the TOP class. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + private static void repairTopSelfReferences( + Launcher cleanupLauncher, Map topToPublicSelfTypes) { + if (topToPublicSelfTypes == null || topToPublicSelfTypes.isEmpty()) { + return; + } + Map> typesByIdentity = new LinkedHashMap<>(); + cleanupLauncher + .getModel() + .getAllTypes() + .forEach(type -> typesByIdentity.put(type.getQualifiedName(), type)); + + for (Map.Entry binding : topToPublicSelfTypes.entrySet()) { + CtType topType = typesByIdentity.get(binding.getKey()); + CtType publicType = typesByIdentity.get(binding.getValue()); + if (topType == null || publicType == null) { + throw new IllegalStateException( + "Cannot repair TOP self type '" + + binding.getKey() + + "' -> '" + + binding.getValue() + + "' because one declaration is missing"); + } + CtTypeReference publicReference = publicType.getReference(); + for (CtThisAccess thisAccess : + topType.getElements(new TypeFilter<>(CtThisAccess.class))) { + if (thisAccess.isImplicit() || thisAccess.getParent(CtType.class) != topType) { + continue; + } + // Do not turn this.field into ((X) this).field: that could select a hidden HWC field. + if (thisAccess.getParent() instanceof CtTargetedExpression targeted + && targeted.getTarget() == thisAccess) { + continue; + } + boolean alreadyRepaired = + thisAccess.getTypeCasts().stream() + .anyMatch(cast -> cast.getQualifiedName().equals(publicReference.getQualifiedName())); + if (!alreadyRepaired) { + thisAccess.addTypeCast(publicReference.clone()); + } + } + } + } + + private Optional> parseTypePrototype(String typeName) { + return parsedTypePrototypes.computeIfAbsent(typeName, this::parseTypeReference); + } + + private Optional> parseTypeReference(String typeName) { + try { + Launcher parser = new Launcher(); + parser.getEnvironment().setNoClasspath(true); + parser.addInputResource( + new VirtualFile("class __TypeProbe { " + typeName + " value; }", "__TypeProbe.java")); + parser.buildModel(); + return parser.getModel().getElements(new TypeFilter<>(CtField.class)).stream() + .findFirst() + .map(field -> field.getType().clone()); + } catch (RuntimeException ignored) { + return Optional.empty(); + } + } + + private CtTypeReference primitiveTypeReference(String typeName) { + return switch (typeName) { + case "void" -> factory().Type().createReference(void.class); + case "int" -> factory().Type().createReference(int.class); + case "long" -> factory().Type().createReference(long.class); + case "double" -> factory().Type().createReference(double.class); + case "float" -> factory().Type().createReference(float.class); + case "boolean" -> factory().Type().createReference(boolean.class); + case "char" -> factory().Type().createReference(char.class); + case "short" -> factory().Type().createReference(short.class); + case "byte" -> factory().Type().createReference(byte.class); + default -> null; + }; + } + + private CtTypeReference unresolvedTypeReference(String typeName) { + CtTypeReference reference = factory().Core().createTypeReference(); + String simpleName = JavaSourceNames.simpleName(typeName); + reference.setSimpleName(simpleName.isBlank() ? "Object" : simpleName); + reference.setPackage(null); + reference.setDeclaringType(null); + reference.setSimplyQualified(true); + return reference; + } + + private static Launcher cleanupLauncher(Path codePath) throws IOException { + Launcher cleanupLauncher = new Launcher(); + cleanupLauncher.getEnvironment().setAutoImports(true); + cleanupLauncher.getEnvironment().setNoClasspath(true); + try (var paths = Files.walk(codePath)) { + paths.filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .forEach(path -> cleanupLauncher.addInputResource(path.toAbsolutePath().toString())); + } + cleanupLauncher.buildModel(); + return cleanupLauncher; + } + + private static Map javaFilesByRelativePath(Path codePath) throws IOException { + Map result = new LinkedHashMap<>(); + try (var paths = Files.walk(codePath)) { + paths.filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .forEach(path -> result.put(codePath.relativize(path), path)); + } + return result; + } + + private static Set copyFormattedFiles( + Path formattedPath, Path stagedPath, Path sourcePath, Map originals) + throws IOException { + Set copiedTargets = new LinkedHashSet<>(); + try (var paths = Files.walk(formattedPath)) { + paths.filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .forEach( + source -> { + try { + Path relative = formattedPath.relativize(source); + Path originalTarget = originals.getOrDefault(relative, sourcePath.resolve(relative)); + Path target = stagedPath.resolve(sourcePath.relativize(originalTarget)); + Files.createDirectories(target.getParent()); + copyReplacingWithRetry(source, target); + if (!copiedTargets.add(target.toAbsolutePath().normalize())) { + throw new IllegalStateException( + "Multiple formatted sources target the same file '" + target + "'"); + } + } catch (IOException e) { + throw new IllegalStateException("Failed to copy formatted file '" + source + "'", e); + } + }); + } + return copiedTargets; + } + + private static void deleteRehomedOriginals( + Path sourcePath, + Path stagedPath, + Collection originals, + Set copiedTargets) + throws IOException { + for (Path original : originals) { + Path stagedOriginal = stagedPath.resolve(sourcePath.relativize(original)); + if (!copiedTargets.contains(stagedOriginal.toAbsolutePath().normalize()) + && Files.exists(stagedOriginal)) { + Files.delete(stagedOriginal); + } + } + } + + private static void commitStagedDirectory(Path source, Path staged, Path backup) + throws IOException { + moveDirectory(source, backup); + try { + moveDirectory(staged, source); + } catch (IOException commitFailure) { + try { + moveDirectory(backup, source); + } catch (IOException rollbackFailure) { + commitFailure.addSuppressed(rollbackFailure); + } + throw commitFailure; + } + FileUtils.deleteDirectory(backup.toFile()); + } + + private static void moveDirectory(Path source, Path target) throws IOException { + IOException lastException = null; + for (int attempt = 0; attempt < 5; attempt++) { + try { + try { + Files.move(source, target, StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException ignored) { + Files.move(source, target); + } + return; + } catch (IOException exception) { + // A successful rename must not be retried merely because the provider reported late. + if (!Files.exists(source) && Files.exists(target)) { + return; + } + lastException = exception; + try { + Thread.sleep(50L * (attempt + 1)); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw exception; + } + } + } + throw lastException; + } + + private static void copyReplacingWithRetry(Path source, Path target) throws IOException { + IOException lastException = null; + for (int attempt = 0; attempt < 5; attempt++) { + try { + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + return; + } catch (IOException e) { + lastException = e; + try { + Thread.sleep(50L * (attempt + 1)); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw e; + } + } + } + throw lastException; + } + + private static boolean isAdaptAnnotation(CtAnnotation annotation) { + Optional sourceName = sourceAnnotationName(annotation); + if (sourceName.isPresent()) { + return AdaptAnnotationNames.matches(sourceName.get()); + } + CtTypeReference type = annotation.getAnnotationType(); + if (type != null) { + String simple = type.getSimpleName(); + String qualified = type.getQualifiedName(); + if (Constants.ANNOT_PACKAGE.equals(qualified) + || (Constants.ANNOT_NAME.equals(simple) + && (qualified == null + || qualified.isBlank() + || Constants.ANNOT_NAME.equals(qualified)))) { + return true; + } + } + String rendered = annotation.toString().trim(); + return hasAnnotationPrefix(rendered, Constants.ANNOT_NAME) + || hasAnnotationPrefix(rendered, Constants.ANNOT_PACKAGE); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static void cleanAdaptAnnotations( + Launcher cleanupLauncher, List> annotations) { + for (CtAnnotation annotation : annotations) { + Optional sourceName = sourceAnnotationName(annotation); + if (sourceName.filter(AdaptAnnotationNames::matches).isPresent() + || (sourceName.isEmpty() && isAdaptAnnotation(annotation))) { + annotation.delete(); + } else if (sourceName.filter(name -> name.contains(".")).isPresent()) { + CtTypeReference originalType = + cleanupLauncher.getFactory().Type().createReference(sourceName.get()); + originalType.setSimplyQualified(false); + ((CtAnnotation) annotation).setAnnotationType(originalType); + } + } + } + + private static Optional sourceAnnotationName(CtAnnotation annotation) { + try { + if (annotation.getPosition() == null || !annotation.getPosition().isValidPosition()) { + return Optional.empty(); + } + String source = annotation.getPosition().getCompilationUnit().getOriginalSourceCode(); + int start = annotation.getPosition().getSourceStart(); + int end = annotation.getPosition().getSourceEnd(); + if (source == null || start < 0 || end < start || end >= source.length()) { + return Optional.empty(); + } + Matcher matcher = SOURCE_ANNOTATION_NAME.matcher(source.substring(start, end + 1)); + return matcher.find() + ? Optional.of(matcher.group(1).replaceAll("\\s+", "")) + : Optional.empty(); + } catch (RuntimeException ignored) { + return Optional.empty(); + } + } + + private static boolean hasAnnotationPrefix(String rendered, String annotationName) { + String prefix = "@" + annotationName; + return rendered.equals(prefix) + || rendered.startsWith(prefix + "(") + || rendered.startsWith(prefix + "["); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Adapt.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Adapt.java index c3709db4..03396fc9 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Adapt.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Adapt.java @@ -3,6 +3,14 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Target; +/** + * Declares how a handwritten Java element relates to reference-CD elements. + * + *

The annotation may be placed on types, fields, methods, parameters, and local variables. + * Explicit annotation matching takes precedence when {@link AdapterParam#ANNOTATION_MATCHING} is + * enabled. Templates consume references from left to right using {@code ${}}, {@code + * ${cap_first}}, and {@code ${uncap_first}} placeholders. + */ @Target({ ElementType.LOCAL_VARIABLE, ElementType.TYPE, @@ -11,9 +19,34 @@ ElementType.PARAMETER, }) public @interface Adapt { + /** Returns whether the annotated Java element is intentionally excluded from adaptation. */ boolean ignore() default false; + /** + * Returns the qualified or owner-relative reference-CD element names represented by this Java + * element. + * + *

For methods, {@code Order.update} selects a unique method by name, {@code Order.update()} + * selects the zero-parameter overload, and {@code Order.update(int)} selects the overload with an + * {@code int} parameter. + */ String[] ref() default {}; + /** + * Returns the name template used to validate and adapt the existing handwritten element. + * + *

For example, {@code ref={"Entity", "Entity.id"}} with {@code + * template="find${}By${cap_first}"} produces {@code findPersonByNumber} when those references map + * to {@code Person} and {@code number}. Placeholders consume references from left to right. + */ String template() default ""; + + /** + * Returns the optional template used when a new name or declaration is generated. + * + *

For example, {@code template="${}"} validates an adapter type while {@code + * genTemplate="${}Builder"} generates {@code PersonBuilder}. An empty value reuses the ordinary + * template where generation supports that fallback. + */ + String genTemplate() default ""; } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdaptAnnotationNames.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdaptAnnotationNames.java new file mode 100644 index 00000000..5eae41ea --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdaptAnnotationNames.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.utils; + +/** Compares annotation AST names with the supported simple and qualified {@link Adapt} names. */ +public final class AdaptAnnotationNames { + + private AdaptAnnotationNames() {} + + /** Returns whether the supplied AST qualified name denotes {@link Adapt}. */ + public static boolean matches(String qualifiedName) { + return Constants.ANNOT_NAME.equals(qualifiedName) + || Constants.ANNOT_PACKAGE.equals(qualifiedName); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdaptReference.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdaptReference.java new file mode 100644 index 00000000..32006465 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdaptReference.java @@ -0,0 +1,95 @@ +package de.monticore.codeAdaption.utils; + +import de.monticore.java.javadsl.JavaDSLMill; +import java.io.IOException; +import java.util.Objects; +import java.util.Optional; + +/** + * Parsed reference to a type, field, or method used by {@code @Adapt} mappings. + * + *

References may be local, such as {@code name} and {@code update(String)}, or owner-qualified, + * such as {@code model.Order.name} and {@code model.Order.update(java.time.Instant)}. The owner is + * separated only before the method's opening parenthesis, while JavaDSL validates owner and method + * syntax and normalizes parameter types through {@link JavaMethodSignatures}. + * + *

The small amount of delimiter handling in this class is intentional: an adapter method + * reference contains parameter types without parameter names, so it is neither a Java invocation + * nor a Java declaration that JavaDSL could parse as a whole. All nested Java type syntax is still + * delegated to JavaDSL. + * + * @param owner optional qualified owner preceding the referenced member + * @param memberName unqualified type or member name + * @param methodSignature normalized method signature when the reference denotes a method + */ +public record AdaptReference( + Optional owner, String memberName, Optional methodSignature) { + + public AdaptReference { + owner = Objects.requireNonNull(owner, "owner"); + memberName = Objects.requireNonNull(memberName, "memberName"); + methodSignature = Objects.requireNonNull(methodSignature, "methodSignature"); + } + + /** + * Parses one adapter reference without confusing dots in qualified parameter types with the + * owner/member separator. + * + * @param value textual adapter reference + * @return the parsed reference, or empty when its owner, member, or signature is malformed + */ + public static Optional parse(String value) { + if (value == null || value.isBlank()) { + return Optional.empty(); + } + String reference = value.trim(); + int open = reference.indexOf('('); + int memberEnd = open < 0 ? reference.length() : open; + int separator = reference.lastIndexOf('.', memberEnd - 1); + String owner = separator < 0 ? "" : reference.substring(0, separator).trim(); + String memberReference = reference.substring(separator + 1).trim(); + + if ((separator >= 0 && owner.isEmpty()) || memberReference.isEmpty() || !isOwner(owner)) { + return Optional.empty(); + } + if (open >= 0 || reference.indexOf(')') >= 0) { + Optional signature = JavaMethodSignatures.parseNormalized(memberReference); + if (signature.isEmpty()) { + return Optional.empty(); + } + return Optional.of( + new AdaptReference( + optional(owner), JavaSourceNames.simpleName(signature.get()), signature)); + } + if (JavaMethodSignatures.parseNormalized(memberReference + "()").isEmpty()) { + return Optional.empty(); + } + return Optional.of( + new AdaptReference(optional(owner), memberReference, Optional.empty())); + } + + /** Returns the normalized member reference, including parameters for methods. */ + public String memberReference() { + return methodSignature.orElse(memberName); + } + + /** Returns whether this reference explicitly denotes a method. */ + public boolean isMethod() { + return methodSignature.isPresent(); + } + + private static Optional optional(String value) { + return value.isEmpty() ? Optional.empty() : Optional.of(value); + } + + private static boolean isOwner(String owner) { + if (owner.isEmpty()) { + return true; + } + try { + return JavaDSLMill.parser().parse_StringMCType(owner).isPresent(); + } catch (IOException | RuntimeException ignored) { + return false; + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterParam.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterParam.java index 5373ce1d..1efe6059 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterParam.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterParam.java @@ -1,13 +1,26 @@ package de.monticore.codeAdaption.utils; +/** Configures matcher precedence and the policy for Java elements that no strategy matches. */ public enum AdapterParam { + /** Enables convention-based matching of equal Java and reference-CD names. */ NAME_MATCHING, + /** Enables matching when a Java identifier contains a reference-CD element name. */ INFIX_MATCHING, + /** Enables explicit matching through {@link Adapt} metadata. */ ANNOTATION_MATCHING, + /** + * Accepts an otherwise unmatched Java type instead of reporting a validation error. + * + *

The type has no reference-CD incarnation to adapt and is therefore excluded from + * mapping-specific adapted output. Use an explicit {@code @Adapt(ignore = true)} annotation when + * an intentionally unchanged reference type must remain in that output. + */ IGNORE_NON_MATCHED_TYPE, + /** Leaves an otherwise unmatched local variable or parameter unchanged. */ IGNORE_NON_MATCHED_VAR, + /** Leaves an otherwise unmatched field, method, or supertype declaration unchanged. */ IGNORE_NON_MATCHED_TYPE_MEMBER } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterUtils.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterUtils.java index 317df905..c5e8d8f4 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterUtils.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/AdapterUtils.java @@ -1,45 +1,127 @@ package de.monticore.codeAdaption.utils; -import static de.monticore.codeAdaption.utils.JavaLoader.print; - import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.codeAdaption.matcher.MatcherHelper; +import de.se_rwth.commons.logging.Log; import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.codeAdaption.utils.visitors.AnnotationRemover; +import de.monticore.ast.ASTNode; import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl._ast.*; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.monticore.javalight._ast.ASTMethodDeclaration; -import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import de.monticore.types.mcbasictypes._ast.ASTMCType; import de.monticore.symbols.oosymbols._symboltable.FieldSymbol; import de.monticore.symboltable.ISymbol; import de.se_rwth.commons.SourcePosition; import java.io.File; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public class AdapterUtils { public static Set getAllCDTypes(ASTCDCompilationUnit cd) { - Set res = new LinkedHashSet<>(); - res.addAll(cd.getCDDefinition().getCDClassesList()); - res.addAll(cd.getCDDefinition().getCDInterfacesList()); - res.addAll(cd.getCDDefinition().getCDEnumsList()); - return res; + return new LinkedHashSet<>(CDModelIndex.of(cd).types()); } public static Optional resolveCDSymbol(String name, ASTCDCompilationUnit refCD) { - List symbol = refCD.getEnclosingScope().resolveFieldMany(name); - if (!symbol.isEmpty()) { - return Optional.of(symbol.iterator().next()); + Optional parsed = AdaptReference.parse(name); + if (parsed.isEmpty() || refCD == null) { + return Optional.empty(); } - return refCD.getEnclosingScope().resolveCDType(name).map(s -> (ISymbol) s).stream().findAny(); + AdaptReference reference = parsed.get(); + String n = reference.memberName(); + CDModelIndex index = CDModelIndex.of(refCD); + Log.debug("AdapterUtils.resolveCDSymbol: resolving '" + name + "'", "AdapterUtils"); + + if (reference.owner().isPresent()) { + String typeName = reference.owner().get(); + String memberName = reference.memberName(); + Log.debug("AdapterUtils.resolveCDSymbol: qualified ref type='" + typeName + "' member='" + memberName + "'", "AdapterUtils"); + + if (reference.isMethod()) { + return index.method(typeName, reference.methodSignature().orElseThrow()) + .map(ASTCDMethod::getSymbol) + .map(symbol -> (ISymbol) symbol); + } + Optional attribute = + index.attribute(typeName, memberName) + .map(ASTCDAttribute::getSymbol) + .map(symbol -> (ISymbol) symbol); + if (attribute.isPresent()) { + Log.debug("AdapterUtils.resolveCDSymbol: resolved to attribute '" + memberName + "' in type '" + typeName + "'", "AdapterUtils"); + return attribute; + } + List methods = index.methods(typeName, memberName); + Optional method = + methods.size() == 1 + ? Optional.of(methods.get(0).getSymbol()) + : Optional.empty(); + if (method.isPresent()) { + Log.debug("AdapterUtils.resolveCDSymbol: resolved to method '" + memberName + "' in type '" + typeName + "'", "AdapterUtils"); + return method; + } + if (methods.size() > 1) { + Log.warn( + "Adapter reference '" + + name + + "' is ambiguous because method '" + + typeName + + "." + + memberName + + "' is overloaded; include a signature-aware mapping"); + return Optional.empty(); + } + } else if (reference.isMethod()) { + List methods = + index.types().stream() + .map(type -> index.method(type.getName(), reference.methodSignature().orElseThrow())) + .flatMap(Optional::stream) + .toList(); + if (methods.size() == 1) { + return Optional.of(methods.get(0).getSymbol()); + } + if (methods.size() > 1) { + Log.warn("Adapter reference '" + name + "' resolves to multiple methods"); + } + return Optional.empty(); + } + + // try resolve as a field in the enclosing scope + List fields = refCD.getEnclosingScope().resolveFieldMany(n); + if (fields.size() == 1) { + Log.debug("AdapterUtils.resolveCDSymbol: resolved to field '" + n + "' in enclosing scope", "AdapterUtils"); + return Optional.of(fields.iterator().next()); + } + if (fields.size() > 1) { + Log.warn("Adapter reference '" + name + "' resolves to multiple fields"); + return Optional.empty(); + } + + // try resolving a top-level CD type by name + Optional tSym = refCD.getEnclosingScope().resolveCDType(n).map(s -> (ISymbol) s).stream().findAny(); + if (tSym.isPresent()) { + Log.debug("AdapterUtils.resolveCDSymbol: resolved to top-level type '" + n + "'", "AdapterUtils"); + } else { + Log.debug("AdapterUtils.resolveCDSymbol: could not resolve '" + name + "' (normalized='" + n + "')", "AdapterUtils"); + } + return tSym; } public static String getFileName(ASTOrdinaryCompilationUnit ast) { return ast.get_SourcePositionStart().getFileName().orElse(""); } + public static String getSimpleFileName(ASTOrdinaryCompilationUnit ast) { + String fullPath = ast.get_SourcePositionStart().getFileName().orElse(""); + if (fullPath.isEmpty()) { + return ""; + } + return new java.io.File(fullPath).getName(); + } + public static String getPosition(SourcePosition pos) { if (pos.getFileName().isPresent()) { String fileName = new File(pos.getFileName().get()).getName(); @@ -48,27 +130,40 @@ public static String getPosition(SourcePosition pos) { return ""; } - /** - * Reads a Java file and removes multi-line and single-line comments. - * - * @param file The Java file from which comments will be removed. - */ - public static void removeComments(File file) { + public static ASTOrdinaryCompilationUnit mergeAsts( + ASTOrdinaryCompilationUnit leftAST, ASTOrdinaryCompilationUnit rightAST) { + return mergeAsts(leftAST, rightAST, false); + } - // remove single line comments - String code = JavaLoader.readFileContent(file).replaceAll("//.*", ""); + /** Merges adapted members into authoritative concrete handwritten code. */ + public static ASTOrdinaryCompilationUnit mergeAstsPreferringLeft( + ASTOrdinaryCompilationUnit leftAST, ASTOrdinaryCompilationUnit rightAST) { + return mergeAsts(leftAST, rightAST, true); + } - // remove multilines comments - Pattern pattern = Pattern.compile("/\\*(?s).*?\\*/"); - Matcher matcher = pattern.matcher(code); - code = matcher.replaceAll(""); + private static ASTOrdinaryCompilationUnit mergeAsts( + ASTOrdinaryCompilationUnit leftAST, + ASTOrdinaryCompilationUnit rightAST, + boolean preferLeftOnConflict) { - // print code - JavaLoader.writeFile(file.toPath(), code); - } + String leftPackage = packageName(leftAST); + String rightPackage = packageName(rightAST); + if (!leftPackage.equals(rightPackage)) { + throw new IllegalArgumentException( + "Cannot merge Java compilation units from different packages: '" + + leftPackage + + "' (" + + getFileName(leftAST) + + ") and '" + + rightPackage + + "' (" + + getFileName(rightAST) + + ")"); + } - public static ASTOrdinaryCompilationUnit mergeAsts( - ASTOrdinaryCompilationUnit leftAST, ASTOrdinaryCompilationUnit rightAST) { + TypeIdentityScope leftTypes = TypeIdentityScope.from(leftAST); + TypeIdentityScope rightTypes = TypeIdentityScope.from(rightAST); + mergeImports(leftAST, rightAST); JavaAstElemCollector lCollector = new JavaAstElemCollector(); JavaDSLTraverser leftTraverser = JavaDSLMill.traverser(); @@ -80,103 +175,553 @@ public static ASTOrdinaryCompilationUnit mergeAsts( rightTraverser.add4JavaDSL(rCollector); rightAST.accept(rightTraverser); - for (ASTTypeDeclaration right : rCollector.getAllTypeDeclarations()) { + // Only compilation-unit declarations participate here. Nested types belong to their enclosing + // declaration and must never be promoted to top-level declarations during a merge. + for (ASTTypeDeclaration right : rightAST.getTypeDeclarationList()) { Optional lType = - lCollector.getAllTypeDeclarations().stream() + leftAST.getTypeDeclarationList().stream() .filter(t -> t.getName().equals(right.getName())) - .findAny(); + .findFirst(); if (lType.isEmpty()) { - leftAST.addTypeDeclaration(right); + leftAST.addTypeDeclaration(right.deepClone()); } else { - leftAST.removeTypeDeclaration(lType.get()); - leftAST.addTypeDeclaration( - mergeTypeDeclaration(lCollector, lType.get(), rCollector, right)); + mergeTypeDeclaration( + lCollector, + lType.get(), + rCollector, + right, + leftTypes, + rightTypes, + getFileName(leftAST), + getFileName(rightAST), + preferLeftOnConflict); } } return leftAST; } + private static String packageName(ASTOrdinaryCompilationUnit unit) { + return unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + } + + private static void mergeImports( + ASTOrdinaryCompilationUnit leftAST, ASTOrdinaryCompilationUnit rightAST) { + Set existingImports = new LinkedHashSet<>(); + Map explicitImportsBySimpleName = new LinkedHashMap<>(); + for (ASTImportDeclaration leftImport : leftAST.getImportDeclarationList()) { + registerImport(leftImport, existingImports, explicitImportsBySimpleName); + } + + for (ASTImportDeclaration rightImport : rightAST.getImportDeclarationList()) { + if (registerImport(rightImport, existingImports, explicitImportsBySimpleName)) { + leftAST.addImportDeclaration(rightImport.deepClone()); + } + } + } + + private static boolean registerImport( + ASTImportDeclaration importDeclaration, + Set existingImports, + Map explicitImportsBySimpleName) { + String qualifiedName = importDeclaration.getMCQualifiedName().getQName(); + ImportIdentity identity = + new ImportIdentity(qualifiedName, importDeclaration.isStatic(), importDeclaration.isSTAR()); + if (!existingImports.add(identity)) { + return false; + } + if (identity.star()) { + return true; + } + + String simpleName = JavaSourceNames.simpleName(qualifiedName); + ImportSimpleName key = new ImportSimpleName(simpleName, identity.isStatic()); + String conflict = explicitImportsBySimpleName.putIfAbsent(key, qualifiedName); + if (conflict != null && !conflict.equals(qualifiedName)) { + throw new IllegalStateException( + "Cannot merge imports '" + + conflict + + "' and '" + + qualifiedName + + "' because both use the " + + (identity.isStatic() ? "static member" : "type") + + " name '" + + simpleName + + "'"); + } + return true; + } + + private record ImportIdentity(String qualifiedName, boolean isStatic, boolean star) {} + + private record ImportSimpleName(String name, boolean isStatic) {} + private static ASTTypeDeclaration mergeTypeDeclaration( JavaAstElemCollector lCollector, ASTTypeDeclaration lefType, JavaAstElemCollector rCollector, - ASTTypeDeclaration rightType) { + ASTTypeDeclaration rightType, + TypeIdentityScope leftTypes, + TypeIdentityScope rightTypes, + String leftSource, + String rightSource, + boolean preferLeftOnConflict) { + + // Get ordered lists of fields and methods from both sides + List lFields = lCollector.getAllFieldDeclarations(lefType); + List rFields = rCollector.getAllFieldDeclarations(rightType); + List lMethods = lCollector.getAllMethodDeclarations(lefType); + List rMethods = rCollector.getAllMethodDeclarations(rightType); - // merge methods - for (ASTMethodDeclaration rMeth : rCollector.getAllMethodDeclarations(rightType)) { - Optional lMeth = - lCollector.getAllMethodDeclarations(lefType).stream() - .filter(m -> compare(m, rMeth)) - .findAny(); + String typeName = lefType.getName(); - if (lMeth.isEmpty()) { + if (!lefType.getClass().equals(rightType.getClass())) { + if (preferLeftOnConflict) { + return lefType; + } + throw new IllegalStateException( + String.format( + "Cannot merge type '%s': declaration kinds differ in '%s' and '%s'", + typeName, leftSource, rightSource)); + } + + mergeTypeRelationships( + lefType, + rightType, + leftTypes, + rightTypes, + leftSource, + rightSource, + preferLeftOnConflict); + + // Java overload identity is the method name plus normalized parameter types. A same-name + // method with different parameters is a valid overload and must be retained. + List leftMethodsBySignature = new ArrayList<>(); + for (ASTMethodDeclaration method : lMethods) { + leftMethodsBySignature.add(new MethodEntry(methodIdentity(method, leftTypes), method)); + } + for (ASTMethodDeclaration rMeth : rMethods) { + MethodIdentity rightIdentity = methodIdentity(rMeth, rightTypes); + List signatureMatches = + leftMethodsBySignature.stream() + .filter(entry -> entry.identity().collidesWith(rightIdentity)) + .toList(); + if (signatureMatches.size() > 1) { + throw new IllegalStateException( + "Ambiguous method identity '" + rightIdentity + "' in type '" + typeName + "'"); + } + ASTMethodDeclaration existingMethod = + signatureMatches.isEmpty() ? null : signatureMatches.get(0).method(); + + if (existingMethod == null) { if (lefType instanceof ASTClassDeclaration) { - ((ASTClassDeclaration) lefType).getClassBody().addClassBodyDeclaration(rMeth); - } else if (rightType instanceof ASTInterfaceDeclaration) { - ((ASTInterfaceDeclaration) lefType).getInterfaceBody().addInterfaceBodyDeclaration(rMeth); + ((ASTClassDeclaration) lefType) + .getClassBody() + .addClassBodyDeclaration(rMeth.deepClone()); + } else if (lefType instanceof ASTInterfaceDeclaration) { + ((ASTInterfaceDeclaration) lefType) + .getInterfaceBody() + .addInterfaceBodyDeclaration(rMeth.deepClone()); + } + leftMethodsBySignature.add(new MethodEntry(rightIdentity, rMeth)); + } else if (!preferLeftOnConflict + && !equivalentWithoutAdaptationMetadata(existingMethod, rMeth)) { + MergeOrigin existingOrigin = mergeOrigin(existingMethod); + MergeOrigin incomingOrigin = mergeOrigin(rMeth); + if (existingOrigin == incomingOrigin) { + throw new IllegalStateException( + String.format( + "Conflicting method '%s' in type '%s': declarations differ between '%s' and '%s'", + rightIdentity, typeName, leftSource, rightSource)); + } + // A reference-derived declaration carries the Adapt marker that identifies it as the + // authoritative template result. This is explicit provenance, not a name-based guess. + if (incomingOrigin == MergeOrigin.REFERENCE_DERIVED) { + ASTMethodDeclaration replacement = + replaceMethodDeclaration(lefType, existingMethod, rMeth); + int entryIndex = leftMethodsBySignature.indexOf(signatureMatches.get(0)); + leftMethodsBySignature.set(entryIndex, new MethodEntry(rightIdentity, replacement)); } } } - // merge fields - for (ASTFieldDeclaration rField : rCollector.getAllFieldDeclarations(rightType)) { + // A nested type remains a member of its enclosing type. Add missing direct nested types, but + // never expose them through the compilation-unit collector. + if (lefType instanceof ASTClassDeclaration leftClass + && rightType instanceof ASTClassDeclaration rightClass) { + Map leftNestedTypes = new LinkedHashMap<>(); + for (var declaration : leftClass.getClassBody().getClassBodyDeclarationList()) { + if (declaration instanceof ASTTypeDeclaration nested) { + leftNestedTypes.put(nested.getName(), nested); + } + } + for (var declaration : rightClass.getClassBody().getClassBodyDeclarationList()) { + if (declaration instanceof ASTTypeDeclaration nested) { + ASTTypeDeclaration existingNested = leftNestedTypes.get(nested.getName()); + if (existingNested == null) { + leftNestedTypes.put(nested.getName(), nested); + leftClass.getClassBody().addClassBodyDeclaration(declaration.deepClone()); + } else if (!preferLeftOnConflict + && !equivalentWithoutAdaptationMetadata(existingNested, nested)) { + throw new IllegalStateException( + String.format( + "Conflicting nested type '%s.%s': declarations differ between '%s' and '%s'", + typeName, nested.getName(), leftSource, rightSource)); + } + } + } + } - Optional lField = - lCollector.getAllFieldDeclarations(lefType).stream() - .filter( - f -> - f.getVariableDeclarator(0) - .getDeclarator() - .getName() - .equals(rField.getVariableDeclarator(0).getDeclarator().getName())) - .findAny(); + // merge fields - compare by name for conflict detection / deduplication + Map leftFieldsByName = new LinkedHashMap<>(); + for (ASTFieldDeclaration lf : lFields) { + String name = lf.getVariableDeclarator(0).getDeclarator().getName(); + leftFieldsByName.put(name, lf); + } - if (lField.isEmpty()) { + for (ASTFieldDeclaration rf : rFields) { + String rFieldName = rf.getVariableDeclarator(0).getDeclarator().getName(); + if (!leftFieldsByName.containsKey(rFieldName)) { if (lefType instanceof ASTClassDeclaration) { - ((ASTClassDeclaration) lefType).getClassBody().addClassBodyDeclaration(rField); + ((ASTClassDeclaration) lefType) + .getClassBody() + .addClassBodyDeclaration(rf.deepClone()); } + } else if (!preferLeftOnConflict + && !equivalentWithoutAdaptationMetadata( + leftFieldsByName.get(rFieldName), rf)) { + throw new IllegalStateException( + String.format( + "Conflicting field '%s' in type '%s': declarations differ between '%s' and '%s'", + rFieldName, typeName, leftSource, rightSource)); } } return lefType; } - /*** - * Compare two method (names and types) and return true when the method are the same. - * @param leftMethod the left method. - * @param rightMethod the right method. - * @return true if both method are the same. + private static void mergeTypeRelationships( + ASTTypeDeclaration leftType, + ASTTypeDeclaration rightType, + TypeIdentityScope leftTypes, + TypeIdentityScope rightTypes, + String leftSource, + String rightSource, + boolean preferLeftOnConflict) { + if (leftType instanceof ASTClassDeclaration leftClass + && rightType instanceof ASTClassDeclaration rightClass) { + mergeCompletedAbstractModifier(leftClass, rightClass); + if (leftClass.isPresentSuperClass() + && rightClass.isPresentSuperClass() + && !leftTypes.identity(leftClass.getSuperClass()) + .sameTypeAs(rightTypes.identity(rightClass.getSuperClass())) + && !preferLeftOnConflict) { + throw new IllegalStateException( + String.format( + "Conflicting superclass of type '%s': declarations differ between '%s' and '%s'", + leftType.getName(), leftSource, rightSource)); + } else if (!leftClass.isPresentSuperClass() && rightClass.isPresentSuperClass()) { + leftClass.setSuperClass(rightClass.getSuperClass().deepClone()); + } + mergeTypes( + leftClass.getImplementedInterfaceList(), + rightClass.getImplementedInterfaceList(), + leftTypes, + rightTypes); + } else if (leftType instanceof ASTInterfaceDeclaration leftInterface + && rightType instanceof ASTInterfaceDeclaration rightInterface) { + mergeTypes( + leftInterface.getExtendedInterfaceList(), + rightInterface.getExtendedInterfaceList(), + leftTypes, + rightTypes); + } else if (leftType instanceof ASTEnumDeclaration leftEnum + && rightType instanceof ASTEnumDeclaration rightEnum) { + mergeTypes( + leftEnum.getImplementedInterfaceList(), + rightEnum.getImplementedInterfaceList(), + leftTypes, + rightTypes); + Set constants = + leftEnum.getEnumConstantDeclarationList().stream() + .map(constant -> constant.getName()) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + for (int index = 0; index < rightEnum.getEnumConstantDeclarationList().size(); index++) { + var constant = rightEnum.getEnumConstantDeclaration(index); + if (constants.add(constant.getName())) { + leftEnum + .getEnumConstantDeclarationList() + .add(Math.min(index, leftEnum.sizeEnumConstantDeclarations()), constant.deepClone()); + } + } + } + } + + /** + * The concrete source is authoritative for handwritten members, while the adapted declaration's + * abstractness has already been normalized to the final target CD. Preserve that structural + * modifier when the adapted declaration is merged into an existing concrete class. */ - protected static boolean compare( - ASTMethodDeclaration leftMethod, ASTMethodDeclaration rightMethod) { - // compare names - if (!leftMethod.getName().equals(rightMethod.getName())) { - return false; + private static void mergeCompletedAbstractModifier( + ASTClassDeclaration concreteClass, ASTClassDeclaration adaptedClass) { + boolean adaptedIsAbstract = + adaptedClass.getJavaModifierList().stream() + .anyMatch(modifier -> "abstract".equals(JavaLoader.print(modifier).trim())); + boolean concreteIsAbstract = + concreteClass.getJavaModifierList().stream() + .anyMatch(modifier -> "abstract".equals(JavaLoader.print(modifier).trim())); + if (!adaptedIsAbstract) { + concreteClass + .getJavaModifierList() + .removeIf(modifier -> "abstract".equals(JavaLoader.print(modifier).trim())); + return; } + if (concreteIsAbstract) { + return; + } + adaptedClass.getJavaModifierList().stream() + .filter(modifier -> "abstract".equals(JavaLoader.print(modifier).trim())) + .findFirst() + .map(modifier -> modifier.deepClone()) + .ifPresent(concreteClass::addJavaModifier); + } - // is present parameters ? - if (!leftMethod.getFormalParameters().isPresentFormalParameterListing()) { - return !rightMethod.getFormalParameters().isPresentFormalParameterListing(); + private static void mergeTypes( + List left, + List right, + TypeIdentityScope leftTypes, + TypeIdentityScope rightTypes) { + List existing = + left.stream().map(leftTypes::identity).collect(java.util.stream.Collectors.toList()); + for (ASTMCType candidate : right) { + TypeIdentity identity = rightTypes.identity(candidate); + if (existing.stream().noneMatch(current -> current.sameTypeAs(identity))) { + left.add(candidate.deepClone()); + existing.add(identity); + } } + } - List leftParams = - leftMethod.getFormalParameters().getFormalParameterListing().getFormalParameterList(); - List rightParams = - rightMethod.getFormalParameters().getFormalParameterListing().getFormalParameterList(); + /** + * Get a method signature string for comparison. + * Format: "methodName(paramType1,paramType2)" + * Uses method name + parameter types (not return type) for comparison. + */ + private static MethodIdentity methodIdentity( + ASTMethodDeclaration method, TypeIdentityScope types) { + List parameterTypes = new ArrayList<>(); + if (method.getFormalParameters().isPresentFormalParameterListing()) { + method.getFormalParameters().getFormalParameterListing().getFormalParameterList() + .forEach(parameter -> parameterTypes.add(types.identity(parameter.getMCType()))); + } + return new MethodIdentity(method.getName(), List.copyOf(parameterTypes)); + } - // same number of parameters ? - if (leftParams.size() != rightParams.size()) { - return false; + private static boolean equivalentWithoutAdaptationMetadata( + ASTNode left, ASTNode right) { + ASTNode cleanedLeft = left.deepClone(); + ASTNode cleanedRight = right.deepClone(); + removeAdaptationMetadata(cleanedLeft); + removeAdaptationMetadata(cleanedRight); + return cleanedLeft.deepEquals(cleanedRight, true); + } + + private record MethodEntry(MethodIdentity identity, ASTMethodDeclaration method) {} + + private enum MergeOrigin { + REFERENCE_DERIVED, + GENERATED_OR_CONCRETE + } + + private static MergeOrigin mergeOrigin(ASTMethodDeclaration method) { + return MatcherHelper.getInfoAnnotation(method.getMCModifierList()).isPresent() + ? MergeOrigin.REFERENCE_DERIVED + : MergeOrigin.GENERATED_OR_CONCRETE; + } + + private static ASTMethodDeclaration replaceMethodDeclaration( + ASTTypeDeclaration owner, + ASTMethodDeclaration existing, + ASTMethodDeclaration replacement) { + ASTMethodDeclaration replacementCopy = replacement.deepClone(); + List declarations; + if (owner instanceof ASTClassDeclaration classDeclaration) { + declarations = classDeclaration.getClassBody().getClassBodyDeclarationList(); + int index = declarations.indexOf(existing); + if (index >= 0) { + classDeclaration.getClassBody().getClassBodyDeclarationList().set(index, replacementCopy); + return replacementCopy; + } + } else if (owner instanceof ASTInterfaceDeclaration interfaceDeclaration) { + declarations = interfaceDeclaration.getInterfaceBody().getInterfaceBodyDeclarationList(); + int index = declarations.indexOf(existing); + if (index >= 0) { + interfaceDeclaration + .getInterfaceBody() + .getInterfaceBodyDeclarationList() + .set(index, replacementCopy); + return replacementCopy; + } } + throw new IllegalStateException( + "Cannot replace method '" + + methodIdentity(existing, TypeIdentityScope.empty()) + + "' because it is not a direct member of type '" + + owner.getName() + + "'"); + } - // parameters have the same type ? - for (int i = 0; i < leftParams.size(); i++) { - if (!print(leftParams.get(i).getMCType()).equals(print(rightParams.get(i).getMCType()))) { + private record MethodIdentity(String name, List parameters) { + private boolean collidesWith(MethodIdentity other) { + if (!name.equals(other.name) || parameters.size() != other.parameters.size()) { return false; } + for (int index = 0; index < parameters.size(); index++) { + if (!parameters.get(index).sameErasureAs(other.parameters.get(index))) { + return false; + } + } + return true; } - return true; + @Override + public String toString() { + return name + + "(" + + parameters.stream() + .map(TypeIdentity::canonical) + .collect(java.util.stream.Collectors.joining(",")) + + ")"; + } } + + private record TypeIdentity(String canonical, String simpleName, boolean resolved) { + private boolean sameTypeAs(TypeIdentity other) { + return canonical.equals(other.canonical); + } + + private boolean sameErasureAs(TypeIdentity other) { + if (canonical.equals(other.canonical)) { + return true; + } + if (resolved && other.resolved) { + return false; + } + // An unresolved same-leaf type might denote the qualified type. Treat it as a collision so + // the merger fails safely instead of emitting an illegal duplicate-erasure overload. + return simpleName.equals(other.simpleName); + } + } + + /** Import-aware, parser-backed type identities for one original compilation unit. */ + private record TypeIdentityScope( + Map> explicitImports, Set wildcardPackages) { + + private static TypeIdentityScope empty() { + return new TypeIdentityScope(Map.of(), Set.of()); + } + + private static TypeIdentityScope from(ASTOrdinaryCompilationUnit unit) { + Map> explicit = new LinkedHashMap<>(); + Set wildcards = new LinkedHashSet<>(); + for (ASTImportDeclaration declaration : unit.getImportDeclarationList()) { + if (declaration.isStatic()) { + continue; + } + String imported = declaration.getMCQualifiedName().getQName(); + if (declaration.isSTAR()) { + wildcards.add(imported); + } else { + explicit + .computeIfAbsent(JavaSourceNames.simpleName(imported), ignored -> new LinkedHashSet<>()) + .add(imported); + } + } + return new TypeIdentityScope( + explicit.entrySet().stream() + .collect( + java.util.stream.Collectors.toUnmodifiableMap( + Map.Entry::getKey, entry -> Set.copyOf(entry.getValue()))), + Set.copyOf(wildcards)); + } + + private TypeIdentity identity(ASTMCType type) { + String resolvedSource = + JavaSourceNames.replaceTypeNames( + JavaSourceNames.printQualifiedType(type), this::resolveReference); + JavaSourceNames.ErasedType erased = + JavaSourceNames.erasedType(resolvedSource) + .orElseThrow( + () -> new IllegalStateException("Cannot parse Java type '" + resolvedSource + "'")); + String canonical = + erased.name().replace('$', '.') + "[]".repeat(erased.arrayDimensions()); + boolean resolved = + erased.name().contains(".") || isPrimitiveOrVoid(erased.name()); + return new TypeIdentity( + canonical, + JavaSourceNames.simpleName(erased.name()) + "[]".repeat(erased.arrayDimensions()), + resolved); + } + + private Optional resolveReference(JavaSourceNames.TypeReferenceName reference) { + if (reference.qualified() || isPrimitiveOrVoid(reference.originalName())) { + return Optional.empty(); + } + Set explicit = explicitImports.get(reference.simpleName()); + if (explicit != null && !explicit.isEmpty()) { + return uniqueBinding(reference.simpleName(), explicit); + } + LinkedHashSet classpathBindings = new LinkedHashSet<>(); + String javaLang = "java.lang." + reference.simpleName(); + if (classpathTypeExists(javaLang)) { + classpathBindings.add(javaLang); + } + for (String wildcardPackage : wildcardPackages) { + String candidate = wildcardPackage + "." + reference.simpleName(); + if (classpathTypeExists(candidate)) { + classpathBindings.add(candidate); + } + } + return uniqueBinding(reference.simpleName(), classpathBindings); + } + + private static Optional uniqueBinding(String simpleName, Set bindings) { + if (bindings.isEmpty()) { + return Optional.empty(); + } + if (bindings.size() == 1) { + return Optional.of(bindings.iterator().next()); + } + throw new IllegalStateException( + "Ambiguous Java type '" + simpleName + "' resolves to " + bindings); + } + + private static boolean classpathTypeExists(String qualifiedName) { + try { + Class.forName(qualifiedName, false, AdapterUtils.class.getClassLoader()); + return true; + } catch (ClassNotFoundException | LinkageError ignored) { + return false; + } + } + + private static boolean isPrimitiveOrVoid(String name) { + return Set.of( + "boolean", "byte", "short", "int", "long", "float", "double", "char", "void") + .contains(name); + } + } + + private static void removeAdaptationMetadata(ASTNode node) { + AnnotationRemover remover = new AnnotationRemover(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(remover); + traverser.add4JavaLight(remover); + traverser.add4MCCommonStatements(remover); + traverser.add4MCVarDeclarationStatements(remover); + node.accept(traverser); + } + } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDImportProjector.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDImportProjector.java new file mode 100644 index 00000000..91034504 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDImportProjector.java @@ -0,0 +1,289 @@ +package de.monticore.codeAdaption.utils; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTCompilationUnit; +import de.monticore.java.javadsl._ast.ASTImportDeclaration; +import de.monticore.java.javadsl._ast.ASTMCArrayType; +import de.monticore.java.javadsl._ast.ASTMCBasicGenericType; +import de.monticore.java.javadsl._ast.ASTMCQualifiedType; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._visitor.JavaDSLTraverser; +import de.monticore.java.javadsl._visitor.JavaDSLVisitor2; +import de.monticore.types.mccollectiontypes._ast.ASTMCListType; +import de.monticore.types.mccollectiontypes._ast.ASTMCMapType; +import de.monticore.types.mccollectiontypes._ast.ASTMCOptionalType; +import de.monticore.types.mccollectiontypes._ast.ASTMCSetType; +import de.monticore.types.mccollectiontypes._visitor.MCCollectionTypesVisitor2; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * Projects non-{@code java.lang} imports from reference and concrete class diagrams into + * handwritten Java adapters. + * + *

Explicit imports are projected only into units that use their simple name. This avoids + * creating invalid Java sources merely because unrelated reference and concrete imports have the + * same simple name. CD wildcard imports are retained as wildcards because their exported type set + * cannot be determined from the import declaration alone. + */ +public final class CDImportProjector { + + private CDImportProjector() {} + + /** + * Adds required CD imports to Java compilation units without duplicating existing imports. + * + *

An existing explicit Java import is the authoritative binding for that unit. If a used, + * otherwise-unbound simple name is supplied by multiple distinct explicit CD imports, projection + * fails deterministically instead of creating an uncompilable pair of imports. + * + *

For example, if the reference CD imports {@code reference.Customer} and the concrete CD + * imports {@code concrete.Customer}, a Java unit that uses unqualified {@code Customer} is + * ambiguous and causes an exception. If that unit already imports {@code reference.Customer}, + * the existing Java import wins and no conflicting import is added. A unit that never uses + * {@code Customer} receives neither explicit import. + * + * @param javaUnits handwritten adapter compilation units to update + * @param classDiagrams class diagrams whose imports are projected + */ + public static void project( + Set javaUnits, ASTCDCompilationUnit... classDiagrams) { + Map imports = parseImports(classDiagrams); + if (imports.isEmpty()) { + return; + } + + List> wildcardImports = + imports.entrySet().stream().filter(entry -> entry.getKey().wildcard()).toList(); + Map>> explicitImports = + explicitImportsBySimpleName(imports); + + for (ASTOrdinaryCompilationUnit unit : javaUnits) { + Set existing = new LinkedHashSet<>(); + Map existingExplicitBindings = new LinkedHashMap<>(); + List> importsToAdd = new ArrayList<>(); + for (ASTImportDeclaration importDeclaration : unit.getImportDeclarationList()) { + if (importDeclaration.isStatic()) { + continue; + } + ImportKey key = ImportKey.from(importDeclaration); + existing.add(key); + if (!key.wildcard()) { + existingExplicitBindings.putIfAbsent(key.simpleName(), key.qualifiedName()); + } + } + + for (Map.Entry wildcardImport : wildcardImports) { + queueIfMissing(existing, importsToAdd, wildcardImport); + } + + Set declaredTypeNames = + unit.getTypeDeclarationList().stream() + .map(type -> type.getName()) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + Set unqualifiedTypeUses = unqualifiedTypeUses(unit); + for (Map.Entry>> candidates : + explicitImports.entrySet()) { + String simpleName = candidates.getKey(); + if (declaredTypeNames.contains(simpleName) + || !unqualifiedTypeUses.contains(simpleName)) { + continue; + } + + String existingBinding = existingExplicitBindings.get(simpleName); + if (existingBinding != null) { + // Handwritten Java already made this binding explicit. Never add a conflicting CD import. + continue; + } + + List> distinctCandidates = + candidates.getValue().stream() + .sorted(Comparator.comparing(entry -> entry.getKey().qualifiedName())) + .toList(); + if (distinctCandidates.size() > 1) { + throw ambiguousImport(simpleName, distinctCandidates); + } + queueIfMissing(existing, importsToAdd, distinctCandidates.get(0)); + } + importsToAdd.forEach(candidate -> unit.addImportDeclaration(candidate.getValue().deepClone())); + } + } + + private static Map>> + explicitImportsBySimpleName(Map imports) { + Map>> bySimpleName = + new LinkedHashMap<>(); + imports.entrySet().stream() + .filter(entry -> !entry.getKey().wildcard()) + .forEach( + entry -> + bySimpleName + .computeIfAbsent(entry.getKey().simpleName(), ignored -> new ArrayList<>()) + .add(entry)); + return bySimpleName; + } + + private static void queueIfMissing( + Set existing, + List> importsToAdd, + Map.Entry candidate) { + if (existing.add(candidate.getKey())) { + importsToAdd.add(candidate); + } + } + + private static IllegalStateException ambiguousImport( + String simpleName, + List> distinctCandidates) { + String qualifiedNames = + distinctCandidates.stream() + .map(entry -> entry.getKey().qualifiedName()) + .collect(java.util.stream.Collectors.joining(", ")); + return new IllegalStateException( + "Ambiguous class-diagram imports for used Java type '" + + simpleName + + "': " + + qualifiedNames); + } + + /** + * Collects simple names from Java type nodes rather than from identifier-shaped source text. + * This distinguishes a type use such as {@code Customer value} from a field or method named + * {@code Customer}, while still covering types nested in arrays and generic arguments. + */ + private static Set unqualifiedTypeUses(ASTOrdinaryCompilationUnit unit) { + Set typeNames = new LinkedHashSet<>(); + JavaDSLVisitor2 collector = + new JavaDSLVisitor2() { + @Override + public void visit(ASTMCQualifiedType type) { + collectUnqualifiedTypeNames(JavaSourceNames.printQualifiedType(type), typeNames); + } + + @Override + public void visit(ASTMCBasicGenericType type) { + collectUnqualifiedTypeNames(JavaSourceNames.printQualifiedType(type), typeNames); + } + + @Override + public void visit(ASTMCArrayType type) { + collectUnqualifiedTypeNames(JavaSourceNames.printQualifiedType(type), typeNames); + } + }; + MCCollectionTypesVisitor2 collectionTypeCollector = + new MCCollectionTypesVisitor2() { + @Override + public void visit(ASTMCListType type) { + collectUnqualifiedTypeNames(JavaSourceNames.printQualifiedType(type), typeNames); + } + + @Override + public void visit(ASTMCMapType type) { + collectUnqualifiedTypeNames(JavaSourceNames.printQualifiedType(type), typeNames); + } + + @Override + public void visit(ASTMCOptionalType type) { + collectUnqualifiedTypeNames(JavaSourceNames.printQualifiedType(type), typeNames); + } + + @Override + public void visit(ASTMCSetType type) { + collectUnqualifiedTypeNames(JavaSourceNames.printQualifiedType(type), typeNames); + } + }; + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(collector); + traverser.add4MCCollectionTypes(collectionTypeCollector); + unit.accept(traverser); + return typeNames; + } + + private static void collectUnqualifiedTypeNames(String printedType, Set typeNames) { + JavaSourceNames.typeReferences(printedType).stream() + .filter(reference -> !reference.qualified()) + .map(JavaSourceNames.TypeReferenceName::simpleName) + .forEach(typeNames::add); + } + + private static Map parseImports( + ASTCDCompilationUnit... classDiagrams) { + Set importKeys = new LinkedHashSet<>(); + for (ASTCDCompilationUnit classDiagram : classDiagrams) { + if (classDiagram == null) { + continue; + } + classDiagram.getMCImportStatementList().stream() + .map( + statement -> + new ImportKey(statement.getMCQualifiedName().getQName(), statement.isStar())) + .filter(importKey -> !importKey.isJavaLang()) + .forEach(importKeys::add); + } + if (importKeys.isEmpty()) { + return Map.of(); + } + + StringBuilder source = new StringBuilder(); + importKeys.forEach( + importKey -> + source + .append("import ") + .append(importKey.qualifiedName()) + .append(importKey.wildcard() ? ".*" : "") + .append(";\n")); + source.append("class CDImportHolder {}\n"); + try { + Optional parsed = + JavaDSLMill.parser().parse_StringCompilationUnit(source.toString()); + if (parsed.isEmpty() || !(parsed.get() instanceof ASTOrdinaryCompilationUnit unit)) { + throw new IllegalStateException("Could not parse class-diagram imports as Java imports"); + } + Map result = new LinkedHashMap<>(); + unit.getImportDeclarationList() + .forEach(importDeclaration -> result.put(ImportKey.from(importDeclaration), importDeclaration)); + if (!result.keySet().equals(importKeys)) { + throw new IllegalStateException("Java parser changed class-diagram import semantics"); + } + return result; + } catch (IOException e) { + throw new IllegalStateException("Could not parse class-diagram imports as Java imports", e); + } + } + + private record ImportKey(String qualifiedName, boolean wildcard) { + + private ImportKey { + if (qualifiedName == null || qualifiedName.isBlank()) { + throw new IllegalArgumentException("Import qualified name must not be blank"); + } + } + + private static ImportKey from(ASTImportDeclaration importDeclaration) { + return new ImportKey( + importDeclaration.getMCQualifiedName().getQName(), importDeclaration.isSTAR()); + } + + private String simpleName() { + return JavaSourceNames.simpleName(qualifiedName); + } + + private boolean isJavaLang() { + if (wildcard) { + return "java.lang".equals(qualifiedName); + } + String prefix = "java.lang."; + return qualifiedName.startsWith(prefix) + && !qualifiedName.substring(prefix.length()).contains("."); + } + } + +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDModelIndex.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDModelIndex.java new file mode 100644 index 00000000..3710141c --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDModelIndex.java @@ -0,0 +1,300 @@ +package de.monticore.codeAdaption.utils; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cdassociation._ast.ASTCDAssociation; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.cdbasis._ast.ASTCDClass; +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; +import de.monticore.cdinterfaceandenum._ast.ASTCDInterface; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * Immutable lookup structure for class-diagram elements and inheritance relationships. + * + *

Named lookups are intentionally based on simple CD type names. Supplying {@code + * shop.Payment} therefore looks up {@code Payment}; this index does not distinguish two types that + * differ only by package. Member-owner lookups use the owning type's simple name, while {@link + * #ownerOf(ASTCDAttribute)} and {@link #ownerOf(ASTCDMethod)} use AST object identity and therefore + * require nodes from this indexed CD. + */ +public final class CDModelIndex { + private final ASTCDCompilationUnit cd; + private final List types; + private final List classes; + private final List interfaces; + private final List enums; + private final List associations; + private final Map typesBySimpleName; + private final Map> attributesByOwner; + private final Map> methodsByOwner; + private final Map attributeOwners; + private final Map methodOwners; + private final Map> directParentsByType; + private final Map> childrenByParent; + private final Map> implementersByInterface; + + private CDModelIndex() { + this.cd = null; + this.types = List.of(); + this.classes = List.of(); + this.interfaces = List.of(); + this.enums = List.of(); + this.associations = List.of(); + this.typesBySimpleName = Map.of(); + this.attributesByOwner = Map.of(); + this.methodsByOwner = Map.of(); + this.attributeOwners = Map.of(); + this.methodOwners = Map.of(); + this.directParentsByType = Map.of(); + this.childrenByParent = Map.of(); + this.implementersByInterface = Map.of(); + } + + private CDModelIndex(ASTCDCompilationUnit cd) { + this.cd = cd; + this.classes = List.copyOf(cd.getCDDefinition().getCDClassesList()); + this.interfaces = List.copyOf(cd.getCDDefinition().getCDInterfacesList()); + this.enums = List.copyOf(cd.getCDDefinition().getCDEnumsList()); + this.associations = List.copyOf(cd.getCDDefinition().getCDAssociationsList()); + + List collectedTypes = new ArrayList<>(); + collectedTypes.addAll(classes); + collectedTypes.addAll(interfaces); + collectedTypes.addAll(enums); + this.types = List.copyOf(collectedTypes); + + Map typeMap = new LinkedHashMap<>(); + Map> attributes = new LinkedHashMap<>(); + Map> methods = new LinkedHashMap<>(); + Map attributeOwnerMap = new IdentityHashMap<>(); + Map methodOwnerMap = new IdentityHashMap<>(); + Map> parents = new LinkedHashMap<>(); + Map> children = new LinkedHashMap<>(); + Map> implementers = new LinkedHashMap<>(); + + for (ASTCDType type : types) { + String typeName = type.getName(); + typeMap.put(typeName, type); + attributes.put(typeName, List.copyOf(type.getCDAttributeList())); + methods.put(typeName, List.copyOf(type.getCDMethodList())); + + for (ASTCDAttribute attribute : type.getCDAttributeList()) { + attributeOwnerMap.put(attribute, type); + } + for (ASTCDMethod method : type.getCDMethodList()) { + methodOwnerMap.put(method, type); + } + + Set directParents = new LinkedHashSet<>(); + CDTypeRelations.firstSuperclassName(type) + .map(JavaSourceNames::simpleName) + .filter(name -> !name.isEmpty()) + .ifPresent(directParents::add); + for (String interfaceName : CDTypeRelations.interfaceNames(type)) { + String simpleName = JavaSourceNames.simpleName(interfaceName); + if (!simpleName.isEmpty()) { + directParents.add(simpleName); + implementers.computeIfAbsent(simpleName, ignored -> new ArrayList<>()).add(type); + } + } + parents.put(typeName, Collections.unmodifiableSet(directParents)); + for (String parent : directParents) { + children.computeIfAbsent(parent, ignored -> new LinkedHashSet<>()).add(typeName); + } + } + + this.typesBySimpleName = Map.copyOf(typeMap); + this.attributesByOwner = copyListMap(attributes); + this.methodsByOwner = copyListMap(methods); + this.attributeOwners = Collections.unmodifiableMap(new IdentityHashMap<>(attributeOwnerMap)); + this.methodOwners = Collections.unmodifiableMap(new IdentityHashMap<>(methodOwnerMap)); + this.directParentsByType = copySetMap(parents); + this.childrenByParent = copySetMap(children); + this.implementersByInterface = copyListMap(implementers); + } + + /** Creates an index, or an empty index when {@code cd} is {@code null}. */ + public static CDModelIndex of(ASTCDCompilationUnit cd) { + if (cd == null) { + return new CDModelIndex(); + } + return new CDModelIndex(cd); + } + + public ASTCDCompilationUnit cd() { + return cd; + } + + public List types() { + return types; + } + + public List classes() { + return classes; + } + + public List interfaces() { + return interfaces; + } + + public List enums() { + return enums; + } + + public List associations() { + return associations; + } + + /** Resolves a type after reducing the supplied name to its simple name. */ + public Optional type(String name) { + return Optional.ofNullable(typesBySimpleName.get(JavaSourceNames.simpleName(name))); + } + + public boolean hasType(String name) { + return type(name).isPresent(); + } + + /** Returns attributes declared directly by the named owner; inherited attributes are excluded. */ + public List attributes(String ownerName) { + return attributesByOwner.getOrDefault(JavaSourceNames.simpleName(ownerName), List.of()); + } + + /** Resolves one directly declared attribute by owner and simple attribute name. */ + public Optional attribute(String ownerName, String attributeName) { + String simpleAttribute = JavaSourceNames.simpleName(attributeName); + return attributes(ownerName).stream() + .filter(attribute -> attribute.getName().equals(simpleAttribute)) + .findFirst(); + } + + /** Returns methods declared directly by the named owner; inherited methods are excluded. */ + public List methods(String ownerName) { + return methodsByOwner.getOrDefault(JavaSourceNames.simpleName(ownerName), List.of()); + } + + /** Returns directly declared overloads having the supplied simple method name. */ + public List methods(String ownerName, String methodName) { + String simpleMethod = JavaSourceNames.simpleName(methodName); + return methods(ownerName).stream() + .filter(method -> method.getName().equals(simpleMethod)) + .toList(); + } + + /** + * Resolves one directly declared method by its normalized {@code name(type,...)} signature. + * + *

For example, {@code method("Order", "update(java.time.Instant)")} does not match a + * zero-argument {@code update()} overload. + */ + public Optional method(String ownerName, String signature) { + String normalizedSignature = signature == null ? "" : signature.trim(); + return methods(ownerName).stream() + .filter(method -> JavaSourceNames.methodSignature(method).equals(normalizedSignature)) + .findFirst(); + } + + public Optional ownerOf(ASTCDAttribute attribute) { + return Optional.ofNullable(attributeOwners.get(attribute)); + } + + public Optional ownerOf(ASTCDMethod method) { + return Optional.ofNullable(methodOwners.get(method)); + } + + /** Returns direct superclass and interface names; the supplied type itself is not included. */ + public Set directParentNames(String typeName) { + return directParentsByType.getOrDefault(JavaSourceNames.simpleName(typeName), Set.of()); + } + + /** Returns every reachable superclass and interface name, excluding the supplied type itself. */ + public Set transitiveParentNames(String typeName) { + Set result = new LinkedHashSet<>(); + ArrayDeque queue = new ArrayDeque<>(directParentNames(typeName)); + while (!queue.isEmpty()) { + String parent = queue.removeFirst(); + if (!result.add(parent)) { + continue; + } + queue.addAll(directParentNames(parent)); + } + return Collections.unmodifiableSet(result); + } + + /** + * Returns whether {@code typeName} has {@code parentName} as a direct or transitive parent. + * Equal type names are not considered a subtype relationship by this method. + */ + public boolean isSubtypeOf(String typeName, String parentName) { + String simpleParent = JavaSourceNames.simpleName(parentName); + return directParentNames(typeName).contains(simpleParent) + || transitiveParentNames(typeName).contains(simpleParent); + } + + /** Returns names of types that directly declare the supplied parent. */ + public Set childNames(String parentName) { + return childrenByParent.getOrDefault(JavaSourceNames.simpleName(parentName), Set.of()); + } + + /** + * Returns types that directly list the supplied interface in their declaration. + * + *

This is a declaration index, not a transitive subtype query. Use {@link #isSubtypeOf} when + * indirect interface implementation must count. + */ + public List implementersOf(String interfaceName) { + return implementersByInterface.getOrDefault(JavaSourceNames.simpleName(interfaceName), List.of()); + } + + /** + * Finds the unique type that directly declares an attribute or method with this simple name. + * Returns empty both when no owner exists and when several types declare that member name. + */ + public Optional ownerOfMemberNamed(String memberName) { + String simpleMember = JavaSourceNames.simpleName(memberName); + ASTCDType owner = null; + for (ASTCDType type : types) { + boolean matches = + attributes(type.getName()).stream().anyMatch(attribute -> attribute.getName().equals(simpleMember)) + || methods(type.getName()).stream().anyMatch(method -> method.getName().equals(simpleMember)); + if (!matches) { + continue; + } + if (owner != null && owner != type) { + return Optional.empty(); + } + owner = type; + } + return Optional.ofNullable(owner); + } + + public Set typeNames() { + return Collections.unmodifiableSet(new LinkedHashSet<>(typesBySimpleName.keySet())); + } + + private static Map> copyListMap(Map> source) { + Map> copy = new LinkedHashMap<>(); + for (Map.Entry> entry : source.entrySet()) { + copy.put(entry.getKey(), List.copyOf(entry.getValue())); + } + return Map.copyOf(copy); + } + + private static Map> copySetMap(Map> source) { + Map> copy = new LinkedHashMap<>(); + for (Map.Entry> entry : source.entrySet()) { + copy.put(entry.getKey(), Set.copyOf(entry.getValue())); + } + return Map.copyOf(copy); + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDTypeRelations.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDTypeRelations.java new file mode 100644 index 00000000..c51e9eae --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/CDTypeRelations.java @@ -0,0 +1,46 @@ +package de.monticore.codeAdaption.utils; + +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import java.util.List; +import java.util.Optional; + +/** + * Provides null-safe, consistently rendered access to CD relationships whose generated AST APIs + * vary by type kind or grammar version. + */ +public final class CDTypeRelations { + + private CDTypeRelations() {} + + /** Returns the first declared superclass, preserving the CD's declaration order. */ + public static Optional firstSuperclassName(ASTCDType type) { + if (type != null && !type.getSuperclassList().isEmpty()) { + return Optional.of(printTypeReference(type.getSuperclassList().get(0))); + } + return Optional.empty(); + } + + /** Returns whether the type declares at least one superclass. */ + public static boolean hasSuperclass(ASTCDType type) { + return type != null && !type.getSuperclassList().isEmpty(); + } + + /** Returns whether the type has an explicit {@code abstract} modifier. */ + public static boolean isAbstract(ASTCDType type) { + return type != null && type.getModifier() != null && type.getModifier().isAbstract(); + } + + /** Returns directly declared interface names in declaration order. */ + public static List interfaceNames(ASTCDType type) { + if (type == null) { + return List.of(); + } + return type.getInterfaceList().stream().map(CDTypeRelations::printTypeReference).toList(); + } + + /** Renders a type reference, using {@code Object} for a missing reference. */ + public static String printTypeReference(ASTMCType typeReference) { + return typeReference != null ? JavaLoader.print(typeReference) : "Object"; + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Constants.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Constants.java index 73576355..a5989696 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Constants.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Constants.java @@ -1,6 +1,10 @@ package de.monticore.codeAdaption.utils; -public class Constants { +/** Names and placeholder syntax shared by {@link Adapt} parsing and matcher templates. */ +public final class Constants { + + private Constants() {} + public static final String TEMPLATE = "template"; public static final String REFERENCE = "ref"; public static final String ANNOT_NAME = "Adapt"; @@ -13,4 +17,5 @@ public class Constants { public static final String SIMPLE_PLACE_HOLDER = "${}"; public static final String CAP_FIRST_PLACE_HOLDER = "${" + CAP_FIRST + "}"; public static final String UNCAP_FIRST_PLACE_HOLDER = "${" + UNCAP_FIRST + "}"; + public static final String GENERATE_TEMPLATE = "genTemplate"; } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Generator.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Generator.java deleted file mode 100644 index c9a2715c..00000000 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/Generator.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.monticore.codeAdaption.utils; - -import de.monticore.CD4CodeTool; -import java.io.File; -import java.nio.file.Path; - -public class Generator { - - public static void generate(File cdFile, Path hwcPath, Path output) { - String[] input = { - "-i", - cdFile.getAbsolutePath(), - "-ct", - "cd2java.CD2Java", - "--gen", - "-hwc", - hwcPath.toString(), - "-o", - output.toString(), - "--fieldfromrole", - "navigable" - }; - CD4CodeTool.main(input); - - // delete hook-points - JavaLoader.readJavaFile(Path.of("target")).forEach(AdapterUtils::removeComments); - } -} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaLoader.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaLoader.java index 05c52bb0..d65474ca 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaLoader.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaLoader.java @@ -1,16 +1,11 @@ package de.monticore.codeAdaption.utils; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - import de.monticore.ast.ASTNode; import de.monticore.cd._symboltable.BuiltInTypes; import de.monticore.cd4code.CD4CodeMill; -import de.monticore.cd4code._cocos.CD4CodeCoCoChecker; import de.monticore.cd4code._parser.CD4CodeParser; import de.monticore.cd4code._symboltable.CD4CodeSymbolTableCompleter; import de.monticore.cd4code._symboltable.ICD4CodeArtifactScope; -import de.monticore.cd4code.cocos.CD4CodeCoCosDelegator; import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl.JavaDSLTool; @@ -23,82 +18,84 @@ import de.monticore.java.javadsl._symboltable.JavaDSLScopesGenitor; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.monticore.prettyprint.IndentPrinter; -import de.monticore.symboltable.ImportStatement; -import de.monticore.types.mcbasictypes.MCBasicTypesMill; import de.monticore.types.mcbasictypes._ast.ASTMCType; -import de.se_rwth.commons.logging.Log; + import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; -import org.junit.jupiter.api.Assertions; +/** + * File-system and parser utilities for the JavaDSL and CD4Code models used during adaptation. + * + *

Loading creates the symbol tables required by the matching and update phases. Semantic + * validation is deliberately owned by the caller: this utility does not select or execute a + * CD4Code CoCo set because different adaptation inputs permit different degrees of + * underspecification. + */ public class JavaLoader { /** - * Parses a class diagram, builds the symbol table, and checks the consistency conditions (CoCos). + * Parses a class diagram and builds the symbol table required for name and type resolution. + * + *

No CD4Code CoCos are executed. Callers that require a particular semantic profile must run + * its checker explicitly after loading. * * @param file The class diagram file to be parsed. It must have a .cd extension. * @return The resulting ASTCDCompilationUnit created from the class diagram. - * @throws AssertionError if the provided file does not have a .cd extension, or if the AST could - * not be created successfully. + * @throws IllegalArgumentException if the provided file is not a readable CD file + * @throws IllegalStateException if parsing fails */ public static ASTCDCompilationUnit loadCD(File file) { // parse the class diagram - assert file.getName().endsWith(".cd"); + requireFileExtension(file, ".cd"); CD4CodeParser cdParser = new CD4CodeParser(); Optional optCdAST = Optional.empty(); try { optCdAST = cdParser.parse(file.getAbsolutePath()); } catch (IOException e) { - System.out.println(e); - // Log.error("It was not possible to parse the class diagram " + file.getAbsolutePath()); + throw new IllegalStateException("Could not read class diagram " + file.getAbsolutePath(), e); + } + if (optCdAST.isEmpty()) { + throw new IllegalStateException("Could not parse class diagram " + file.getAbsolutePath()); } - Assertions.assertTrue(optCdAST.isPresent()); // create symbol table - createCDSymTab(optCdAST.get()); + initializeCDSymbolTable(optCdAST.get()); - // checkCoCos - CD4CodeCoCoChecker cdChecker = new CD4CodeCoCosDelegator().getCheckerForAllCoCos(); - // cdChecker.checkAll(optCdAST.get()); return optCdAST.get(); } + /** + * Parses a CD file with the same symbol-table setup used by {@link #loadCD(File)}, but without + * requiring the diagram name to match the file name. This is used by adaptation fixtures whose + * file names are scenario-oriented. + */ public static ASTCDCompilationUnit parseCD(String cdFile) { try { Optional cd = CD4CodeMill.parser().parseCDCompilationUnit(cdFile); if (cd.isPresent()) { - ICD4CodeArtifactScope as = CD4CodeMill.scopesGenitorDelegator().createFromAST(cd.get()); - as.addImports(new ImportStatement("java.lang.String", true)); - as.addImports(new ImportStatement("java.util", true)); - cd.get().accept(new CD4CodeSymbolTableCompleter(cd.get()).getTraverser()); - + initializeCDSymbolTable(cd.get()); return cd.get(); - } else { - fail("Could not parse CDs."); } - + throw new IllegalStateException("Could not parse class diagram " + cdFile); } catch (IOException e) { - fail(e.getMessage()); + throw new IllegalStateException("Could not read class diagram " + cdFile, e); } - return null; } - private static void createCDSymTab(ASTCDCompilationUnit ast) { + /** Rebuilds the symbol table for a parsed or cloned class-diagram AST. */ + public static void initializeCDSymbolTable(ASTCDCompilationUnit ast) { BuiltInTypes.addBuiltInTypes(CD4CodeMill.globalScope()); ICD4CodeArtifactScope as = CD4CodeMill.scopesGenitorDelegator().createFromAST(ast); - as.addImports(new ImportStatement("java.lang", true)); - as.addImports(new ImportStatement("java.util", true)); - CD4CodeSymbolTableCompleter c = - new CD4CodeSymbolTableCompleter( - ast.getMCImportStatementList(), MCBasicTypesMill.mCQualifiedNameBuilder().build()); + CD4CodeSymbolTableCompleter c = new CD4CodeSymbolTableCompleter(ast); ast.accept(c.getTraverser()); ast.setEnclosingScope(as); } @@ -110,15 +107,21 @@ private static void createCDSymTab(ASTCDCompilationUnit ast) { * @return The resulting ASTOrdinaryCompilationUnit created from the Java file. */ public static ASTOrdinaryCompilationUnit loadJava(File javaFile) { - assertTrue(javaFile.getName().endsWith(".java")); + requireFileExtension(javaFile, ".java"); // parse JavaDSLTool tool = new JavaDSLTool(); Optional ast; ast = Optional.ofNullable(tool.parse(javaFile.getAbsolutePath())); - assertTrue(ast.isPresent()); - assertTrue(ast.get() instanceof ASTOrdinaryCompilationUnit); + if (ast.isEmpty()) { + throw new IllegalStateException("Could not parse Java source " + javaFile.getAbsolutePath()); + } + if (!(ast.get() instanceof ASTOrdinaryCompilationUnit)) { + throw new IllegalStateException( + "Expected an ordinary Java compilation unit in " + javaFile.getAbsolutePath()); + } + ASTOrdinaryCompilationUnit ordinaryCompilationUnit = (ASTOrdinaryCompilationUnit) ast.get(); // create symbol table IJavaDSLGlobalScope globalScope = JavaDSLMill.globalScope(); @@ -128,10 +131,10 @@ public static ASTOrdinaryCompilationUnit loadJava(File javaFile) { traverser.add4JavaDSL(genitor); genitor.putOnStack(globalScope); - IJavaDSLArtifactScope artifactScope = genitor.createFromAST(ast.get()); + IJavaDSLArtifactScope artifactScope = genitor.createFromAST(ordinaryCompilationUnit); globalScope.addSubScope(artifactScope); - return (ASTOrdinaryCompilationUnit) ast.get(); + return ordinaryCompilationUnit; } /** @@ -154,9 +157,12 @@ public static String print(ASTMCType type) { public static void writeFile(Path path, String content) { try { - FileUtils.writeStringToFile(path.toFile(), content, Charset.defaultCharset(), false); + if (path.getParent() != null) { + Files.createDirectories(path.getParent()); + } + Files.writeString(path, content, StandardCharsets.UTF_8); } catch (IOException e) { - Log.error("Exception occur when writing the file " + path); + throw new IllegalStateException("Could not write file " + path, e); } } @@ -165,11 +171,13 @@ public static void writeFile(Path path, String content) { * * @param directoryPath The root directory to read. * @return A set of Java files represented as ASTOrdinaryCompilationUnit. - */ + */ public static Set readJavaCode(Path directoryPath) { + // Clear stale artifact scopes before loading a new source batch. + JavaDSLMill.globalScope().clear(); Set res = new LinkedHashSet<>(); readJavaCode(directoryPath, res); - return res.stream().map(JavaLoader::loadJava).collect(Collectors.toSet()); + return res.stream().map(JavaLoader::loadJava).collect(Collectors.toCollection(LinkedHashSet::new)); } /** @@ -185,47 +193,100 @@ public static Set readJavaFile(Path directoryPath) { } private static void readJavaCode(Path directoryPath, Set res) { - File directory = directoryPath.toFile(); - File[] files = directory.listFiles(); - - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - readJavaCode(file.toPath(), res); - } else if (file.isFile() && file.getName().endsWith(".java")) { - res.add(file); - } - } + Path normalized = directoryPath.toAbsolutePath().normalize(); + if (!Files.exists(normalized)) { + throw new IllegalArgumentException("Java source directory does not exist: " + normalized); + } + if (!Files.isDirectory(normalized)) { + throw new IllegalArgumentException("Java source path is not a directory: " + normalized); + } + try (var paths = Files.walk(normalized)) { + paths.filter(Files::isRegularFile) + .filter(path -> path.getFileName().toString().endsWith(".java")) + .sorted(Comparator.comparing(Path::toString)) + .map(Path::toFile) + .forEach(res::add); + } catch (IOException e) { + throw new IllegalStateException("Could not read Java source directory " + normalized, e); } } public static String readFileContent(File file) { - byte[] bytes = new byte[0]; try { - bytes = Files.readAllBytes(Path.of(file.getAbsolutePath())); + return Files.readString(file.toPath(), StandardCharsets.UTF_8); } catch (IOException e) { - Log.error("It was not possible to read the file " + file.getAbsolutePath()); + throw new IllegalStateException("Could not read file " + file.getAbsolutePath(), e); } - return new String(bytes); } + /** + * Pretty-prints Java ASTs into {@code codePath}, preserving package directories when present. + */ public static void printAST(Set asts, Path codePath) { for (ASTOrdinaryCompilationUnit ast : asts) { - File sourceFile = - new File(ast.get_SourcePositionStart().getFileName().orElse(codePath.toString())); + Path targetPath = outputPathFor(ast, codePath); + JavaDSLFullPrettyPrinter prettyPrinter = new JavaDSLFullPrettyPrinter(new IndentPrinter()); String output = prettyPrinter.prettyprint(ast); - JavaLoader.writeFile(sourceFile.toPath(), output); + JavaLoader.writeFile(targetPath, output); + } + } + + private static Path outputPathFor(ASTOrdinaryCompilationUnit ast, Path codePath) { + String fileName = primaryTypeFileName(ast).orElseGet(() -> sourceFileName(ast).orElse("Unknown.java")); + if (ast.isPresentPackageDeclaration()) { + Path packagePath = + Path.of( + ast.getPackageDeclaration() + .getMCQualifiedName() + .getQName() + .replace('.', File.separatorChar)); + return codePath.resolve(packagePath).resolve(fileName); } + return codePath.resolve(sourceFileName(ast).orElse(fileName)); + } + + private static Optional primaryTypeFileName(ASTOrdinaryCompilationUnit ast) { + return ast.getTypeDeclarationList().stream() + .findFirst() + .map(type -> type.getName() + ".java"); + } + + private static Optional sourceFileName(ASTOrdinaryCompilationUnit ast) { + return ast.get_SourcePositionStart() + .getFileName() + .flatMap( + fileName -> { + try { + return Optional.of(Path.of(fileName).getFileName().toString()); + } catch (InvalidPathException e) { + return Optional.empty(); + } + }); } + /** + * Deletes an output directory before adaptation writes new generated code. + */ public static void removeDirectory(Path path) { + Path normalized = path.toAbsolutePath().normalize(); + if (normalized.getParent() == null) { + throw new IllegalArgumentException("Refusing to delete filesystem root: " + normalized); + } try { - // Delete the directory and its contents - FileUtils.deleteDirectory(new File(path.toString())); - ; + FileUtils.deleteDirectory(normalized.toFile()); } catch (IOException e) { - Log.error("Failed to delete directory: " + e.getMessage()); + throw new IllegalStateException("Failed to delete directory " + normalized, e); + } + } + + private static void requireFileExtension(File file, String extension) { + Objects.requireNonNull(file, "file"); + if (!file.getName().endsWith(extension)) { + throw new IllegalArgumentException("Expected a " + extension + " file: " + file); + } + if (!file.isFile()) { + throw new IllegalArgumentException("File does not exist or is not a regular file: " + file); } } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaMethodSignatures.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaMethodSignatures.java new file mode 100644 index 00000000..d7da458a --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaMethodSignatures.java @@ -0,0 +1,97 @@ +package de.monticore.codeAdaption.utils; + +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTMCBasicGenericType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Creates method lookup keys by parsing Java type syntax instead of manually splitting nested + * generic parameter lists. + */ +public final class JavaMethodSignatures { + + private static final String PARAMETER_CONTAINER = "__MethodParameters"; + + private JavaMethodSignatures() {} + + /** + * Parses and normalizes a complete method signature. + * + *

For example, {@code + * update(java.util.Map>,int[],long)} becomes {@code + * update(Map>,int[],long)}. + * + * @param signature method name followed by a parameter-type list + * @return the normalized signature, or empty when the input is not a complete valid signature + */ + public static Optional parseNormalized(String signature) { + if (signature == null) { + return Optional.empty(); + } + String normalized = signature.trim(); + int open = normalized.indexOf('('); + int close = normalized.lastIndexOf(')'); + if (open < 1 + || close < open + || close != normalized.length() - 1 + || normalized.indexOf('(', open + 1) >= 0) { + return Optional.empty(); + } + + String methodName = normalized.substring(0, open).trim(); + if (!isMethodName(methodName)) { + return Optional.empty(); + } + String parameters = normalized.substring(open + 1, close).trim(); + if (parameters.isEmpty()) { + return Optional.of(methodName + "()"); + } + return parseParameterTypes(parameters) + .map(types -> methodName + "(" + String.join(",", types) + ")"); + } + + private static boolean isMethodName(String name) { + try { + return JavaDSLMill.parser().parse_StringMethodDeclaration("void " + name + "() {}").isPresent(); + } catch (IOException | RuntimeException ignored) { + return false; + } + } + + /** + * Parses a comma-separated parameter list by embedding it as the type arguments of a synthetic + * generic type. This lets the JavaDSL grammar, rather than string splitting, distinguish commas + * between parameters from commas inside nested generic types. + * + *

The arbitrary container name is parsed by JavaDSL as {@link ASTMCBasicGenericType}; its type + * arguments are the original method parameter types. + */ + private static Optional> parseParameterTypes(String parameters) { + try { + Optional parsed = + JavaDSLMill.parser().parse_StringMCType(PARAMETER_CONTAINER + "<" + parameters + ">"); + if (parsed.isEmpty()) { + return Optional.empty(); + } + if (!(parsed.get() instanceof ASTMCBasicGenericType basicGeneric)) { + return Optional.empty(); + } + List arguments = basicGeneric.getMCTypeArgumentList(); + List types = new ArrayList<>(); + for (ASTMCTypeArgument argument : arguments) { + if (argument.getMCTypeOpt().isEmpty()) { + return Optional.empty(); + } + types.add(JavaSourceNames.printNormalizedType(argument.getMCTypeOpt().get())); + } + return Optional.of(List.copyOf(types)); + } catch (IOException | RuntimeException ignored) { + return Optional.empty(); + } + } +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaSourceNames.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaSourceNames.java new file mode 100644 index 00000000..df4bf4b6 --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaSourceNames.java @@ -0,0 +1,518 @@ +package de.monticore.codeAdaption.utils; + +import de.monticore.cd4codebasis._ast.ASTCDMethod; +import de.monticore.cd4codebasis._ast.ASTCDParameter; +import de.monticore.cdbasis._ast.ASTCDAttribute; +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.types.mcarraytypes._ast.ASTMCArrayType; +import de.monticore.types.mcbasictypes._ast.ASTMCPrimitiveType; +import de.monticore.types.mcbasictypes._ast.ASTMCQualifiedType; +import de.monticore.types.mcbasictypes._ast.ASTMCReturnType; +import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.monticore.types.mccollectiontypes._ast.ASTMCGenericType; +import de.monticore.types.mccollectiontypes._ast.ASTMCTypeArgument; +import de.monticore.types.mcfullgenerictypes._ast.ASTMCWildcardTypeArgument; +import java.io.IOException; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +/** + * Shared Java/CD naming and type operations with parser-first handling of qualified names, + * generics, arrays, primitives, wildcards, and the CD placeholder type {@code any}. + */ +public final class JavaSourceNames { + + private JavaSourceNames() {} + + /** + * Extracts the simple leaf name from a Java type, qualified name, or method-like value. + * + * @return an empty string for {@code null}; otherwise the parser-derived or conservative fallback + * simple name + */ + public static String simpleName(String name) { + if (name == null) { + return ""; + } + String trimmed = beforeParameterList(name.trim()); + if (trimmed.isEmpty()) { + return ""; + } + return parseTypeKey(trimmed) + .map(TypeKey::simpleName) + .orElseGet(() -> fallbackSimpleName(trimmed)); + } + + /** Alias for {@link #simpleName(String)} used when the input is known to be a type. */ + public static String simpleTypeName(String typeName) { + return simpleName(typeName); + } + + /** + * Normalizes a printed Java/CD type to simple type names while preserving generic and array + * structure and translating {@code any} to {@code Object}. + */ + public static String normalizeType(String type) { + String normalized = type == null ? "" : type.trim(); + if (normalized.isBlank()) { + return "Object"; + } + String rendered = parseTypeKey(normalized) + .map(TypeKey::normalized) + .orElseGet(() -> fallbackNormalize(normalized)); + return rendered; + } + + /** Renders and normalizes a non-void MontiCore type AST. */ + public static String printNormalizedType(ASTMCType type) { + return TypeKey.from(type).normalized(); + } + + /** Renders a non-void MontiCore type AST while preserving qualified names. */ + public static String printQualifiedType(ASTMCType type) { + return TypeKey.from(type).render(false, false, false); + } + + /** Renders a return type, including {@code void}, while preserving qualified names. */ + public static String printQualifiedReturnType(ASTCDMethod method) { + return TypeKey.from(method.getMCReturnType()).render(false, false, false); + } + + /** + * Parses and deterministically renders a type without discarding package qualification. + * Malformed input is returned trimmed so callers can reject it without guessing. + */ + public static String canonicalType(String type) { + if (type == null || type.isBlank()) { + return "Object"; + } + String trimmed = type.trim(); + return parseTypeKey(trimmed) + .map(key -> key.render(false, false, false)) + .orElse(trimmed); + } + + /** Renders the normalized type of a CD attribute. */ + public static String printNormalizedFieldType(ASTCDAttribute attribute) { + return printNormalizedType(attribute.getMCType()); + } + + /** Renders the normalized return type, including {@code void}, of a CD method. */ + public static String printNormalizedReturnType(ASTCDMethod method) { + return TypeKey.from(method.getMCReturnType()).normalized(); + } + + /** Returns an owner-independent {@code name(type,...)} lookup key for a CD method. */ + public static String methodSignature(ASTCDMethod method) { + List parameters = new ArrayList<>(); + for (ASTCDParameter parameter : method.getCDParameterList()) { + parameters.add(printNormalizedType(parameter.getMCType())); + } + return method.getName() + "(" + String.join(",", parameters) + ")"; + } + + /** Uppercases the first character without changing the remainder. */ + public static String capitalize(String value) { + if (value == null || value.isEmpty()) { + return value; + } + return Character.toUpperCase(value.charAt(0)) + value.substring(1); + } + + /** Lowercases the first character without changing the remainder. */ + public static String uncapitalize(String value) { + if (value == null || value.isEmpty()) { + return value; + } + return Character.toLowerCase(value.charAt(0)) + value.substring(1); + } + + /** Returns the number of path segments in a normalized source file name. */ + public static int pathDepth(String fileName) { + if (fileName == null || fileName.isBlank()) { + return 0; + } + try { + return Path.of(fileName).getNameCount(); + } catch (InvalidPathException ignored) { + return fileName.replace('\\', '/').split("/").length; + } + } + + /** + * Rewrites simple type-name leaves inside a printed Java type while preserving parsed generic and + * array structure. The mapper receives each leaf simple name and can return a replacement. + */ + public static String replaceSimpleTypeNames( + String rawType, Function> replacementForSimpleName) { + return replaceTypeNames( + rawType, reference -> replacementForSimpleName.apply(reference.simpleName())); + } + + /** + * Rewrites named type leaves while exposing their original qualification. This is the preferred + * operation for import-aware resolution because an already-qualified leaf must not be rebound by + * an unrelated same-simple-name import. + */ + public static String replaceTypeNames( + String rawType, Function> replacementForType) { + if (rawType == null || rawType.isEmpty()) { + return rawType; + } + Optional parsed = parseTypeKey(rawType.trim()); + if (parsed.isEmpty()) { + String trimmed = rawType.trim(); + Optional replacement = + replacementForType.apply( + new TypeReferenceName(trimmed, fallbackSimpleName(trimmed), trimmed.contains("."))); + return replacement.orElse(rawType); + } + RewriteResult rewritten = parsed.get().rewrite(replacementForType); + return rewritten.changed() ? rewritten.type().render(false, true, false) : rawType; + } + + /** Describes one named leaf in a parsed Java type without discarding qualification. */ + public record TypeReferenceName(String originalName, String simpleName, boolean qualified) {} + + /** + * Extracts named leaves from a type, including nested generic arguments and wildcard bounds. + * Arrays retain the identity of their component type. + */ + public static List typeReferences(String rawType) { + if (rawType == null || rawType.isBlank()) { + return List.of(); + } + Optional parsed = parseTypeKey(rawType.trim()); + if (parsed.isEmpty()) { + String fallback = fallbackSimpleName(rawType); + return fallback.isBlank() + ? List.of() + : List.of(new TypeReferenceName(rawType.trim(), fallback, rawType.contains("."))); + } + List references = new ArrayList<>(); + parsed.get().collectReferences(references); + return List.copyOf(references); + } + + /** Parser-derived erasure information for classpath assignability checks. */ + public record ErasedType(String name, int arrayDimensions, List typeArguments) {} + + /** + * Returns the outer named type, array rank, and canonical generic arguments without guessing for + * malformed input. + */ + public static Optional erasedType(String rawType) { + if (rawType == null || rawType.isBlank()) { + return Optional.empty(); + } + return parseTypeKey(rawType.trim()) + .map( + key -> + new ErasedType( + key.name(), + key.arrayDimensions(), + key.arguments().stream() + .map(argument -> argument.render(false, false)) + .toList())); + } + + private static Optional parseTypeKey(String rawType) { + try { + Optional returnType = + JavaDSLMill.parser().parse_StringMCReturnType(rawType); + if (returnType.isPresent()) { + return Optional.of(TypeKey.from(returnType.get())); + } + } catch (IOException | RuntimeException ignored) { + // Fall through to the Java type parser. + } + + try { + Optional type = JavaDSLMill.parser().parse_StringMCType(rawType); + if (type.isPresent()) { + return Optional.of(TypeKey.from(type.get())); + } + } catch (IOException | RuntimeException ignored) { + } + + return Optional.empty(); + } + + private static String beforeParameterList(String value) { + int paren = value.indexOf('('); + return paren >= 0 ? value.substring(0, paren).trim() : value; + } + + private static String fallbackNormalize(String value) { + String compact = value.replace(" ", ""); + if ("any".equals(compact)) { + return "Object"; + } + return fallbackSimpleName(compact); + } + + /** + * Boxes primitive generic type arguments through the type parser while preserving malformed input + * unchanged. + */ + public static String boxPrimitiveTypeArguments(String rendered) { + return parseTypeKey(rendered == null ? "" : rendered.trim()) + .map(TypeKey::normalized) + .orElse(rendered); + } + + private static String fallbackSimpleName(String value) { + // This path is deliberately conservative and runs only when JavaDSL cannot parse the input. + String simple = value.trim(); + while (simple.endsWith("[]")) { + simple = simple.substring(0, simple.length() - 2).trim(); + } + int generic = simple.indexOf('<'); + if (generic >= 0) { + simple = simple.substring(0, generic).trim(); + } + int dot = simple.lastIndexOf('.'); + return dot < 0 ? simple : simple.substring(dot + 1).trim(); + } + + private record TypeKey(String name, List arguments, int arrayDimensions) { + + private static TypeKey from(ASTMCReturnType returnType) { + if (returnType.isPresentMCVoidType()) { + return named("void"); + } + return from(returnType.getMCType()); + } + + private static TypeKey from(ASTMCType type) { + if (type instanceof ASTMCArrayType arrayType) { + return from(arrayType.getMCType()).withAdditionalArrays(arrayType.getDimensions()); + } + if (type instanceof ASTMCPrimitiveType primitiveType) { + return named(primitiveType.printType()); + } + if (type instanceof de.monticore.java.javadsl._ast.ASTMCBasicGenericType basicGenericType) { + return new TypeKey( + basicGenericType.getAnnotatedNameList().stream() + .map(de.monticore.java.javadsl._ast.ASTAnnotatedName::getName) + .reduce((left, right) -> left + "." + right) + .orElse(""), + basicGenericType.getMCTypeArgumentList().stream() + .map(TypeArgumentKey::from) + .toList(), + 0); + } + if (type instanceof ASTMCGenericType genericType) { + return new TypeKey( + String.join(".", genericType.getNameList()), + genericType.getMCTypeArgumentList().stream() + .map(TypeArgumentKey::from) + .toList(), + 0); + } + if (type instanceof ASTMCQualifiedType qualifiedType) { + return named(qualifiedType.getMCQualifiedName().getQName()); + } + return named(type.printType()); + } + + private static TypeKey named(String name) { + return new TypeKey(name, List.of(), 0); + } + + private TypeKey withAdditionalArrays(int additionalArrayDimensions) { + return new TypeKey(name, arguments, arrayDimensions + additionalArrayDimensions); + } + + private String simpleName() { + return normalizeLeafName(name); + } + + private String normalized() { + return render(true, false, false); + } + + private String render(boolean normalizeNames, boolean spaced, boolean boxLeaf) { + String leafName = normalizeNames ? normalizeLeafName(name) : name; + if (boxLeaf) { + leafName = boxedPrimitiveName(leafName); + } + StringBuilder result = new StringBuilder(leafName); + if (!arguments.isEmpty()) { + String delimiter = spaced ? ", " : ","; + result.append("<"); + for (int i = 0; i < arguments.size(); i++) { + if (i > 0) { + result.append(delimiter); + } + result.append(arguments.get(i).render(normalizeNames, spaced)); + } + result.append(">"); + } + result.append("[]".repeat(arrayDimensions)); + return result.toString(); + } + + private RewriteResult rewrite( + Function> replacementForType) { + boolean changed = false; + String rewrittenName = name; + Optional replacement = + replacementForType.apply( + new TypeReferenceName(name, simpleName(), name.contains("."))); + if (replacement.isPresent()) { + rewrittenName = replacement.get(); + changed = true; + } + + List rewrittenArguments = new ArrayList<>(); + for (TypeArgumentKey argument : arguments) { + TypeArgumentRewrite rewritten = argument.rewrite(replacementForType); + rewrittenArguments.add(rewritten.argument()); + changed |= rewritten.changed(); + } + return new RewriteResult( + new TypeKey(rewrittenName, List.copyOf(rewrittenArguments), arrayDimensions), changed); + } + + private void collectReferences(List references) { + if (!name.isBlank() && !isPrimitiveOrVoid(name)) { + references.add(new TypeReferenceName(name, simpleName(), name.contains("."))); + } + for (TypeArgumentKey argument : arguments) { + argument.collectReferences(references); + } + } + + private static boolean isPrimitiveOrVoid(String value) { + return switch (value) { + case "boolean", "byte", "short", "int", "long", "float", "double", "char", "void" -> + true; + default -> false; + }; + } + + private static String boxedPrimitiveName(String value) { + return switch (value) { + case "boolean" -> "Boolean"; + case "byte" -> "Byte"; + case "short" -> "Short"; + case "int" -> "Integer"; + case "long" -> "Long"; + case "float" -> "Float"; + case "double" -> "Double"; + case "char" -> "Character"; + default -> value; + }; + } + + private static String normalizeLeafName(String value) { + if (value == null || value.isBlank() || "any".equals(value)) { + return "Object"; + } + int dot = value.lastIndexOf('.'); + String simple = dot < 0 ? value : value.substring(dot + 1); + return "any".equals(simple) ? "Object" : simple; + } + } + + private interface TypeArgumentKey { + static TypeArgumentKey from(ASTMCTypeArgument argument) { + if (argument instanceof ASTMCWildcardTypeArgument wildcard) { + if (wildcard.isPresentUpperBound()) { + return new WildcardTypeArgument(Optional.of(TypeKey.from(wildcard.getUpperBound())), true); + } + if (wildcard.isPresentLowerBound()) { + return new WildcardTypeArgument( + Optional.of(TypeKey.from(wildcard.getLowerBound())), false); + } + return new WildcardTypeArgument(Optional.empty(), true); + } + return argument + .getMCTypeOpt() + .map(type -> new ConcreteTypeArgument(TypeKey.from(type))) + .orElseGet(() -> new UnknownTypeArgument(argument.printType())); + } + + String render(boolean normalizeNames, boolean spaced); + + TypeArgumentRewrite rewrite( + Function> replacementForType); + + void collectReferences(List references); + } + + private record ConcreteTypeArgument(TypeKey type) implements TypeArgumentKey { + @Override + public String render(boolean normalizeNames, boolean spaced) { + return type.render(normalizeNames, spaced, true); + } + + @Override + public TypeArgumentRewrite rewrite( + Function> replacementForType) { + RewriteResult rewritten = type.rewrite(replacementForType); + return new TypeArgumentRewrite(new ConcreteTypeArgument(rewritten.type()), rewritten.changed()); + } + + @Override + public void collectReferences(List references) { + type.collectReferences(references); + } + } + + private record WildcardTypeArgument(Optional bound, boolean upper) + implements TypeArgumentKey { + @Override + public String render(boolean normalizeNames, boolean spaced) { + if (bound.isEmpty()) { + return "?"; + } + String separator = spaced ? (upper ? " extends " : " super ") : (upper ? "extends" : "super"); + return "?" + separator + bound.get().render(normalizeNames, spaced, true); + } + + @Override + public TypeArgumentRewrite rewrite( + Function> replacementForType) { + if (bound.isEmpty()) { + return new TypeArgumentRewrite(this, false); + } + RewriteResult rewritten = bound.get().rewrite(replacementForType); + return new TypeArgumentRewrite( + new WildcardTypeArgument(Optional.of(rewritten.type()), upper), rewritten.changed()); + } + + @Override + public void collectReferences(List references) { + bound.ifPresent(type -> type.collectReferences(references)); + } + } + + private record UnknownTypeArgument(String raw) implements TypeArgumentKey { + @Override + public String render(boolean normalizeNames, boolean spaced) { + return normalizeNames ? normalizeType(raw) : raw; + } + + @Override + public TypeArgumentRewrite rewrite( + Function> replacementForType) { + String rewritten = replaceTypeNames(raw, replacementForType); + return new TypeArgumentRewrite(new UnknownTypeArgument(rewritten), !rewritten.equals(raw)); + } + + @Override + public void collectReferences(List references) { + references.addAll(typeReferences(raw)); + } + } + + private record RewriteResult(TypeKey type, boolean changed) {} + + private record TypeArgumentRewrite(TypeArgumentKey argument, boolean changed) {} +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaSourcePostProcessor.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaSourcePostProcessor.java new file mode 100644 index 00000000..d298370b --- /dev/null +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/JavaSourcePostProcessor.java @@ -0,0 +1,337 @@ +package de.monticore.codeAdaption.utils; + +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTCompilationUnit; +import de.monticore.java.javadsl._ast.ASTImportDeclaration; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.se_rwth.commons.logging.Log; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** Import cleanup for generated Java sources, backed by JavaDSL source positions. */ +public final class JavaSourcePostProcessor { + + private JavaSourcePostProcessor() {} + + public static void processDirectory(Path codePath) throws IOException { + List javaFiles; + try (var paths = Files.walk(codePath)) { + javaFiles = + paths + .filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .sorted() + .toList(); + } + + Map states = new LinkedHashMap<>(); + for (Path javaFile : javaFiles) { + String content = Files.readString(javaFile, StandardCharsets.UTF_8); + SourceState state = removeInvalidImports(content); + states.put(javaFile, state); + } + + for (Path javaFile : javaFiles) { + SourceState state = states.get(javaFile); + String processed = process(state.content(), javaFile.getFileName().toString()); + String original = Files.readString(javaFile, StandardCharsets.UTF_8); + if (!processed.equals(original)) { + Files.writeString(javaFile, processed, StandardCharsets.UTF_8); + } + } + } + + public static String process(String content) { + return process(content, primaryTypeFileName(content)); + } + + public static String process(String content, String fileName) { + content = normalizeJavaSource(content); + Optional ast = parseOrdinaryCompilationUnit(content); + if (ast.isEmpty()) { + Log.warn("Could not parse generated Java source for structured import cleanup."); + return content; + } + + List invalidImports = knownInvalidImports(ast.get()); + if (invalidImports.isEmpty()) { + return content; + } + return applyImportEdits(content, invalidImports, ast.get()); + } + + private static SourceState removeInvalidImports(String content) { + content = normalizeJavaSource(content); + Optional ast = parseOrdinaryCompilationUnit(content); + if (ast.isEmpty()) { + Log.warn("Could not parse generated Java source before import cleanup."); + return new SourceState(content); + } + + List invalidImports = knownInvalidImports(ast.get()); + String cleaned = applyImportEdits(content, invalidImports, ast.get()); + return new SourceState(cleaned); + } + + private static Optional parseOrdinaryCompilationUnit( + String content) { + try { + Optional ast = JavaDSLMill.parser().parse_StringCompilationUnit(content); + return ast.filter(ASTOrdinaryCompilationUnit.class::isInstance) + .map(ASTOrdinaryCompilationUnit.class::cast); + } catch (IOException | RuntimeException e) { + Log.debug( + "JavaDSL could not parse generated source: " + e.getMessage(), + "JavaSourcePostProcessor"); + return Optional.empty(); + } + } + + private static String normalizeJavaSource(String content) { + return removeParameterizedImports(content); + } + + private static String removeParameterizedImports(String content) { + StringBuilder cleaned = new StringBuilder(content.length()); + LexicalState state = LexicalState.NORMAL; + int lineStart = 0; + while (lineStart < content.length()) { + int lineEnd = lineStart; + while (lineEnd < content.length() + && content.charAt(lineEnd) != '\r' + && content.charAt(lineEnd) != '\n') { + lineEnd++; + } + int nextLine = lineEnd; + if (nextLine < content.length() && content.charAt(nextLine) == '\r') { + nextLine++; + } + if (nextLine < content.length() && content.charAt(nextLine) == '\n') { + nextLine++; + } + + String line = content.substring(lineStart, lineEnd); + if (!(state == LexicalState.NORMAL && isParameterizedImportLine(line))) { + cleaned.append(content, lineStart, nextLine); + } + state = advanceLexicalState(content, lineStart, nextLine, state); + lineStart = nextLine; + } + return cleaned.toString(); + } + + private static boolean isParameterizedImportLine(String line) { + String trimmed = line.trim(); + return trimmed.startsWith("import ") + && !trimmed.startsWith("import static ") + && trimmed.endsWith(";") + && trimmed.substring("import ".length(), trimmed.length() - 1).contains("<"); + } + + private static LexicalState advanceLexicalState( + String content, int start, int end, LexicalState initialState) { + LexicalState state = initialState; + for (int index = start; index < end; index++) { + char current = content.charAt(index); + char next = index + 1 < end ? content.charAt(index + 1) : '\0'; + switch (state) { + case NORMAL -> { + if (current == '/' && next == '/') { + state = LexicalState.LINE_COMMENT; + index++; + } else if (current == '/' && next == '*') { + state = LexicalState.BLOCK_COMMENT; + index++; + } else if (current == '"' + && index + 2 < end + && content.charAt(index + 1) == '"' + && content.charAt(index + 2) == '"') { + state = LexicalState.TEXT_BLOCK; + index += 2; + } else if (current == '"') { + state = LexicalState.STRING; + } else if (current == '\'') { + state = LexicalState.CHARACTER; + } + } + case LINE_COMMENT -> { + if (current == '\r' || current == '\n') { + state = LexicalState.NORMAL; + } + } + case BLOCK_COMMENT -> { + if (current == '*' && next == '/') { + state = LexicalState.NORMAL; + index++; + } + } + case STRING -> { + if (current == '\\') { + index++; + } else if (current == '"' || current == '\r' || current == '\n') { + state = LexicalState.NORMAL; + } + } + case CHARACTER -> { + if (current == '\\') { + index++; + } else if (current == '\'' || current == '\r' || current == '\n') { + state = LexicalState.NORMAL; + } + } + case TEXT_BLOCK -> { + if (current == '"' + && index + 2 < end + && content.charAt(index + 1) == '"' + && content.charAt(index + 2) == '"' + && !isEscaped(content, index)) { + state = LexicalState.NORMAL; + index += 2; + } + } + } + } + return state; + } + + private static boolean isEscaped(String content, int index) { + int backslashes = 0; + for (int cursor = index - 1; cursor >= 0 && content.charAt(cursor) == '\\'; cursor--) { + backslashes++; + } + return backslashes % 2 != 0; + } + + private enum LexicalState { + NORMAL, + LINE_COMMENT, + BLOCK_COMMENT, + STRING, + CHARACTER, + TEXT_BLOCK + } + + private static List knownInvalidImports(ASTOrdinaryCompilationUnit ast) { + return ast.getImportDeclarationList().stream() + .filter(JavaSourcePostProcessor::isKnownInvalidImport) + .toList(); + } + + private static boolean isKnownInvalidImport(ASTImportDeclaration importDeclaration) { + if (importDeclaration.isStatic()) { + return false; + } + String imported = importDeclaration.getMCQualifiedName().getQName(); + return Constants.ANNOT_PACKAGE.equals(imported) || !imported.contains("."); + } + + private static String primaryTypeFileName(String content) { + return parseOrdinaryCompilationUnit(content) + .flatMap(JavaSourcePostProcessor::primaryTypeName) + .map(name -> name + ".java") + .orElse("Generated.java"); + } + + private static Optional primaryTypeName(ASTOrdinaryCompilationUnit ast) { + return ast.getTypeDeclarationList().stream().findFirst().map(ASTTypeDeclaration::getName); + } + + private static String applyImportEdits( + String content, + List importsToRemove, + ASTOrdinaryCompilationUnit ast) { + if (importsToRemove.isEmpty()) { + return content; + } + + SourceDocument document = new SourceDocument(content); + List edits = new ArrayList<>(); + for (ASTImportDeclaration importToRemove : importsToRemove) { + int line = importToRemove.get_SourcePositionStart().getLine(); + if (line > 0) { + edits.add(new SourceEdit(document.lineStart(line), document.lineEndIncludingEnding(line), "")); + } + } + + return document.apply(edits); + } + + private record SourceState(String content) {} + + private record SourceEdit(int start, int end, String replacement) {} + + private static final class SourceDocument { + private final String content; + private final List lineStarts; + private final String lineSeparator; + + private SourceDocument(String content) { + this.content = content; + this.lineStarts = lineStarts(content); + this.lineSeparator = detectLineSeparator(content); + } + + private int lineStart(int line) { + if (line <= 1) { + return 0; + } + if (line > lineStarts.size()) { + return content.length(); + } + return lineStarts.get(line - 1); + } + + private int lineEndIncludingEnding(int line) { + if (line < lineStarts.size()) { + return lineStarts.get(line); + } + return content.length(); + } + + private String lineSeparator() { + return lineSeparator; + } + + private String apply(List edits) { + StringBuilder result = new StringBuilder(content); + edits.stream() + .sorted( + Comparator.comparingInt(SourceEdit::start) + .thenComparingInt(SourceEdit::end) + .reversed()) + .forEach(edit -> result.replace(edit.start(), edit.end(), edit.replacement())); + return result.toString(); + } + + private static List lineStarts(String content) { + List starts = new ArrayList<>(); + starts.add(0); + for (int i = 0; i < content.length(); i++) { + if (content.charAt(i) == '\n') { + starts.add(i + 1); + } + } + return starts; + } + + private static String detectLineSeparator(String content) { + int newline = content.indexOf('\n'); + if (newline > 0 && content.charAt(newline - 1) == '\r') { + return "\r\n"; + } + return "\n"; + } + } + +} diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/AnnotationRemover.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/AnnotationRemover.java index 2dbfc218..ae80593a 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/AnnotationRemover.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/AnnotationRemover.java @@ -11,7 +11,6 @@ import de.monticore.statements.mccommonstatements._ast.ASTJavaModifier; import de.monticore.statements.mccommonstatements._visitor.MCCommonStatementsVisitor2; import de.monticore.statements.mcstatementsbasis._ast.ASTMCModifier; -import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; import de.monticore.statements.mcvardeclarationstatements._visitor.MCVarDeclarationStatementsVisitor2; import java.util.List; import java.util.Optional; diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/JavaAstElemCollector.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/JavaAstElemCollector.java index e5883a06..9f1d4fcc 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/JavaAstElemCollector.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/utils/visitors/JavaAstElemCollector.java @@ -4,6 +4,7 @@ import de.monticore.java.javadsl._ast.*; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.monticore.java.javadsl._visitor.JavaDSLVisitor2; +import de.monticore.javalight._ast.ASTFormalParameterListing; import de.monticore.javalight._ast.ASTMethodDeclaration; import de.monticore.javalight._visitor.JavaLightVisitor2; import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; @@ -11,38 +12,64 @@ import de.monticore.types.mcbasictypes._ast.ASTMCType; import java.util.*; -/*** - * this visitor collect Type, field,methods, local-variable and formal parameters - * from a ASTOrdinaryCompilationUnit +/** + * Collects top-level types and their fields, methods, supertypes, parameters, and local variables + * from an {@link ASTOrdinaryCompilationUnit}. + * + *

For example, in {@code void visit(Person input) { for (Person person : people) {} }}, {@code + * input} is collected as a method parameter and enhanced-for variable {@code person} as a local + * variable, although JavaDSL represents both with {@code ASTFormalParameter} nodes. */ - public class JavaAstElemCollector implements JavaDSLVisitor2 { private final List typeDeclarations = new ArrayList<>(); private final Map typeElements = new LinkedHashMap<>(); + private int typeNestingDepth; @Override public void visit(ASTClassDeclaration node) { - TypeElementCollector collector = visitType(node); - - collector.supertypesDeclarations.addAll(node.getImplementedInterfaceList()); - if (node.isPresentSuperClass()) { - collector.supertypesDeclarations.add(node.getSuperClass()); + if (typeNestingDepth == 0) { + TypeElementCollector collector = visitType(node); + collector.supertypesDeclarations.addAll(node.getImplementedInterfaceList()); + if (node.isPresentSuperClass()) { + collector.supertypesDeclarations.add(node.getSuperClass()); + } + typeElements.put(node, collector); } + typeNestingDepth++; + } - typeElements.put(node, collector); + @Override + public void endVisit(ASTClassDeclaration node) { + typeNestingDepth--; } @Override public void visit(ASTInterfaceDeclaration node) { - TypeElementCollector collector = visitType(node); - collector.supertypesDeclarations.addAll(node.getExtendedInterfaceList()); - typeElements.put(node, collector); + if (typeNestingDepth == 0) { + TypeElementCollector collector = visitType(node); + collector.supertypesDeclarations.addAll(node.getExtendedInterfaceList()); + typeElements.put(node, collector); + } + typeNestingDepth++; + } + + @Override + public void endVisit(ASTInterfaceDeclaration node) { + typeNestingDepth--; } @Override public void visit(ASTEnumDeclaration node) { - TypeElementCollector collector = visitType(node); - typeElements.put(node, collector); + if (typeNestingDepth == 0) { + TypeElementCollector collector = visitType(node); + typeElements.put(node, collector); + } + typeNestingDepth++; + } + + @Override + public void endVisit(ASTEnumDeclaration node) { + typeNestingDepth--; } private TypeElementCollector visitType(ASTTypeDeclaration node) { @@ -111,14 +138,58 @@ class TypeElementCollector implements JavaDSLVisitor2, JavaLightVisitor2 { Map> localVarsMap = new LinkedHashMap<>(); Map> formalParamsMap = new LinkedHashMap<>(); + private int typeNestingDepth; + + @Override + public void visit(ASTClassDeclaration node) { + typeNestingDepth++; + } + + @Override + public void endVisit(ASTClassDeclaration node) { + typeNestingDepth--; + } + + @Override + public void visit(ASTInterfaceDeclaration node) { + typeNestingDepth++; + } + + @Override + public void endVisit(ASTInterfaceDeclaration node) { + typeNestingDepth--; + } + + @Override + public void visit(ASTEnumDeclaration node) { + typeNestingDepth++; + } + + @Override + public void endVisit(ASTEnumDeclaration node) { + typeNestingDepth--; + } @Override public void visit(ASTMethodDeclaration node) { + if (typeNestingDepth != 1) { + return; + } methodDeclarations.add(node); List localVars = new ArrayList<>(); List formalParams = new ArrayList<>(); + // Collect method signature parameters directly from the method declaration. + // This avoids collecting for-each loop variables (which are also ASTFormalParameter + // in the grammar) as formal parameters — they should be treated as local variables. + Set signatureParams = new HashSet<>(); + if (node.getFormalParameters().isPresentFormalParameterListing()) { + ASTFormalParameterListing paramListing = node.getFormalParameters().getFormalParameterListing(); + formalParams.addAll(paramListing.getFormalParameterList()); + signatureParams.addAll(paramListing.getFormalParameterList()); + } + JavaDSLTraverser traverser = JavaDSLMill.traverser(); JavaDSLVisitor2 localVarCollector = @@ -129,23 +200,49 @@ public void visit(ASTLocalVariableDeclaration node) { } }; - MCCommonStatementsVisitor2 formalParamVisitor = + // For-each loop variables are ASTFormalParameter nodes in the method body. + // Collect them as local variables instead of formal parameters, + // since they have no CD-level parameter mapping. + // Filter out method signature parameters to avoid double-counting. + MCCommonStatementsVisitor2 forEachVarCollector = new MCCommonStatementsVisitor2() { @Override public void visit(ASTFormalParameter node) { - formalParams.add(node); + if (!signatureParams.contains(node)) { + localVars.add(wrapAsLocalVariable(node)); + } } }; traverser.add4JavaDSL(localVarCollector); - traverser.add4MCCommonStatements(formalParamVisitor); + traverser.add4MCCommonStatements(forEachVarCollector); node.accept(traverser); localVarsMap.put(node, localVars); formalParamsMap.put(node, formalParams); } + /** + * Wraps an ASTFormalParameter like from a for-each loop as an ASTLocalVariableDeclaration + * so it can be handled by the local variable matching path instead of the formal parameter path. + */ + private ASTLocalVariableDeclaration wrapAsLocalVariable(ASTFormalParameter param) { + ASTLocalVariableDeclaration localVar = JavaDSLMill.localVariableDeclarationBuilder() + .setMCType(param.getMCType()) + .build(); + localVar.set_SourcePositionStart(param.get_SourcePositionStart()); + localVar.set_SourcePositionEnd(param.get_SourcePositionEnd()); + var variableDeclarator = + JavaDSLMill.variableDeclaratorBuilder().setDeclarator(param.getDeclarator()).build(); + variableDeclarator.set_SourcePositionStart(param.get_SourcePositionStart()); + variableDeclarator.set_SourcePositionEnd(param.get_SourcePositionEnd()); + localVar.getVariableDeclaratorList().add(variableDeclarator); + return localVar; + } + @Override public void visit(ASTFieldDeclaration node) { - fieldDeclarations.add(node); + if (typeNestingDepth == 1) { + fieldDeclarations.add(node); + } } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/CodeValidator.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/CodeValidator.java index fdd59d01..a6c1fa88 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/CodeValidator.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/CodeValidator.java @@ -29,9 +29,9 @@ import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; import de.monticore.codeAdaption.validator.cocos.OneVarInDeclaration; import de.monticore.codeAdaption.validator.cocos.ValidAnnotation; -import de.monticore.codeAdaption.validator.cocos.ValidTemplate; import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl._ast.ASTFieldDeclaration; +import de.monticore.java.javadsl._ast.ASTImportDeclaration; import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; import de.monticore.java.javadsl._ast.ASTTypeDeclaration; @@ -39,23 +39,81 @@ import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.monticore.javalight._ast.ASTMethodDeclaration; import de.monticore.javalight._cocos.JavaLightASTAnnotationCoCo; -import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; -import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTFormalParameterCoCo; import de.monticore.types.mcbasictypes._ast.ASTMCType; +import de.se_rwth.commons.logging.Log; import java.nio.file.Path; import java.util.*; +import java.util.function.Supplier; -/*** - * this class check if a reference Code is valid for the given reference code. +/** + * Checks whether handwritten Java code can be adapted to a reference class diagram. It also + * provides the mappings that are later used to update the Java code. + * + *

The validation has three parts: + * + *

    + *
  • {@link ValidAnnotation} checks each relevant {@code @Adapt} annotation. Its template must + * exist, the number of placeholders and references must be equal, and every reference must + * exist in the reference CD. + *
  • {@link OneVarInDeclaration} rejects declarations such as {@code String first, second;}. + * The updater can only process one declared variable at a time. + *
  • Every relevant Java type, field, method, parameter, and local variable is matched to the + * reference CD. The {@link AdapterParam} settings decide whether an element without a match + * is allowed or reported as an error. + *
+ * + *

This class does not compile Java code, run all JavaDSL or CD4Code checks, or check CD + * conformance. Other parts of the application perform those tasks. */ - public class CodeValidator { + /** Defines what happens when a Java declaration has no match in the reference CD. */ + public enum ValidationPolicy { + /** Use the {@code IGNORE_NON_MATCHED_*} settings from the supplied {@link AdapterParam}s. */ + CONFIGURED, + /** + * Allow additional Java types, members, and variables that belong to an already selected + * concretization helper set. The usual annotation and declaration checks still run. If a + * declaration does have a CD match, that match is checked normally. + */ + PRESERVE_CONCRETIZATION_HELPERS + } + private final CompTypeMatcher typeMatcher; private final CompTMemberMatcher tMemberMatcher; + /** + * Checks supertypes with the configured strictness, even when other unmatched helper + * declarations are allowed. + */ + private final CompTMemberMatcher validationSupertypeMatcher; private final CompVariableMatcher variableMatcher; + /** + * Creates the matcher chains in configured precedence order. + * + * @param cd reference class diagram used to resolve Java elements + * @param params enabled matching and unmatched-element policies + */ public CodeValidator(ASTCDCompilationUnit cd, Set params) { + this(cd, params, ValidationPolicy.CONFIGURED); + } + + /** + * Creates the matchers and specifies how Java declarations without a CD match are handled. + * + *

The supplied parameter set is not changed. {@link + * ValidationPolicy#PRESERVE_CONCRETIZATION_HELPERS} may only be used after the required helper + * files have already been selected. Manual adaptation must use {@link + * ValidationPolicy#CONFIGURED}. + * + * @param cd reference class diagram used to resolve Java elements + * @param params enabled matching and unmatched-element settings + * @param validationPolicy treatment of Java declarations without a CD match + */ + public CodeValidator( + ASTCDCompilationUnit cd, Set params, ValidationPolicy validationPolicy) { + Objects.requireNonNull(params, "params"); + Objects.requireNonNull(validationPolicy, "validationPolicy"); List typeMatchers = new ArrayList<>(); List tMemberMatchers = new ArrayList<>(); List variableMatchers = new ArrayList<>(); @@ -77,19 +135,30 @@ public CodeValidator(ASTCDCompilationUnit cd, Set params) { variableMatchers.add(new InfixVariableMatcher(cd)); } - if (params.contains(IGNORE_NON_MATCHED_TYPE)) { + // Helper mode may allow additional members, but it must still reject unknown external + // supertypes. Therefore, supertypes use a separate matcher with the configured strictness. + List configuredSupertypeMatchers = new ArrayList<>(tMemberMatchers); + if (params.contains(IGNORE_NON_MATCHED_TYPE_MEMBER)) { + configuredSupertypeMatchers.add(new IgnoreTMemberMatcher()); + } else { + configuredSupertypeMatchers.add(new ErrorTMemberMatcher()); + } + + boolean preserveHelpers = + validationPolicy == ValidationPolicy.PRESERVE_CONCRETIZATION_HELPERS; + if (preserveHelpers || params.contains(IGNORE_NON_MATCHED_TYPE)) { typeMatchers.add(new IgnoreTypeMatcher()); } else { typeMatchers.add(new ErrorTypeMatcher()); } - if (params.contains(IGNORE_NON_MATCHED_TYPE_MEMBER)) { + if (preserveHelpers || params.contains(IGNORE_NON_MATCHED_TYPE_MEMBER)) { tMemberMatchers.add(new IgnoreTMemberMatcher()); } else { tMemberMatchers.add(new ErrorTMemberMatcher()); } - if (params.contains(IGNORE_NON_MATCHED_VAR)) { + if (preserveHelpers || params.contains(IGNORE_NON_MATCHED_VAR)) { variableMatchers.add(new IgnoreVariableMatcher()); } else { variableMatchers.add(new ErrorVariableMatcher()); @@ -98,39 +167,183 @@ public CodeValidator(ASTCDCompilationUnit cd, Set params) { typeMatcher = new CompTypeMatcher(typeMatchers); variableMatcher = new CompVariableMatcher(variableMatchers); tMemberMatcher = new CompTMemberMatcher(tMemberMatchers); + validationSupertypeMatcher = new CompTMemberMatcher(configuredSupertypeMatchers); tMemberMatcher.setTypeMatcher(typeMatcher); + validationSupertypeMatcher.setTypeMatcher(typeMatcher); } + /** + * Parses all reference Java files, runs the adapter CoCos, and verifies matcher coverage. + * + * @param refCD reference CD used by annotation validation + * @param refCode directory containing handwritten reference Java + * @return {@code true} if this validation run produced no new error findings + */ public boolean isValid(ASTCDCompilationUnit refCD, Path refCode) { - Set asts = readJavaCode(refCode); + Objects.requireNonNull(refCode, "refCode"); + return validate(refCD, () -> readJavaCode(refCode), null); + } + + /** + * Validates Java files that have already been parsed. + * + *

This avoids parsing the same files again after helper dependency selection. The supplied + * ASTs are read but not modified. + * + * @param refCD reference CD used by annotation validation + * @param asts pre-parsed Java compilation units to validate + * @return {@code true} if this validation run produced no new error findings + */ + public boolean isValid( + ASTCDCompilationUnit refCD, Set asts) { + Objects.requireNonNull(asts, "asts"); + return validate(refCD, () -> asts, null); + } + + /** + * Validates a selected set of top-level types against the reference CD. + * + *

All supplied source files are loaded so that references to other source types can be + * resolved. However, CD matching for types, members, variables, and supertypes is only performed + * for {@code selectedTopLevelTypeIdentities}. Annotation and declaration-shape checks still run + * for every supplied source file. + * + * @param refCD reference CD used by annotation validation + * @param asts pre-parsed Java compilation units to validate + * @param selectedTopLevelTypeIdentities package-qualified top-level identities selected for one + * concretization mapping + * @return {@code true} if this validation run produced no new error findings + */ + public boolean isValid( + ASTCDCompilationUnit refCD, + Set asts, + Set selectedTopLevelTypeIdentities) { + Objects.requireNonNull(asts, "asts"); + Objects.requireNonNull(selectedTopLevelTypeIdentities, "selectedTopLevelTypeIdentities"); + return validate(refCD, () -> asts, Set.copyOf(selectedTopLevelTypeIdentities)); + } + + private boolean validate( + ASTCDCompilationUnit refCD, + Supplier> sourceUnits, + Set selectedTopLevelTypeIdentities) { + boolean failQuickEnabled = Log.isFailQuickEnabled(); + int findingsBefore = Log.getFindings().size(); + long errorsBefore = Log.getErrorCount(); + boolean valid; + List diagnostics = List.of(); + try { + Log.enableFailQuick(false); + Set asts = sourceUnits.get(); + + // Run annotation and declaration-shape checks for every source file. These checks are also + // global when CD matching below is limited to a selected helper set. + asts.forEach(ast -> runAdapterCoCos(ast, refCD)); + + // Collect every declared type before matching. This lets the matchers recognize references + // to other supplied source files and distinguish them from unknown external types. + Set allTypes = new LinkedHashSet<>(); + Map sourceTypesByIdentity = new LinkedHashMap<>(); + Map collectors = new LinkedHashMap<>(); + for (ASTOrdinaryCompilationUnit ast : asts) { + JavaAstElemCollector collector = new JavaAstElemCollector(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(collector); + ast.accept(traverser); - // check cocos phase 1 - asts.forEach(ast -> runCoCosPhase1(ast, refCD)); + collectors.put(ast, collector); + allTypes.addAll(collector.getAllTypeDeclarations()); + collector + .getAllTypeDeclarations() + .forEach(type -> sourceTypesByIdentity.put(qualifiedTypeIdentity(ast, type), type)); + } + typeMatcher.setAllTypeDeclarations(allTypes); - // cocos phase 2 - asts.forEach(ast -> runCoCosPhase2(ast, refCD)); + // Check CD matches now that all source types are known. If a helper set was supplied, skip + // top-level types that do not belong to it. + for (Map.Entry entry : + collectors.entrySet()) { + ASTOrdinaryCompilationUnit unit = entry.getKey(); + typeMatcher.setAllTypeDeclarations(visibleSourceTypes(unit, sourceTypesByIdentity)); + checkAllMatching( + entry.getValue(), + type -> + selectedTopLevelTypeIdentities == null + || selectedTopLevelTypeIdentities.contains(qualifiedTypeIdentity(unit, type)), + selectedTopLevelTypeIdentities, + sourceTypesByIdentity.keySet()); + } + typeMatcher.setAllTypeDeclarations(allTypes); + valid = Log.getErrorCount() == errorsBefore; + if (!valid) { + diagnostics = + Log.getFindings().subList(findingsBefore, Log.getFindings().size()).stream() + .map(finding -> finding.getMsg()) + .distinct() + .toList(); + } + } finally { + if (Log.getErrorCount() > errorsBefore && Log.getFindings().size() > findingsBefore) { + Log.getFindings().subList(findingsBefore, Log.getFindings().size()).clear(); + } + Log.enableFailQuick(failQuickEnabled); + } + diagnostics.forEach(message -> Log.warn("Reference-code validation: " + message)); + return valid; + } - // check that all elements matched - Set allType = new LinkedHashSet<>(); - for (ASTOrdinaryCompilationUnit ast : asts) { + /** + * Initializes the type matcher with all declarations from the supplied compilation units. + * + *

The complete type set is needed by matchers that decide whether a type name is unique in the + * current source batch. + * + * @param javaFiles Java compilation units whose type declarations form the matching scope + */ + public void initializeTypeMatcher(Set javaFiles) { + Set allTypes = new LinkedHashSet<>(); + + for (ASTOrdinaryCompilationUnit ast : javaFiles) { JavaAstElemCollector collector = new JavaAstElemCollector(); JavaDSLTraverser traverser = JavaDSLMill.traverser(); traverser.add4JavaDSL(collector); ast.accept(traverser); - allType.addAll(collector.getAllTypeDeclarations()); - checkAllMatching(collector); + allTypes.addAll(collector.getAllTypeDeclarations()); } - typeMatcher.setAllTypeDeclarations(allType); - return true; + typeMatcher.setAllTypeDeclarations(allTypes); } + /** + * Queries every configured matcher so missing required matches are emitted before adaptation. + * Member and variable matchers are only queried when their enclosing type or method matched. + */ protected void checkAllMatching(JavaAstElemCollector collector) { + checkAllMatching(collector, type -> true, null, Set.of()); + } + private void checkAllMatching( + JavaAstElemCollector collector, + java.util.function.Predicate validateType, + Set selectedTopLevelTypeIdentities, + Set sourceTypeIdentities) { for (ASTTypeDeclaration type : collector.getAllTypeDeclarations()) { + if (!validateType.test(type)) { + continue; + } if (getMatchedType(type).isPresent()) { + collector + .getAllFSuperTypeDeclarations(type) + .forEach( + supertype -> { + if (!isQualifiedSourceSupertype(supertype, sourceTypeIdentities) + && !isSelectedQualifiedSourceSupertype( + supertype, selectedTopLevelTypeIdentities)) { + validationSupertypeMatcher.getMatchedSupertype(type, supertype); + } + }); collector.getAllFieldDeclarations(type).forEach(f -> getMatchedField(type, f)); for (ASTMethodDeclaration method : collector.getAllMethodDeclarations(type)) { @@ -147,48 +360,158 @@ protected void checkAllMatching(JavaAstElemCollector collector) { } } + /** + * Returns whether a qualified supertype is another source type in the selected helper set. + * + *

For example, in {@code class A extends helpers.Base}, the normal matcher would only compare + * the name {@code Base} with the reference CD. If dependency selection has already identified + * {@code helpers.Base} as a required source helper, it must not be reported as an unknown + * external supertype. + */ + private boolean isSelectedQualifiedSourceSupertype( + ASTMCType supertype, Set selectedTopLevelTypeIdentities) { + if (selectedTopLevelTypeIdentities == null) { + return false; + } + String printed = de.monticore.codeAdaption.utils.JavaSourceNames + .printQualifiedType(supertype) + .replace('$', '.'); + return printed.contains(".") && selectedTopLevelTypeIdentities.contains(printed); + } + + /** Returns whether a qualified supertype is declared by another supplied source unit. */ + private boolean isQualifiedSourceSupertype( + ASTMCType supertype, Set sourceTypeIdentities) { + String printed = + de.monticore.codeAdaption.utils.JavaSourceNames + .printQualifiedType(supertype) + .replace('$', '.'); + int typeArguments = printed.indexOf('<'); + String rawType = typeArguments < 0 ? printed : printed.substring(0, typeArguments); + return rawType.contains(".") && sourceTypeIdentities.contains(rawType); + } + + /** + * Returns source declarations visible from one compilation unit according to package and import + * bindings. A single-type import is authoritative for its simple name, even when that imported + * type is external to the supplied source batch. + */ + private Set visibleSourceTypes( + ASTOrdinaryCompilationUnit unit, + Map sourceTypesByIdentity) { + String packageName = + unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + Map visibleBySimpleName = new LinkedHashMap<>(); + sourceTypesByIdentity.forEach( + (identity, declaration) -> { + if (packageName.equals(packageName(identity))) { + visibleBySimpleName.putIfAbsent(declaration.getName(), declaration); + } + }); + + Set explicitBindings = new LinkedHashSet<>(); + unit.getImportDeclarationList().stream() + .filter(importDeclaration -> !importDeclaration.isStatic()) + .filter(importDeclaration -> !importDeclaration.isSTAR()) + .forEach( + importDeclaration -> { + String identity = importDeclaration.getMCQualifiedName().getQName(); + String simpleName = + de.monticore.codeAdaption.utils.JavaSourceNames.simpleName(identity); + explicitBindings.add(simpleName); + visibleBySimpleName.remove(simpleName); + ASTTypeDeclaration declaration = sourceTypesByIdentity.get(identity); + if (declaration != null) { + visibleBySimpleName.put(simpleName, declaration); + } + }); + + Map> wildcardCandidates = new LinkedHashMap<>(); + unit.getImportDeclarationList().stream() + .filter(importDeclaration -> !importDeclaration.isStatic()) + .filter(ASTImportDeclaration::isSTAR) + .map(importDeclaration -> importDeclaration.getMCQualifiedName().getQName()) + .forEach( + importedPackage -> + sourceTypesByIdentity.forEach( + (identity, declaration) -> { + if (importedPackage.equals(packageName(identity)) + && !explicitBindings.contains(declaration.getName())) { + wildcardCandidates + .computeIfAbsent( + declaration.getName(), ignored -> new LinkedHashSet<>()) + .add(declaration); + } + })); + wildcardCandidates.forEach( + (simpleName, candidates) -> { + if (!visibleBySimpleName.containsKey(simpleName) && candidates.size() == 1) { + visibleBySimpleName.put(simpleName, candidates.iterator().next()); + } + }); + return new LinkedHashSet<>(visibleBySimpleName.values()); + } + + private static String packageName(String qualifiedTypeIdentity) { + int separator = qualifiedTypeIdentity.lastIndexOf('.'); + return separator < 0 ? "" : qualifiedTypeIdentity.substring(0, separator); + } + + /** Builds the package-qualified top-level type name used by dependency selection. */ + private String qualifiedTypeIdentity( + ASTOrdinaryCompilationUnit unit, ASTTypeDeclaration type) { + String packageName = + unit.isPresentPackageDeclaration() + ? unit.getPackageDeclaration().getMCQualifiedName().getQName() + : ""; + return packageName.isBlank() ? type.getName() : packageName + "." + type.getName(); + } + + /** Returns the configured matching for a Java method, if a matcher accepted it. */ public Optional getMatchedMethod( ASTTypeDeclaration type, ASTMethodDeclaration element) { return tMemberMatcher.getMatchedMethod(type, element); } + /** Returns the configured matching for a Java field, if a matcher accepted it. */ public Optional getMatchedField( ASTTypeDeclaration type, ASTFieldDeclaration element) { return tMemberMatcher.getMatchedField(type, element); } + /** Returns the configured matching for a declared Java supertype, if one exists. */ public Optional getMatchedSupertype(ASTTypeDeclaration type, ASTMCType supertype) { return tMemberMatcher.getMatchedSupertype(type, supertype); } + /** Returns the configured matching for a Java type declaration, if a matcher accepted it. */ public Optional getMatchedType(ASTTypeDeclaration element) { return typeMatcher.getMatchedType(element); } + /** Returns the configured matching for a local variable inside the supplied method. */ public Optional getMatchedLocalVariable( ASTTypeDeclaration type, ASTMethodDeclaration method, ASTLocalVariableDeclaration element) { return variableMatcher.getMatchedLocalVariable(type, method, element); } + /** Returns the configured matching for a formal parameter inside the supplied method. */ public Optional getMatchedParameter( ASTTypeDeclaration type, ASTMethodDeclaration method, ASTFormalParameter element) { return variableMatcher.getMatchedFormalParameter(type, method, element); } - protected void runCoCosPhase1(ASTOrdinaryCompilationUnit ast, ASTCDCompilationUnit refCD) { + /** + * Runs only the adapter-specific annotation and single-variable-declaration CoCos on one Java + * compilation unit. + */ + protected void runAdapterCoCos(ASTOrdinaryCompilationUnit ast, ASTCDCompilationUnit refCD) { JavaDSLCoCoChecker checker = new JavaDSLCoCoChecker(); checker.addCoCo((JavaLightASTAnnotationCoCo) new ValidAnnotation(refCD)); // implicitly also adds CoCo to JavaDSLASTLocalVariableDeclaration checker.addCoCo((JavaDSLASTFieldDeclarationCoCo) new OneVarInDeclaration()); checker.checkAll(ast); } - - protected void runCoCosPhase2(ASTOrdinaryCompilationUnit ast, ASTCDCompilationUnit refCD) { - JavaDSLCoCoChecker checker = new JavaDSLCoCoChecker(); - // implicitly also adds CoCo to JavaDSLASTLocalVariableDeclaration - checker.addCoCo((JavaDSLASTTypeDeclarationCoCo) new ValidTemplate(refCD)); - checker.addCoCo((JavaLightASTMethodDeclarationCoCo) new ValidTemplate(refCD)); - checker.addCoCo((MCCommonStatementsASTFormalParameterCoCo) new ValidTemplate(refCD)); - checker.checkAll(ast); - } } diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/cocos/ValidAnnotation.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/cocos/ValidAnnotation.java index 03669393..4af39daf 100644 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/cocos/ValidAnnotation.java +++ b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/cocos/ValidAnnotation.java @@ -1,13 +1,12 @@ package de.monticore.codeAdaption.validator.cocos; import static de.monticore.codeAdaption.utils.AdapterUtils.getPosition; -import static de.monticore.codeAdaption.utils.Constants.*; import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.codeAdaption.matcher.annotMatcher.AnnotElementCollector; +import de.monticore.codeAdaption.utils.AdaptAnnotationNames; import de.monticore.codeAdaption.utils.AdapterUtils; import de.monticore.codeAdaption.utils.Constants; -import de.monticore.codeAdaption.utils.JavaLoader; import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.monticore.javalight._ast.ASTAnnotation; @@ -37,10 +36,9 @@ public ValidAnnotation(ASTCDCompilationUnit cd) { protected String refNotFound = "0xRC001 %s Invalid annotation:the reference [%s] was not found in the class diagram [%s]"; - @Override public void check(ASTAnnotation node) { - if (JavaLoader.print(node.getAnnotationName()).endsWith(ANNOT_NAME)) { + if (AdaptAnnotationNames.matches(node.getAnnotationName().getQName())) { checkAnnotation(node); } } @@ -60,14 +58,10 @@ public void checkAnnotation(ASTMCModifier node) { Log.error(String.format(missingTemplate, pos)); } - // template argument == references - long arguments = - collector.getTemplate() == null - ? 0 - : collector.getTemplate().chars().filter(s -> (char) s == '$').count(); long references = collector.getReferences().size(); - if (arguments != collector.getReferences().size()) { - Log.error(String.format(templateArguments, pos, references, arguments)); + validateTemplateArity(collector.getTemplate(), references, pos); + if (collector.getGenTemplate() != null && !collector.getGenTemplate().isBlank()) { + validateTemplateArity(collector.getGenTemplate(), references, pos); } // reference must exist in the class diagram @@ -80,6 +74,15 @@ public void checkAnnotation(ASTMCModifier node) { } } + private void validateTemplateArity(String template, long references, String pos) { + long arguments = template == null ? 0 : extractPlaceHolders(template).size(); + // A constant template is valid and intentionally ignores its references. As soon as a + // placeholder is used, every reference must have a corresponding placeholder. + if (arguments > 0 && arguments != references) { + Log.error(String.format(templateArguments, pos, references, arguments)); + } + } + public static List extractPlaceHolders(String template) { List matches = new ArrayList<>(); Pattern pattern = Pattern.compile(Constants.PLACE_HOLDER_REGEX); diff --git a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/cocos/ValidTemplate.java b/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/cocos/ValidTemplate.java deleted file mode 100644 index 26bdb84a..00000000 --- a/ref-code-adaptation/src/main/java/de/monticore/codeAdaption/validator/cocos/ValidTemplate.java +++ /dev/null @@ -1,96 +0,0 @@ -package de.monticore.codeAdaption.validator.cocos; - -import static de.monticore.codeAdaption.matcher.MatcherHelper.*; - -import de.monticore.cdbasis._ast.ASTCDCompilationUnit; -import de.monticore.codeAdaption.matcher.CodeMatching; -import de.monticore.codeAdaption.matcher.MatcherHelper; -import de.monticore.codeAdaption.utils.AdapterUtils; -import de.monticore.java.javadsl._ast.*; -import de.monticore.java.javadsl._cocos.JavaDSLASTFieldDeclarationCoCo; -import de.monticore.java.javadsl._cocos.JavaDSLASTLocalVariableDeclarationCoCo; -import de.monticore.java.javadsl._cocos.JavaDSLASTTypeDeclarationCoCo; -import de.monticore.javalight._ast.ASTAnnotation; -import de.monticore.javalight._ast.ASTMethodDeclaration; -import de.monticore.javalight._cocos.JavaLightASTMethodDeclarationCoCo; -import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; -import de.monticore.statements.mccommonstatements._ast.ASTJavaModifier; -import de.monticore.statements.mccommonstatements._cocos.MCCommonStatementsASTFormalParameterCoCo; -import de.monticore.statements.mcstatementsbasis._ast.ASTMCModifier; -import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; -import de.se_rwth.commons.logging.Log; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -public class ValidTemplate - implements JavaDSLASTFieldDeclarationCoCo, - JavaDSLASTTypeDeclarationCoCo, - JavaLightASTMethodDeclarationCoCo, - MCCommonStatementsASTFormalParameterCoCo, - JavaDSLASTLocalVariableDeclarationCoCo { - protected ASTCDCompilationUnit cd; - - public ValidTemplate(ASTCDCompilationUnit cd) { - this.cd = cd; - } - - protected String message = - "0xRC003 %s invalid annotation template: filling template [%s] with the references " - + " produce [%s]. But expected [%s].\n" - + "allowed templates placeholder are {${},${cap_first} and ${uncap_first} }"; - - @Override - public void check(ASTMethodDeclaration node) { - Optional annotation = getInfoAnnotation(node.getMCModifierList()); - annotation.ifPresent(astJavaAnnotation -> checkTemplate(astJavaAnnotation, node.getName())); - } - - @Override - public void check(ASTFieldDeclaration node) { - String srcName = node.getVariableDeclarator(0).getDeclarator().getName(); - Optional annotation = getInfoJavaAnnot(node.getJavaModifierList()); - annotation.ifPresent(astJavaAnnotation -> checkTemplate(astJavaAnnotation, srcName)); - } - - @Override - public void check(ASTTypeDeclaration node) { - Optional annotation = Optional.empty(); - if (node instanceof ASTClassDeclaration) { - annotation = getInfoJavaAnnot(((ASTClassDeclaration) node).getJavaModifierList()); - } else if (node instanceof ASTInterfaceDeclaration) { - annotation = getInfoJavaAnnot(((ASTInterfaceDeclaration) node).getJavaModifierList()); - } else if (node instanceof ASTEnumDeclaration) { - annotation = getInfoJavaAnnot(((ASTEnumDeclaration) node).getJavaModifierList()); - } - - annotation.ifPresent(astJavaAnnotation -> checkTemplate(astJavaAnnotation, node.getName())); - } - - @Override - public void check(ASTFormalParameter node) { - Optional annotation = getInfoAnnotation(node.getMCModifierList()); - annotation.ifPresent( - astJavaAnnotation -> checkTemplate(astJavaAnnotation, node.getDeclarator().getName())); - } - - @Override - public void check(ASTLocalVariableDeclaration node) { - String srcName = node.getVariableDeclarator(0).getDeclarator().getName(); - Optional annotation = getInfoAnnotation(node.getMCModifierList()); - annotation.ifPresent(astJavaAnnotation -> checkTemplate(astJavaAnnotation, srcName)); - } - - public void checkTemplate(ASTMCModifier annotation, String sourceNAme) { - String pos = AdapterUtils.getPosition(annotation.get_SourcePositionStart()); - CodeMatching matching = mkMatchingFromAnnotation(annotation, cd); - if (matching.getTemplate() != null) { - String computedName = - MatcherHelper.fillTemplate(matching.getTemplate(), matching.getReferences()); - if (!computedName.equals(sourceNAme)) { - Log.error(String.format(message, pos, matching.getTemplate(), computedName, sourceNAme)); - } - } - } -} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/AdaptedCodeMergerTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/AdaptedCodeMergerTest.java new file mode 100644 index 00000000..e2440e5e --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/AdaptedCodeMergerTest.java @@ -0,0 +1,546 @@ +package de.monticore.codeAdaption; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._visitor.JavaDSLTraverser; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class AdaptedCodeMergerTest extends AdapterAbstractTest { + + @TempDir Path tempDir; + + private AdaptedCodeMerger merger; + + @BeforeEach + void setUp() { + initMills(); + merger = new AdaptedCodeMerger(); + } + + @Test + void preservesSameSimpleTypeNameInDifferentPackages() throws IOException { + ASTOrdinaryCompilationUnit first = + parse("a/User.java", "package a; public class User { int first; }"); + ASTOrdinaryCompilationUnit second = + parse("b/User.java", "package b; public class User { int second; }"); + + Set merged = + merger.mergeAdaptedCode(new LinkedHashSet<>(), linkedSet(second, first)); + + assertEquals(2, merged.size()); + assertEquals( + Set.of("a", "b"), + merged.stream() + .map(unit -> unit.getPackageDeclaration().getMCQualifiedName().getQName()) + .collect(java.util.stream.Collectors.toSet())); + } + + @Test + void mergesValidSameNameOverloads() throws IOException { + ASTOrdinaryCompilationUnit first = + parse("first/Service.java", "package p; public class Service { void run(String value) {} }"); + ASTOrdinaryCompilationUnit second = + parse("second/Service.java", "package p; public class Service { void run(int value) {} }"); + + ASTOrdinaryCompilationUnit merged = + merger.mergeAdaptedCode(linkedSet(first), linkedSet(second)).iterator().next(); + JavaAstElemCollector collector = collect(merged); + + assertEquals( + 2, + collector + .getAllMethodDeclarations(merged.getTypeDeclarationList().get(0)) + .size()); + } + + @Test + void mergesMethodsProducedBySeparateIncarnationPasses() throws IOException { + ASTOrdinaryCompilationUnit math = + parse( + "math/Teacher.java", + "package Concrete; class Teacher { MathDept firstMathDept(MathDept fallback) { return fallback; } }"); + ASTOrdinaryCompilationUnit science = + parse( + "science/Teacher.java", + "package Concrete; class Teacher { ScienceDept firstScienceDept(ScienceDept fallback) { return fallback; } }"); + + Set merged = + merger.mergeAdaptedCode( + merger.mergeAdaptedCode(new LinkedHashSet<>(), linkedSet(math)), + linkedSet(science)); + ASTOrdinaryCompilationUnit teacher = merged.iterator().next(); + JavaAstElemCollector collector = collect(teacher); + + assertEquals( + Set.of("firstMathDept", "firstScienceDept"), + collector.getAllMethodDeclarations(teacher.getTypeDeclarationList().get(0)).stream() + .map(de.monticore.javalight._ast.ASTMethodDeclaration::getName) + .collect(java.util.stream.Collectors.toSet())); + } + + @Test + void splitKeepsNestedTypesInsideTheirTopLevelOwner() throws IOException { + ASTOrdinaryCompilationUnit source = + parse( + "Combined.java", + "package p; class Outer { static class Inner { void nested() {} } void outer() {} } " + + "class Second {}"); + + Set split = merger.splitCompilationUnitsByType(linkedSet(source)); + + assertEquals(2, split.size()); + ASTOrdinaryCompilationUnit outer = + split.stream() + .filter(unit -> unit.getTypeDeclarationList().get(0).getName().equals("Outer")) + .findFirst() + .orElseThrow(); + assertEquals(1, outer.getTypeDeclarationList().size()); + assertTrue(JavaLoader.print(outer).contains("class Inner")); + + JavaAstElemCollector collector = collect(outer); + assertEquals(1, collector.getAllTypeDeclarations().size()); + assertEquals( + 1, + collector + .getAllMethodDeclarations(outer.getTypeDeclarationList().get(0)) + .size()); + } + + @Test + void mergesAdaptedTypeIntoUniqueConcreteTypeAcrossPackages() throws IOException { + ASTOrdinaryCompilationUnit adapted = + parse("adapter/Port.java", "package adapter; interface Port { void send(); }"); + ASTOrdinaryCompilationUnit concrete = + parse("concrete/Port.java", "package concrete; interface Port {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { interface Port; }"); + + ASTOrdinaryCompilationUnit merged = + merger + .mergeAdaptedCodeIntoConcreteBase( + linkedSet(concrete), + linkedSet(adapted), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))) + .iterator() + .next(); + + assertEquals( + "concrete", merged.getPackageDeclaration().getMCQualifiedName().getQName()); + assertTrue(JavaLoader.print(merged).contains("void send()")); + } + + @Test + void preservesCompletedAbstractnessWhenMergingIntoConcreteCode() throws IOException { + ASTOrdinaryCompilationUnit adapted = + parse( + "adapter/BankAccount.java", + "package adapter; abstract class BankAccount implements Auditable {}"); + ASTOrdinaryCompilationUnit concrete = + parse("concrete/BankAccount.java", "package concrete; class BankAccount {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { abstract class BankAccount; }"); + + ASTOrdinaryCompilationUnit merged = + merger + .mergeAdaptedCodeIntoConcreteBase( + linkedSet(concrete), + linkedSet(adapted), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))) + .stream() + .filter(unit -> unit.getTypeDeclarationList().get(0).getName().equals("BankAccount")) + .findFirst() + .orElseThrow(); + + String rendered = JavaLoader.print(merged); + assertTrue(rendered.contains("abstract class BankAccount"), rendered); + assertTrue(rendered.contains("implements Auditable"), rendered); + } + + @Test + void removesStaleAbstractnessWhenCompletedTypeIsConcrete() throws IOException { + ASTOrdinaryCompilationUnit adapted = + parse("adapter/BankAccount.java", "package adapter; class BankAccount {}"); + ASTOrdinaryCompilationUnit concrete = + parse( + "concrete/BankAccount.java", + "package concrete; public abstract class BankAccount {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class BankAccount; }"); + + ASTOrdinaryCompilationUnit merged = + merger + .mergeAdaptedCodeIntoConcreteBase( + linkedSet(concrete), + linkedSet(adapted), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))) + .iterator() + .next(); + + String rendered = JavaLoader.print(merged); + assertTrue(rendered.contains("public class BankAccount"), rendered); + assertFalse(rendered.contains("abstract class BankAccount"), rendered); + } + + @Test + void importsRelocatedTypesIntoDependentAdaptedUnits() throws IOException { + ASTOrdinaryCompilationUnit adaptedAccount = + parse("adapter/Account.java", "package adapter; class Account {}"); + ASTOrdinaryCompilationUnit adaptedTransaction = + parse( + "adapter/Transaction.java", + "package adapter; class Transaction { Account source; }"); + ASTOrdinaryCompilationUnit concreteAccount = + parse("concrete/Account.java", "package concrete; class Account {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class Account; }"); + + Set merged = + merger.mergeAdaptedCodeIntoConcreteBase( + linkedSet(concreteAccount), + linkedSet(adaptedAccount, adaptedTransaction), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))); + + ASTOrdinaryCompilationUnit transaction = + merged.stream() + .filter(unit -> unit.getTypeDeclarationList().get(0).getName().equals("Transaction")) + .findFirst() + .orElseThrow(); + assertEquals( + Set.of("concrete.Account"), + transaction.getImportDeclarationList().stream() + .map(importDeclaration -> importDeclaration.getMCQualifiedName().getQName()) + .collect(java.util.stream.Collectors.toSet())); + } + + @Test + void importsRelocatedSuperclassIntoDependentAdaptedType() throws IOException { + ASTOrdinaryCompilationUnit adaptedBase = + parse("adapter/Base.java", "package adapter; class Base {}"); + ASTOrdinaryCompilationUnit adaptedChild = + parse("adapter/Child.java", "package adapter; class Child extends Base {}"); + ASTOrdinaryCompilationUnit concreteBase = + parse("domain/Base.java", "package domain; class Base {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class Base; }"); + + Set merged = + merger.mergeAdaptedCodeIntoConcreteBase( + linkedSet(concreteBase), + linkedSet(adaptedBase, adaptedChild), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))); + + ASTOrdinaryCompilationUnit child = + merged.stream() + .filter(unit -> unit.getTypeDeclarationList().get(0).getName().equals("Child")) + .findFirst() + .orElseThrow(); + assertTrue(JavaLoader.print(child).contains("import domain.Base;")); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("memberImportRepairCases") + void repairsMemberImports(String description, MemberImportRepairCase testCase) + throws IOException { + ASTOrdinaryCompilationUnit consumer = + parse("adapter/Consumer.java", testCase.consumerSource()); + LinkedHashSet concreteUnits = new LinkedHashSet<>(); + concreteUnits.add( + parse( + "domain/" + testCase.typeName() + ".java", + "package domain; class " + testCase.typeName() + " {}")); + if (testCase.includeUnrelatedCandidate()) { + concreteUnits.add( + parse( + "other/" + testCase.typeName() + ".java", + "package other; class " + testCase.typeName() + " {}")); + } + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString( + cd, "classdiagram Concrete { class " + testCase.typeName() + "; }"); + + Set merged = + merger.mergeAdaptedCodeIntoConcreteBase( + concreteUnits, + linkedSet(consumer), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))); + + String rendered = JavaLoader.print(unitNamed(merged, "Consumer")); + testCase + .expectedFragments() + .forEach(fragment -> assertTrue(rendered.contains(fragment), rendered)); + testCase + .forbiddenFragments() + .forEach(fragment -> assertFalse(rendered.contains(fragment), rendered)); + } + + @Test + void memberImportRepairDoesNotRebindConcreteOnlyReference() throws IOException { + ASTOrdinaryCompilationUnit concreteConsumer = + parse( + "concrete/Consumer.java", + "package adapter; class Consumer { External handwritten; }"); + ASTOrdinaryCompilationUnit adaptedConsumer = + parse( + "adapter/Consumer.java", + "package adapter; class Consumer { void generated() {} }"); + ASTOrdinaryCompilationUnit domainExternal = + parse("domain/External.java", "package domain; class External {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class Consumer; class External; }"); + + Set merged = + merger.mergeAdaptedCodeIntoConcreteBase( + linkedSet(concreteConsumer, domainExternal), + linkedSet(adaptedConsumer), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))); + + String rendered = JavaLoader.print(unitNamed(merged, "Consumer")); + assertTrue(rendered.contains("External handwritten"), rendered); + assertFalse(rendered.contains("import domain.External;"), rendered); + } + + @Test + void memberImportRepairDoesNotImportForQualifiedReference() throws IOException { + ASTOrdinaryCompilationUnit consumer = + parse( + "adapter/Consumer.java", + "package adapter; class Consumer { foreign.Date createdAt; }"); + ASTOrdinaryCompilationUnit domainDate = + parse("domain/Date.java", "package domain; class Date {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class Date; }"); + + Set merged = + merger.mergeAdaptedCodeIntoConcreteBase( + linkedSet(domainDate), + linkedSet(consumer), + CDModelIndex.of(JavaLoader.parseCD(cd.toString()))); + + String rendered = JavaLoader.print(unitNamed(merged, "Consumer")); + assertTrue(rendered.contains("foreign.Date createdAt"), rendered); + assertFalse(rendered.contains("import domain.Date;"), rendered); + } + + @Test + void rejectsConflictingImportForRelocatedType() throws IOException { + ASTOrdinaryCompilationUnit adaptedAccount = + parse("adapter/Account.java", "package adapter; class Account {}"); + ASTOrdinaryCompilationUnit adaptedTransaction = + parse( + "adapter/Transaction.java", + "package adapter; import other.Account; class Transaction { Account source; }"); + ASTOrdinaryCompilationUnit concreteAccount = + parse("concrete/Account.java", "package concrete; class Account {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class Account; }"); + + assertThrows( + CodeAdaptationException.class, + () -> + merger.mergeAdaptedCodeIntoConcreteBase( + linkedSet(concreteAccount), + linkedSet(adaptedAccount, adaptedTransaction), + CDModelIndex.of(JavaLoader.parseCD(cd.toString())))); + } + + @Test + void rejectsConflictingImportsFromSeparateMappingPasses() throws IOException { + ASTOrdinaryCompilationUnit first = + parse("first/Service.java", "package p; import a.User; class Service {}"); + ASTOrdinaryCompilationUnit second = + parse("second/Service.java", "package p; import b.User; class Service {}"); + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, + () -> merger.mergeAdaptedCode(linkedSet(first), linkedSet(second))); + + assertTrue(exception.getMessage().contains("both use the type name 'User'")); + } + + @Test + void keepsStaticAndWildcardImportsInSeparateNamespaces() throws IOException { + ASTOrdinaryCompilationUnit first = + parse( + "first/Service.java", + "package p; import a.User; import a.*; class Service {}"); + ASTOrdinaryCompilationUnit second = + parse( + "second/Service.java", + "package p; import static b.User; import b.*; class Service {}"); + + ASTOrdinaryCompilationUnit merged = + merger.mergeAdaptedCode(linkedSet(first), linkedSet(second)).iterator().next(); + + assertEquals(4, merged.getImportDeclarationList().size()); + } + + @Test + void rejectsConflictingImportsWhenMergingIntoConcreteCode() throws IOException { + ASTOrdinaryCompilationUnit concrete = + parse("concrete/Service.java", "package p; import a.User; class Service {}"); + ASTOrdinaryCompilationUnit adapted = + parse("adapted/Service.java", "package p; import b.User; class Service { void run() {} }"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class Service; }"); + + assertThrows( + IllegalStateException.class, + () -> + merger.mergeAdaptedCodeIntoConcreteBase( + linkedSet(concrete), + linkedSet(adapted), + CDModelIndex.of(JavaLoader.parseCD(cd.toString())))); + } + + @Test + void mergesEnumConstantsFromAdaptedAndConcreteSources() throws IOException { + ASTOrdinaryCompilationUnit concrete = + parse("concrete/Colour.java", "package p; enum Colour { RED, BLUE }"); + ASTOrdinaryCompilationUnit adapted = + parse("adapted/Colour.java", "package p; enum Colour { RED, GREEN }"); + + ASTOrdinaryCompilationUnit merged = + merger.mergeAdaptedCode(linkedSet(concrete), linkedSet(adapted)).iterator().next(); + + assertTrue(JavaLoader.print(merged).replaceAll("\\s+", "").contains("RED,GREEN,BLUE")); + } + + @Test + void rejectsAmbiguousConcretePackageForAdaptedType() throws IOException { + ASTOrdinaryCompilationUnit adapted = + parse("adapter/Account.java", "package adapter; class Account {}"); + ASTOrdinaryCompilationUnit first = + parse("first/Account.java", "package first; class Account {}"); + ASTOrdinaryCompilationUnit second = + parse("second/Account.java", "package second; class Account {}"); + Path cd = tempDir.resolve("Concrete.cd"); + Files.writeString(cd, "classdiagram Concrete { class Account; }"); + + CodeAdaptationException exception = + assertThrows( + CodeAdaptationException.class, + () -> + merger.mergeAdaptedCodeIntoConcreteBase( + linkedSet(first, second), + linkedSet(adapted), + CDModelIndex.of(JavaLoader.parseCD(cd.toString())))); + + assertTrue(exception.getMessage().contains("multiple packages")); + } + + private static Stream memberImportRepairCases() { + return Stream.of( + Arguments.of( + "preserves unrelated explicit import", + new MemberImportRepairCase( + "Date", + "package adapter; import java.util.Date; class Consumer { Date createdAt; }", + false, + List.of("import java.util.Date;"), + List.of("import domain.Date;"))), + Arguments.of( + "accepts exact explicit import", + new MemberImportRepairCase( + "Date", + "package adapter; import domain.Date; class Consumer { Date createdAt; }", + false, + List.of("import domain.Date;"), + List.of())), + Arguments.of( + "preserves implicit java.lang binding", + new MemberImportRepairCase( + "Runnable", + "package adapter; class Consumer { Runnable action; }", + false, + List.of("Runnable action"), + List.of("import domain.Runnable;"))), + Arguments.of( + "adds explicit import under unrelated wildcard", + new MemberImportRepairCase( + "Date", + "package adapter; import java.io.*; class Consumer { Date createdAt; }", + false, + List.of("import java.io.*;", "import domain.Date;"), + List.of())), + Arguments.of( + "preserves matching platform wildcard", + new MemberImportRepairCase( + "Date", + "package adapter; import java.util.*; class Consumer { Date createdAt; }", + false, + List.of("import java.util.*;"), + List.of("import domain.Date;"))), + Arguments.of( + "accepts matching domain wildcard", + new MemberImportRepairCase( + "Date", + "package adapter; import domain.*; class Consumer { Date createdAt; }", + false, + List.of("import domain.*;"), + List.of("import domain.Date;"))), + Arguments.of( + "uses unique matching wildcard despite unrelated output candidate", + new MemberImportRepairCase( + "Date", + "package adapter; import domain.*; class Consumer { Date createdAt; }", + true, + List.of("import domain.*;"), + List.of("import domain.Date;")))); + } + + private record MemberImportRepairCase( + String typeName, + String consumerSource, + boolean includeUnrelatedCandidate, + List expectedFragments, + List forbiddenFragments) {} + + private ASTOrdinaryCompilationUnit parse(String relativePath, String source) throws IOException { + Path file = tempDir.resolve(relativePath); + Files.createDirectories(file.getParent()); + Files.writeString(file, source); + return JavaLoader.loadJava(file.toFile()); + } + + private ASTOrdinaryCompilationUnit unitNamed( + Set units, String typeName) { + return units.stream() + .filter(unit -> unit.getTypeDeclarationList().get(0).getName().equals(typeName)) + .findFirst() + .orElseThrow(); + } + + @SafeVarargs + private static LinkedHashSet linkedSet(T... values) { + return new LinkedHashSet<>(java.util.List.of(values)); + } + + private static JavaAstElemCollector collect(ASTOrdinaryCompilationUnit unit) { + JavaAstElemCollector collector = new JavaAstElemCollector(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(collector); + unit.accept(traverser); + return collector; + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/AdapterAbstractTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/AdapterAbstractTest.java index ce994ab8..27c78736 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/AdapterAbstractTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/AdapterAbstractTest.java @@ -1,8 +1,30 @@ package de.monticore.codeAdaption; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import de.monticore.cd._symboltable.BuiltInTypes; import de.monticore.cd4code.CD4CodeMill; +import de.monticore.cdconcretization.UnderspecifiedPlaceholderType; import de.monticore.java.javadsl.JavaDSLMill; import de.se_rwth.commons.logging.Log; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.tools.JavaCompiler; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; public abstract class AdapterAbstractTest { @@ -11,6 +33,161 @@ public static void initMills() { JavaDSLMill.globalScope().clear(); CD4CodeMill.init(); CD4CodeMill.globalScope().clear(); + BuiltInTypes.addBuiltInTypes(CD4CodeMill.globalScope()); + UnderspecifiedPlaceholderType.addPlaceholderType(CD4CodeMill.globalScope()); Log.init(); } + + protected static String readFileContent(Path outputPath, String filename) { + Path filePath = findFile(outputPath, filename); + assertTrue(Files.exists(filePath), () -> "Expected generated file: " + filePath); + try { + return Files.readString(filePath, StandardCharsets.UTF_8); + } catch (IOException e) { + fail("Failed to read file: " + filePath + " (" + e.getMessage() + ")"); + return ""; + } + } + + private static Path findFile(Path outputPath, String filename) { + Path direct = outputPath.resolve(filename); + if (Files.exists(direct)) { + return direct; + } + try (Stream paths = Files.walk(outputPath)) { + return paths + .filter(Files::isRegularFile) + .filter(path -> path.getFileName().toString().equals(filename)) + .findFirst() + .orElse(direct); + } catch (IOException e) { + return direct; + } + } + + protected static Set generatedFileNames(Path outputPath) { + try (Stream files = Files.walk(outputPath)) { + return files + .filter(Files::isRegularFile) + .map(path -> path.getFileName().toString()) + .collect(Collectors.toSet()); + } catch (IOException e) { + fail("Failed to list generated files in " + outputPath + ": " + e.getMessage()); + return Set.of(); + } + } + + protected static List generatedJavaFiles(Path sourceDir) { + try (Stream files = Files.walk(sourceDir)) { + return files + .filter(path -> path.toString().endsWith(".java")) + .sorted() + .collect(Collectors.toCollection(ArrayList::new)); + } catch (IOException e) { + fail("Failed to list generated Java files in " + sourceDir + ": " + e.getMessage()); + return List.of(); + } + } + + protected static List generatedJavaFilesRecursively(Path sourceDir) { + assertTrue(Files.isDirectory(sourceDir), () -> "Expected Java source directory: " + sourceDir); + try (Stream files = Files.walk(sourceDir)) { + return files + .filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .sorted() + .collect(Collectors.toCollection(ArrayList::new)); + } catch (IOException e) { + fail("Failed to list generated Java files in " + sourceDir + ": " + e.getMessage()); + return List.of(); + } + } + + protected static void assertNoAdapterMetadata(Path outputPath) { + for (Path file : generatedJavaFilesRecursively(outputPath)) { + try { + String content = Files.readString(file, StandardCharsets.UTF_8); + assertFalse(content.contains("@Adapt"), () -> "Adapter annotation leaked into " + file); + assertFalse( + content.contains("de.monticore.codeAdaption.utils.Adapt"), + () -> "Adapter import leaked into " + file); + } catch (IOException e) { + fail("Failed to inspect generated Java metadata in " + file + ": " + e.getMessage()); + } + } + } + + protected static void assertGeneratedJavaCompiles(Path sourceDir) { + assertGeneratedJavaCompiles(generatedJavaFiles(sourceDir)); + } + + protected static void assertGeneratedJavaCompiles(List javaFiles) { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + assertNotNull(compiler, "Tests must run on a JDK with javax.tools.JavaCompiler available"); + + Path classesDir = + javaFiles.isEmpty() + ? Path.of("target", "adapter", "_compile") + : javaFiles.get(0).toAbsolutePath().getParent().resolve("_compile"); + try { + deleteRecursively(classesDir); + Files.createDirectories(classesDir); + StringWriter compilerOutput = new StringWriter(); + try (StandardJavaFileManager fileManager = + compiler.getStandardFileManager(null, null, StandardCharsets.UTF_8)) { + Iterable compilationUnits = + fileManager.getJavaFileObjectsFromPaths(javaFiles); + List options = + List.of( + "-d", + classesDir.toString(), + "-classpath", + System.getProperty("java.class.path")); + Boolean compiled = + compiler.getTask(compilerOutput, fileManager, null, options, null, compilationUnits) + .call(); + assertEquals( + Boolean.TRUE, + compiled, + () -> "Generated Java does not compile" + System.lineSeparator() + + compilerOutput); + } + } catch (IOException e) { + fail("Failed to compile generated Java: " + e.getMessage()); + } finally { + deleteRecursively(classesDir); + } + } + + protected static void deleteRecursively(Path path) { + if (path == null || !Files.exists(path)) { + return; + } + try (Stream paths = Files.walk(path)) { + for (Path current : paths.sorted(Comparator.reverseOrder()).toList()) { + deletePathWithRetry(current); + } + } catch (IOException e) { + fail("Failed to delete path recursively: " + path + " (" + e.getMessage() + ")"); + } + } + + protected static void deletePathWithRetry(Path path) { + IOException last = null; + for (int attempt = 0; attempt < 5; attempt++) { + try { + Files.deleteIfExists(path); + return; + } catch (IOException e) { + last = e; + try { + Thread.sleep(100L * (attempt + 1)); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + fail("Interrupted while deleting " + path + ": " + interrupted.getMessage()); + } + } + } + fail("Failed to delete path: " + path + " (" + (last == null ? "unknown" : last.getMessage()) + ")"); + } } diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/BuilderPatternAdapterTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/BuilderPatternAdapterTest.java new file mode 100644 index 00000000..4217da1a --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/BuilderPatternAdapterTest.java @@ -0,0 +1,203 @@ +package de.monticore.codeAdaption; + +import static de.monticore.cdconformance.CDConfParameter.*; +import static de.monticore.codeAdaption.utils.AdapterParam.*; +import static org.junit.jupiter.api.Assertions.*; + +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.updater.CodeUpdaterMill; +import de.monticore.codeAdaption.updater.spoonUpdater.SpoonUpdater; +import de.monticore.codeAdaption.utils.AdapterParam; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class BuilderPatternAdapterTest extends AdapterAbstractTest { + @TempDir Path temporaryDirectory; + + private final String resourcesPath = + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/"; + private final File refCD = new File(resourcesPath + "Reference.cd"); + private final File concreteCD = new File(resourcesPath + "Concrete.cd"); + private final Path concreteCodePath = Path.of(resourcesPath + "concrete"); + private final Path adapterCodePath = Path.of(resourcesPath + "adapter"); + private Path outputPath; + + private Set confParameters; + private Set adapterParams; + + @BeforeEach + public void setup() { + initMills(); + outputPath = temporaryDirectory.resolve("builder-pattern"); + deleteRecursively(outputPath); + confParameters = Set.of(NAME_MAPPING, INHERITANCE, STEREOTYPE_MAPPING, STRICT_PARAMETER_ORDER); + adapterParams = Set.of(NAME_MATCHING, ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR, IGNORE_NON_MATCHED_TYPE); + } + + @Test + @DisplayName("Builder Pattern: Adapt Builder to create PersonBuilder and TaskBuilder") + public void testBuilderPatternAdaptation() { + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + + assertDoesNotThrow( + () -> + adapter.adapt( + refCD, concreteCD, Set.of("buildPat"), adapterCodePath, concreteCodePath, outputPath)); + + assertEquals( + Set.of("PersonBuilder.java", "TaskBuilder.java", "Person.java", "Task.java"), + generatedFileNames(outputPath)); + assertNoAdapterMetadata(outputPath); + assertGeneratedJavaCompiles(outputPath); + + String personBuilderContent = simplifyGeneratedReferences(readFileContent(outputPath, "PersonBuilder.java")); + String taskBuilderContent = simplifyGeneratedReferences(readFileContent(outputPath, "TaskBuilder.java")); + + assertTrue(personBuilderContent.contains("public class PersonBuilder")); + assertTrue(personBuilderContent.contains("public PersonBuilder setName(String name)")); + assertTrue(personBuilderContent.contains("public PersonBuilder setAge(int age)")); + assertTrue(personBuilderContent.contains("public PersonBuilder setEmail(String email)")); + assertTrue(personBuilderContent.contains("public PersonBuilder setPhone(String phone)")); + assertTrue(personBuilderContent.contains("public PersonBuilder setTags(List tags)")); + assertTrue(personBuilderContent.contains("public Person build()")); + assertTrue(personBuilderContent.contains("return this;")); + + assertTrue(taskBuilderContent.contains("public class TaskBuilder")); + assertTrue(taskBuilderContent.contains("public TaskBuilder setId(int id)")); + assertTrue(taskBuilderContent.contains("public TaskBuilder setTitle(String title)")); + assertTrue(taskBuilderContent.contains("public TaskBuilder setDescription(String description)")); + assertTrue(taskBuilderContent.contains("public TaskBuilder setStatus(String status)")); + assertTrue(taskBuilderContent.contains("public TaskBuilder setDueDate(Date dueDate)")); + assertTrue(taskBuilderContent.contains("public TaskBuilder setAssignee(String assignee)")); + assertTrue(taskBuilderContent.contains("public Task build()")); + assertTrue(taskBuilderContent.contains("return this;")); + + assertTrue(personBuilderContent.contains("String nameField")); + assertTrue(personBuilderContent.contains("int ageField")); + assertTrue(personBuilderContent.contains("String emailField")); + assertTrue(personBuilderContent.contains("String phoneField")); + assertTrue(personBuilderContent.contains("List tagsField")); + + assertTrue(taskBuilderContent.contains("int idField")); + assertTrue(taskBuilderContent.contains("String titleField")); + assertTrue(taskBuilderContent.contains("String descriptionField")); + assertTrue(taskBuilderContent.contains("String statusField")); + assertTrue(taskBuilderContent.contains("Date dueDateField")); + assertTrue(taskBuilderContent.contains("String assigneeField")); + + assertTrue(personBuilderContent.contains("this.nameField = name")); + assertTrue(personBuilderContent.contains("this.ageField = age")); + + assertTrue(taskBuilderContent.contains("this.idField = id")); + assertTrue(taskBuilderContent.contains("this.titleField = title")); + + assertTrue(personBuilderContent.contains("new Person(")); + assertTrue(taskBuilderContent.contains("new Task(")); + assertTrue( + personBuilderContent.contains( + "return new Person(this.nameField, this.ageField, this.emailField, this.phoneField, this.tagsField);")); + assertTrue( + taskBuilderContent + .contains( + "return new Task(this.idField, this.titleField, this.descriptionField, this.statusField, this.dueDateField, this.assigneeField);")); + assertFalse(personBuilderContent.contains("class Builder")); + assertFalse(taskBuilderContent.contains("class Builder")); + + String personContent = simplifyGeneratedReferences(readFileContent(outputPath, "Person.java")); + String taskContent = simplifyGeneratedReferences(readFileContent(outputPath, "Task.java")); + + assertTrue(personContent.contains("public class Person")); + assertTrue(personContent.contains("String name")); + assertTrue(personContent.contains("int age")); + + assertTrue(taskContent.contains("public class Task")); + assertTrue(taskContent.contains("int id")); + assertTrue(taskContent.contains("String title")); + } + + @Test + @DisplayName("Builder Pattern: TOP separation preserves fluent public self types") + void topSeparationCompilesFluentBuilderWithConcreteHwc() throws IOException { + Path concreteWithBuilder = Files.createDirectories(temporaryDirectory.resolve("concrete")); + Files.copy(concreteCodePath.resolve("Person.java"), concreteWithBuilder.resolve("Person.java")); + Files.copy(concreteCodePath.resolve("Task.java"), concreteWithBuilder.resolve("Task.java")); + String packageName = "de.monticore.codeAdaption.evaluation.testcase_6_builder_pattern"; + Files.writeString( + concreteWithBuilder.resolve("PersonBuilder.java"), + "package " + + packageName + + "; public class PersonBuilder extends PersonBuilderTOP {" + + " public void validate() {} } "); + Files.writeString( + concreteWithBuilder.resolve("BuilderUsage.java"), + "package " + + packageName + + "; class BuilderUsage { void use() {" + + " new PersonBuilder().setName(\"Ada\").validate(); } }"); + Path topOutput = temporaryDirectory.resolve("top-output"); + + new CodeAdapter(adapterParams, confParameters) + .adaptWithTopSeparation( + refCD, + concreteCD, + Set.of("buildPat"), + adapterCodePath, + concreteWithBuilder, + topOutput); + + String personBuilderTop = readFileContent(topOutput, "PersonBuilderTOP.java"); + assertTrue(personBuilderTop.contains("return ((PersonBuilder) (this));")); + assertGeneratedJavaCompiles(topOutput); + } + + @Test + @DisplayName("Builder Pattern: configured updater provider is used across isolated passes") + public void usesConfiguredUpdaterProvider() { + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + Path millOutputPath = outputPath.resolve("mill"); + AtomicInteger createdUpdaters = new AtomicInteger(); + CodeUpdaterMill.init( + () -> { + createdUpdaters.incrementAndGet(); + return new SpoonUpdater(); + }); + + try { + assertDoesNotThrow( + () -> + adapter.adapt( + refCD, + concreteCD, + Set.of("buildPat"), + adapterCodePath, + concreteCodePath, + millOutputPath)); + + assertTrue( + createdUpdaters.get() > 1, + "Adaptation should obtain separate updater instances for independent passes"); + assertTrue(generatedFileNames(millOutputPath).contains("PersonBuilder.java")); + assertTrue(generatedFileNames(millOutputPath).contains("TaskBuilder.java")); + assertGeneratedJavaCompiles(millOutputPath); + } finally { + CodeUpdaterMill.init(); + } + } + + private static String simplifyGeneratedReferences(String content) { + return content + .replace("de.monticore.codeAdaption.evaluation.testcase_6_builder_pattern.", "") + .replace("java.lang.", "") + .replace("java.util.", ""); + } +} + diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CDConcretizationAdapterTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CDConcretizationAdapterTest.java new file mode 100644 index 00000000..003d0cd7 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CDConcretizationAdapterTest.java @@ -0,0 +1,532 @@ +package de.monticore.codeAdaption; + +import static de.monticore.cdconformance.CDConfParameter.ADAPTED_NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_CARD_RESTRICTION; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.METHOD_OVERLOADING; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.SRC_TARGET_ASSOC_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STRICT_PARAMETER_ORDER; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.testutil.CDConcretizationTestCase; +import de.monticore.codeAdaption.testutil.CDConcretizationTestCases; +import de.monticore.codeAdaption.testutil.CDConcretizationFixtureWorkspace; +import de.monticore.codeAdaption.testutil.GeneratedJavaStructureOracle; +import de.monticore.codeAdaption.utils.AdapterParam; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.se_rwth.commons.logging.LogStub; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +public class CDConcretizationAdapterTest extends AdapterAbstractTest { + + private Set confParameters; + private Set adapterParams; + + @TempDir Path temporaryDirectory; + + @BeforeEach + public void setup() { + LogStub.init(); + initMills(); + adapterParams = + Set.of(NAME_MATCHING, ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR, IGNORE_NON_MATCHED_TYPE); + } + + static Stream testCases() { + return CDConcretizationTestCases.enabledCases().stream(); + } + + static Stream expectedFailureCases() { + return CDConcretizationTestCases.expectedFailureCases().stream(); + } + + static Stream unsupportedCases() { + return CDConcretizationTestCases.unsupportedCases().stream(); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("testCases") + void adaptsWithConcretizationCompleter(CDConcretizationTestCase testCase) { + CDConcretizationTestCase materializedTestCase = + CDConcretizationFixtureWorkspace.materialize(testCase); + confParameters = defaultConformanceParams(materializedTestCase.strictParameterOrder()); + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + cleanPreviousOutput(materializedTestCase); + + assertDoesNotThrow( + () -> + adapter.adapt( + materializedTestCase.refCd().toFile(), + materializedTestCase.concCd().toFile(), + materializedTestCase.mappings(), + materializedTestCase.adapterPath(), + materializedTestCase.concretePath(), + materializedTestCase.outputPath(), + true, + true), + () -> "Adaptation failed for " + materializedTestCase.displayName()); + + Path persistedCD = persistedCD(materializedTestCase); + assertTrue( + Files.isRegularFile(persistedCD), + () -> "No concretized CD persisted for " + materializedTestCase.displayName()); + assertDoesNotThrow(() -> JavaLoader.parseCD(persistedCD.toString())); + + List javaFiles = + generatedJavaFiles(materializedTestCase, materializedTestCase.outputPath()); + assertFalse( + javaFiles.isEmpty(), + () -> "No Java output generated for " + materializedTestCase.displayName()); + assertNoWildcardJdkImports(materializedTestCase, javaFiles); + assertNoAdaptMetadata(materializedTestCase, javaFiles); + CDConcretizationTestCases.expectedOutputModel(testCase) + .ifPresentOrElse( + expected -> + GeneratedJavaStructureOracle.assertMatches( + testCase.concCd(), expected, javaFiles), + () -> + assertTrue( + CDConcretizationTestCases.noExpectedOutputReason(testCase).isPresent(), + () -> "No expected-output model or documented reason for " + testCase)); + + assertGeneratedJavaCompiles(compilationSources(javaFiles, materializedTestCase)); + } + + @Test + void persistsTheCompletedModelRatherThanTheConcreteInput() { + CDConcretizationTestCase testCase = attributeInheritanceTestCase(); + CDConcretizationTestCase materializedTestCase = + CDConcretizationFixtureWorkspace.materialize(testCase); + cleanPreviousOutput(materializedTestCase); + + new CodeAdapter( + adapterParams, + defaultConformanceParams(materializedTestCase.strictParameterOrder())) + .adapt( + materializedTestCase.refCd().toFile(), + materializedTestCase.concCd().toFile(), + materializedTestCase.mappings(), + materializedTestCase.adapterPath(), + materializedTestCase.concretePath(), + materializedTestCase.outputPath(), + true, + true); + + CDModelIndex persisted = + CDModelIndex.of(JavaLoader.parseCD(persistedCD(materializedTestCase).toString())); + assertEquals(Set.of("Person"), persisted.directParentNames("Teacher")); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("expectedFailureCases") + void rejectsInvalidConcretizationWithoutReplacingOutput(CDConcretizationTestCase testCase) + throws IOException { + assertRejectedWithoutReplacingOutput(testCase); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("unsupportedCases") + void rejectsDocumentedUpstreamLimitationWithoutReplacingOutput( + CDConcretizationTestCase testCase) throws IOException { + assertFalse(CDConcretizationTestCases.unsupportedReason(testCase).isBlank()); + assertRejectedWithoutReplacingOutput(testCase); + } + + @Test + void compilationOracleRejectsUnadaptedReferenceType() throws IOException { + CDConcretizationTestCase testCase = oracleTestCase("stale-reference"); + write(testCase.refCd(), "classdiagram Reference { class ReferenceType; }"); + write(testCase.concCd(), "classdiagram Concrete { class ConcreteType; }"); + Path adapted = + write( + testCase.outputPath().resolve("adapted/UsesReference.java"), + "package adapted; public class UsesReference { ReferenceType value; }"); + + List sources = compilationSources(List.of(adapted), testCase); + + assertFalse( + sources.stream() + .anyMatch(path -> path.getFileName().toString().equals("ReferenceType.java"))); + assertThrows(AssertionError.class, () -> assertGeneratedJavaCompiles(sources)); + } + + @ParameterizedTest(name = "adapted package {0} compiles: {1}") + @CsvSource({"wrong, false", "expected, true"}) + void compilationOracleResolvesOriginalConcreteTypeFromExpectedPackage( + String adaptedPackage, boolean compiles) throws IOException { + CDConcretizationTestCase testCase = oracleTestCase("concrete-type-" + adaptedPackage); + write(testCase.refCd(), "classdiagram Reference { class ReferenceType; }"); + write(testCase.concCd(), "classdiagram Concrete { class ConcreteType; }"); + write( + testCase.concretePath().resolve("Anchor.java"), + "package expected;\npublic class Anchor {}"); + Path adapted = + write( + testCase.outputPath().resolve(adaptedPackage + "/UsesConcrete.java"), + "package " + + adaptedPackage + + "; public class UsesConcrete { ConcreteType value; }"); + + List sources = compilationSources(List.of(adapted), testCase); + + assertTrue( + sources.stream() + .anyMatch( + path -> + path.toString() + .replace('\\', '/') + .endsWith("/expected/ConcreteType.java"))); + if (compiles) { + assertDoesNotThrow(() -> assertGeneratedJavaCompiles(sources)); + } else { + assertThrows(AssertionError.class, () -> assertGeneratedJavaCompiles(sources)); + } + } + + @Test + void structuralOracleRejectsCompilableOutputWithMissingExpectedMember() throws IOException { + Path original = temporaryDirectory.resolve("Original.cd"); + Path expected = temporaryDirectory.resolve("ExpectedOut.cd"); + write(original, "classdiagram Original { class Sample { int present; } }"); + write(expected, "classdiagram Expected { class Sample { int present; int missing; } }"); + Path generated = + write( + temporaryDirectory.resolve("generated/Sample.java"), + "public class Sample { int present; }"); + + assertThrows( + AssertionError.class, + () -> GeneratedJavaStructureOracle.assertMatches(original, expected, List.of(generated))); + } + + private void assertRejectedWithoutReplacingOutput(CDConcretizationTestCase testCase) + throws IOException { + CDConcretizationTestCase materializedTestCase = + CDConcretizationFixtureWorkspace.materialize(testCase); + confParameters = defaultConformanceParams(materializedTestCase.strictParameterOrder()); + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + cleanPreviousOutput(materializedTestCase); + Files.createDirectories(materializedTestCase.outputPath()); + Path marker = materializedTestCase.outputPath().resolve("existing-output.txt"); + Files.writeString(marker, "preserve", StandardCharsets.UTF_8); + + assertThrows( + IllegalStateException.class, + () -> + adapter.adapt( + materializedTestCase.refCd().toFile(), + materializedTestCase.concCd().toFile(), + materializedTestCase.mappings(), + materializedTestCase.adapterPath(), + materializedTestCase.concretePath(), + materializedTestCase.outputPath(), + true, + true)); + assertTrue(Files.exists(marker), "A failed run must preserve the previous output"); + assertEquals("preserve", Files.readString(marker, StandardCharsets.UTF_8)); + Path persistedCD = persistedCD(materializedTestCase); + assertTrue( + Files.isRegularFile(persistedCD), + "A failed concretization must persist its working concrete CD"); + assertDoesNotThrow(() -> JavaLoader.parseCD(persistedCD.toString())); + } + + private static Path persistedCD(CDConcretizationTestCase testCase) { + return testCase.outputPath().resolve(testCase.concCd().getFileName()); + } + + private static CDConcretizationTestCase attributeInheritanceTestCase() { + return CDConcretizationTestCases.allCases().stream() + .filter( + candidate -> + candidate + .concCd() + .toString() + .replace('\\', '/') + .endsWith("inheritance/AttributeInheritanceConc.cd")) + .findFirst() + .orElseThrow(); + } + + private static Set defaultConformanceParams(boolean strictParameterOrder) { + Set params = new LinkedHashSet<>(); + params.add(STEREOTYPE_MAPPING); + params.add(NAME_MAPPING); + params.add(SRC_TARGET_ASSOC_MAPPING); + params.add(INHERITANCE); + params.add(ALLOW_CARD_RESTRICTION); + params.add(METHOD_OVERLOADING); + params.add(ADAPTED_NAME_MAPPING); + params.add(ALLOW_ADDITIONAL_PARAMETERS); + if (strictParameterOrder) { + params.add(STRICT_PARAMETER_ORDER); + } + return params; + } + + private static void cleanPreviousOutput(CDConcretizationTestCase testCase) { + deleteOutputPath(testCase.outputPath()); + deleteOutputPath(testCase.outputPath().resolveSibling(testCase.outputPath().getFileName() + "_formatted")); + deleteOutputPath(testCase.outputPath().resolveSibling(testCase.outputPath().getFileName() + "_generated")); + deleteOutputPath( + testCase.outputPath().resolveSibling(testCase.outputPath().getFileName() + "_compile_stubs")); + } + + private static List compilationSources( + List adaptedJava, CDConcretizationTestCase testCase) { + Map sourcesByType = new LinkedHashMap<>(); + adaptedJava.forEach(path -> sourcesByType.put(sourceTypeName(path), path)); + Path concretePath = testCase.concretePath(); + if (Files.isDirectory(concretePath)) { + generatedJavaFilesRecursively(concretePath).forEach( + path -> sourcesByType.putIfAbsent(sourceTypeName(path), path)); + } + addConcreteModelStubs(testCase, sourcesByType); + return new ArrayList<>(sourcesByType.values()); + } + + private static void addConcreteModelStubs( + CDConcretizationTestCase testCase, Map sourcesByType) { + Map declarations = new LinkedHashMap<>(); + initMills(); + addModelDeclarations(JavaLoader.parseCD(testCase.concCd().toString()), declarations); + + Set concretePackages = + Files.isDirectory(testCase.concretePath()) + ? generatedJavaFilesRecursively(testCase.concretePath()).stream() + .map(CDConcretizationAdapterTest::packageName) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)) + : Set.of(); + if (concretePackages.size() != 1) { + return; + } + String packageName = concretePackages.iterator().next(); + Path stubRoot = + testCase + .outputPath() + .resolveSibling(testCase.outputPath().getFileName() + "_compile_stubs"); + Map generatedStubs = new LinkedHashMap<>(); + for (Map.Entry declaration : declarations.entrySet()) { + String qualifiedTypeName = qualify(packageName, declaration.getKey()); + if (sourcesByType.containsKey(qualifiedTypeName)) { + continue; + } + Path packageDirectory = + packageName.isBlank() + ? stubRoot + : stubRoot.resolve(packageName.replace('.', java.io.File.separatorChar)); + Path source = packageDirectory.resolve(declaration.getKey() + ".java"); + Path conflict = generatedStubs.putIfAbsent(qualifiedTypeName, source); + if (conflict != null && !conflict.equals(source)) { + throw new IllegalStateException( + "Conflicting compile stubs for " + + qualifiedTypeName + + ": " + + conflict + + " and " + + source); + } + String body = + declaration.getValue().equals("enum") + ? "public enum " + declaration.getKey() + " {;}" + : "public " + declaration.getValue() + " " + declaration.getKey() + " {}"; + String content = + (packageName.isBlank() ? "" : "package " + packageName + ";\n") + body + "\n"; + try { + Files.createDirectories(packageDirectory); + Files.writeString(source, content, StandardCharsets.UTF_8); + } catch (IOException exception) { + throw new IllegalStateException("Failed to write compile stub " + source, exception); + } + sourcesByType.put(qualifiedTypeName, source); + } + } + + private static void addModelDeclarations( + ASTCDCompilationUnit cd, Map declarations) { + cd + .getCDDefinition() + .getCDClassesList() + .forEach(type -> addModelDeclaration(declarations, type.getName(), "class")); + cd + .getCDDefinition() + .getCDInterfacesList() + .forEach(type -> addModelDeclaration(declarations, type.getName(), "interface")); + cd + .getCDDefinition() + .getCDEnumsList() + .forEach(type -> addModelDeclaration(declarations, type.getName(), "enum")); + } + + private static void addModelDeclaration( + Map declarations, String typeName, String kind) { + String conflict = declarations.putIfAbsent(typeName, kind); + if (conflict != null) { + throw new IllegalStateException( + "Conflicting concrete model declarations for " + + typeName + + ": " + + conflict + + " and " + + kind); + } + } + + private static String sourceTypeName(Path source) { + String fileName = source.getFileName().toString(); + String simpleName = fileName.substring(0, fileName.length() - ".java".length()); + return qualify(packageName(source), simpleName); + } + + private static String qualify(String packageName, String simpleName) { + return packageName.isBlank() ? simpleName : packageName + "." + simpleName; + } + + private static String packageName(Path source) { + try { + String packageName = ""; + for (String line : Files.readAllLines(source, StandardCharsets.UTF_8)) { + String trimmed = line.strip(); + if (trimmed.startsWith("package ") && trimmed.endsWith(";")) { + packageName = trimmed.substring("package ".length(), trimmed.length() - 1).strip(); + break; + } + } + return packageName; + } catch (IOException exception) { + throw new IllegalStateException("Failed to inspect Java source " + source, exception); + } + } + + private static void deleteOutputPath(java.nio.file.Path path) { + if (!path.startsWith(java.nio.file.Path.of(CDConcretizationTestCase.OUTPUT_ROOT))) { + throw new IllegalArgumentException("Refusing to delete path outside test output root: " + path); + } + deleteRecursively(path); + } + + private static List generatedJavaFiles( + CDConcretizationTestCase testCase, Path outputPath) { + try { + return generatedJavaFilesRecursively(outputPath).stream() + .filter(CDConcretizationAdapterTest::isFinalOutputFile) + .sorted() + .toList(); + } catch (RuntimeException e) { + throw new IllegalStateException("Failed to inspect generated output for " + testCase.displayName(), e); + } + } + + private static boolean isFinalOutputFile(java.nio.file.Path path) { + for (java.nio.file.Path part : path) { + String name = part.toString(); + if (name.startsWith("_temp_") || name.endsWith("_formatted")) { + return false; + } + } + return true; + } + + private static void assertNoAdaptMetadata( + CDConcretizationTestCase testCase, List javaFiles) { + List filesWithMetadata = + javaFiles.stream() + .filter(CDConcretizationAdapterTest::containsAdaptMetadata) + .toList(); + + assertTrue( + filesWithMetadata.isEmpty(), + () -> "Generated output still contains @Adapt metadata for " + + testCase.displayName() + + ": " + + filesWithMetadata); + } + + private static boolean containsAdaptMetadata(java.nio.file.Path path) { + try { + String source = java.nio.file.Files.readString(path); + return source.contains("@Adapt") + || source.contains("import de.monticore.codeAdaption.utils.Adapt"); + } catch (IOException e) { + throw new IllegalStateException("Failed to read generated Java file " + path, e); + } + } + + private static void assertNoWildcardJdkImports( + CDConcretizationTestCase testCase, List javaFiles) { + List filesWithWildcards = + javaFiles.stream() + .filter(CDConcretizationAdapterTest::containsWildcardJdkImport) + .toList(); + + assertTrue( + filesWithWildcards.isEmpty(), + () -> "Generated output contains wildcard JDK imports for " + + testCase.displayName() + + ": " + + filesWithWildcards); + } + + private static boolean containsWildcardJdkImport(java.nio.file.Path path) { + try { + String source = java.nio.file.Files.readString(path); + return source.contains("import java.util.*;") || source.contains("import java.time.*;"); + } catch (IOException e) { + throw new IllegalStateException("Failed to read generated Java file " + path, e); + } + } + + private CDConcretizationTestCase oracleTestCase(String name) throws IOException { + Path root = temporaryDirectory.resolve(name); + Files.createDirectories(root.resolve("concrete")); + Files.createDirectories(root.resolve("output")); + return new CDConcretizationTestCase( + name, + name, + root.resolve("Reference.cd"), + root.resolve("Concrete.cd"), + root.resolve("adapter"), + root.resolve("concrete"), + root.resolve("output"), + Set.of("ref"), + false, + true); + } + + private static Path write(Path path, String content) throws IOException { + Files.createDirectories(path.getParent()); + Files.writeString(path, content + System.lineSeparator(), StandardCharsets.UTF_8); + return path; + } + +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterCleanupTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterCleanupTest.java new file mode 100644 index 00000000..d54732e8 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterCleanupTest.java @@ -0,0 +1,54 @@ +package de.monticore.codeAdaption; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class CodeAdapterCleanupTest { + + @TempDir Path output; + + @Test + void cleanCodePreservesPackagePathsAndRemovesOnlyToolAdaptAnnotations() throws IOException { + Path first = output.resolve("a/User.java"); + Path second = output.resolve("b/User.java"); + Files.createDirectories(first.getParent()); + Files.createDirectories(second.getParent()); + Files.writeString( + first, + """ + package a; + import de.monticore.codeAdaption.utils.Adapt; + + @Adapt(ignore = true) + @demo.local.Adapt(ignore = true) + public class User {} + """); + Files.writeString( + second, + """ + package b; + import de.monticore.codeAdaption.utils.Adapt; + + @Adapt(ignore = true) + public class User {} + """); + + new OutputCodeService().cleanCode(output); + + assertTrue(Files.exists(first)); + assertTrue(Files.exists(second)); + assertFalse(Files.exists(output.resolve("User.java"))); + String firstCleaned = Files.readString(first); + String secondCleaned = Files.readString(second); + assertTrue(firstCleaned.contains("@demo.local.Adapt"), firstCleaned); + assertFalse(firstCleaned.contains("import de.monticore.codeAdaption.utils.Adapt")); + assertFalse(secondCleaned.contains("import de.monticore.codeAdaption.utils.Adapt")); + assertFalse(secondCleaned.contains("@Adapt")); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterPipelineRegressionTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterPipelineRegressionTest.java new file mode 100644 index 00000000..2ee18c32 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterPipelineRegressionTest.java @@ -0,0 +1,234 @@ +package de.monticore.codeAdaption; + +import static de.monticore.cdconformance.CDConfParameter.ADAPTED_NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_CARD_RESTRICTION; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.METHOD_OVERLOADING; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.SRC_TARGET_ASSOC_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.utils.AdapterUtils; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** Regression coverage for merge and output behavior across the complete adaptation pipeline. */ +class CodeAdapterPipelineRegressionTest extends AdapterAbstractTest { + + private static final Set CONCRETIZATION_PARAMETERS = + Set.of( + STEREOTYPE_MAPPING, + NAME_MAPPING, + SRC_TARGET_ASSOC_MAPPING, + INHERITANCE, + ALLOW_CARD_RESTRICTION, + METHOD_OVERLOADING, + ADAPTED_NAME_MAPPING, + ALLOW_ADDITIONAL_PARAMETERS); + + @TempDir Path temporaryDirectory; + + @BeforeEach + void setUp() { + initMills(); + } + + @Test + void divergentMethodBodiesFromAdaptationPassesMustNotBeSilentlyDiscarded() throws IOException { + ASTOrdinaryCompilationUnit first = + javaUnit("first/Service.java", "package p; class Service { int value() { return 1; } }"); + ASTOrdinaryCompilationUnit second = + javaUnit("second/Service.java", "package p; class Service { int value() { return 2; } }"); + + assertThrows(IllegalStateException.class, () -> AdapterUtils.mergeAsts(first, second)); + } + + @Test + void divergentFieldDefinitionsFromAdaptationPassesMustNotBeSilentlyDiscarded() + throws IOException { + ASTOrdinaryCompilationUnit first = + javaUnit( + "first/Service.java", + "package p; class Service { static final int value = 1; }"); + ASTOrdinaryCompilationUnit second = + javaUnit("second/Service.java", "package p; class Service { int value = 2; }"); + + assertThrows(IllegalStateException.class, () -> AdapterUtils.mergeAsts(first, second)); + } + + @Test + void divergentSuperclassesFromAdaptationPassesMustNotBeSilentlyDiscarded() + throws IOException { + ASTOrdinaryCompilationUnit first = + javaUnit("first/Service.java", "package p; class Service extends FirstBase {}"); + ASTOrdinaryCompilationUnit second = + javaUnit("second/Service.java", "package p; class Service extends SecondBase {}"); + + assertThrows(IllegalStateException.class, () -> AdapterUtils.mergeAsts(first, second)); + } + + @Test + void divergentNestedTypesFromAdaptationPassesMustNotBeSilentlyDiscarded() + throws IOException { + ASTOrdinaryCompilationUnit first = + javaUnit( + "first/Service.java", + "package p; class Service { static class State { int value() { return 1; } } }"); + ASTOrdinaryCompilationUnit second = + javaUnit( + "second/Service.java", + "package p; class Service { static class State { int value() { return 2; } } }"); + + assertThrows(IllegalStateException.class, () -> AdapterUtils.mergeAsts(first, second)); + } + + @Test + void qualifiedSameSimpleParameterTypesRemainDistinctOverloads() throws IOException { + ASTOrdinaryCompilationUnit first = + javaUnit( + "qualified-first/Service.java", + "package p; class Service { void run(alpha.Role role) {} }"); + ASTOrdinaryCompilationUnit second = + javaUnit( + "qualified-second/Service.java", + "package p; class Service { void run(beta.Role role) {} }"); + + String merged = JavaLoader.print(AdapterUtils.mergeAsts(first, second)); + + assertTrue(merged.contains("alpha.Role"), merged); + assertTrue(merged.contains("beta.Role"), merged); + } + + @Test + void genericArgumentsDoNotCreateFalseJavaOverloads() throws IOException { + ASTOrdinaryCompilationUnit first = + javaUnit( + "generic-first/Service.java", + "package p; class Service { void run(java.util.List values) {} }"); + ASTOrdinaryCompilationUnit second = + javaUnit( + "generic-second/Service.java", + "package p; class Service { void run(java.util.List values) {} }"); + + assertThrows(IllegalStateException.class, () -> AdapterUtils.mergeAsts(first, second)); + } + + @Test + void importedAndQualifiedSuperclassBindingsAreTheSameIdentity() throws IOException { + ASTOrdinaryCompilationUnit imported = + javaUnit( + "imported-super/Service.java", + "package p; import alpha.Base; class Service extends Base {}"); + ASTOrdinaryCompilationUnit qualified = + javaUnit( + "qualified-super/Service.java", + "package p; class Service extends alpha.Base {}"); + + assertDoesNotThrow(() -> AdapterUtils.mergeAsts(imported, qualified)); + } + + @Test + void formattingAndCommentsDoNotCreateMergeConflicts() throws IOException { + ASTOrdinaryCompilationUnit first = + javaUnit( + "formatted-first/Service.java", + "package p; class Service { int value() { return 1; } }"); + ASTOrdinaryCompilationUnit second = + javaUnit( + "formatted-second/Service.java", + "package p; class Service { /* same behavior */ int value(){return 1;} }"); + + assertDoesNotThrow(() -> AdapterUtils.mergeAsts(first, second)); + } + + @Test + void concreteCompilationUnitWithSeveralTopLevelTypesMustProduceCompilableOutput() + throws Exception { + Path concrete = temporaryDirectory.resolve("concrete"); + Path staging = temporaryDirectory.resolve("staging"); + Files.createDirectories(concrete); + Files.createDirectories(staging.resolve("p")); + Files.writeString( + concrete.resolve("Service.java"), + "package p; class Helper { int handwritten; } public class Service {}"); + Files.writeString( + staging.resolve("p/Helper.java"), + "package p; class Helper { int adapted; }"); + + new OutputCodeService().copyConcreteFiles(concrete, staging); + + assertGeneratedJavaCompiles(generatedJavaFilesRecursively(staging)); + } + + @Test + void helperReachableOnlyFromRetainedIgnoredRootMustBeIncluded() throws IOException { + Path fixture = temporaryDirectory.resolve("ignored-root-helper"); + Path referenceCode = fixture.resolve("reference-code"); + Path concreteCode = fixture.resolve("concrete-code"); + Path output = fixture.resolve("output"); + Files.createDirectories(referenceCode.resolve("demo")); + Files.createDirectories(concreteCode.resolve("demo")); + Path referenceCD = fixture.resolve("Reference.cd"); + Path concreteCD = fixture.resolve("Concrete.cd"); + Files.writeString(referenceCD, "classdiagram Reference { class Service; }"); + Files.writeString( + concreteCD, + "classdiagram Concrete { <> class ConcreteService; }"); + Files.writeString( + referenceCode.resolve("demo/IgnoredFacade.java"), + """ + package demo; + import de.monticore.codeAdaption.utils.Adapt; + @Adapt(ignore = true) + public class IgnoredFacade { + private final Helper helper = new Helper(); + public Object helper() { return helper; } + } + """); + Files.writeString( + referenceCode.resolve("demo/Helper.java"), + "package demo; final class Helper {}"); + Files.writeString( + concreteCode.resolve("demo/ConcreteService.java"), + "package demo; public class ConcreteService {}"); + + new CodeAdapter(Set.of(NAME_MATCHING, ANNOTATION_MATCHING), CONCRETIZATION_PARAMETERS) + .adapt( + referenceCD.toFile(), + concreteCD.toFile(), + Set.of("ref"), + referenceCode, + concreteCode, + output, + true, + true); + + assertTrue( + Files.isRegularFile(output.resolve("demo/Helper.java")), + "The dependency closure must start at explicitly retained ignored roots"); + assertGeneratedJavaCompiles(generatedJavaFilesRecursively(output)); + } + + private ASTOrdinaryCompilationUnit javaUnit(String relativePath, String source) + throws IOException { + Path file = temporaryDirectory.resolve(relativePath); + Files.createDirectories(file.getParent()); + Files.writeString(file, source); + return JavaLoader.loadJava(file.toFile()); + } + +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterSafetyTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterSafetyTest.java new file mode 100644 index 00000000..c65f2ce6 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterSafetyTest.java @@ -0,0 +1,83 @@ +package de.monticore.codeAdaption; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.utils.JavaLoader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class CodeAdapterSafetyTest { + + @TempDir Path tempDir; + + @ParameterizedTest(name = "rejects path containment: {0}") + @ValueSource(strings = {"output-inside-reference", "reference-inside-output"}) + void rejectsOverlappingReferenceAndOutputPaths(String scenario) { + Path reference = tempDir.resolve("reference"); + Path output = tempDir.resolve("output"); + if ("output-inside-reference".equals(scenario)) { + output = reference.resolve("generated"); + } else { + reference = output.resolve("reference"); + } + Path concrete = tempDir.resolve("concrete"); + Path finalReference = reference; + Path finalOutput = output; + + assertThrows( + IllegalArgumentException.class, + () -> CodeAdapter.validatePaths(finalReference, concrete, finalOutput)); + } + + @Test + void rejectsClassDiagramInsideOutput() { + Path output = tempDir.resolve("output"); + AdaptationWorkspace workspace = + new AdaptationWorkspace( + tempDir.resolve("reference"), tempDir.resolve("concrete"), output); + + assertThrows( + IllegalArgumentException.class, + () -> + workspace.validateReadOnlyInput( + output.resolve("Reference.cd"), "reference class diagram")); + } + + @Test + void refusesToDeleteFilesystemRoot() { + Path root = tempDir.toAbsolutePath().getRoot(); + + assertThrows(IllegalArgumentException.class, () -> JavaLoader.removeDirectory(root)); + } + + @Test + void missingJavaSourceDirectoryIsAnError() { + assertThrows( + IllegalArgumentException.class, + () -> JavaLoader.readJavaCode(tempDir.resolve("does-not-exist"))); + } + + @Test + void rejectsDifferentConcreteFilesWithTheSamePackageTarget() throws IOException { + Path concrete = tempDir.resolve("concrete"); + Path first = concrete.resolve("first/User.java"); + Path second = concrete.resolve("second/User.java"); + Files.createDirectories(first.getParent()); + Files.createDirectories(second.getParent()); + Files.writeString(first, "package p; class User { int first; }"); + Files.writeString(second, "package p; class User { int second; }"); + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, + () -> new OutputCodeService().copyConcreteFiles(concrete, tempDir.resolve("output"))); + + assertTrue(exception.getMessage().contains("both target")); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterTest.java index 78099f19..9d41c337 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterTest.java @@ -5,12 +5,11 @@ import de.monticore.cdconformance.CDConfParameter; import de.monticore.codeAdaption.utils.AdapterParam; -import de.monticore.codeAdaption.utils.JavaLoader; import java.io.File; import java.nio.file.Path; -import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.*; +import org.junit.jupiter.api.io.TempDir; public class CodeAdapterTest extends AdapterAbstractTest { private final String baseDir = "src/test/resources/de/monticore/codeAdaption/"; @@ -18,8 +17,9 @@ public class CodeAdapterTest extends AdapterAbstractTest { private final File REF_CD = new File(baseDir + "App.cd"); private final File CON_CD = new File(baseDir + "UniApp.cd"); + @TempDir Path temporaryDirectory; + private Set confParameters; - private Set adapterParams; private Set mappings = Set.of("ref"); @@ -31,67 +31,67 @@ public void setup() { @Test public void testAdaptionWithName() { - Path outputPath = Path.of("target/adapter/name"); - adapterParams = Set.of(NAME_MATCHING); - CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + Path outputPath = adapt(Set.of(NAME_MATCHING), "name"); - adapter.adapt(REF_CD, CON_CD, mappings, Path.of(baseDir + "adapter/name"), conHwc, outputPath); - - String student = readFileWithoutSpace(outputPath, "Student.java"); - Assertions.assertEquals("publicclassStudent{StringstudentId;}", student); + String student = readFileContent(outputPath, "Student.java"); + Assertions.assertTrue(student.contains("public class Student")); + Assertions.assertTrue(student.contains("String studentId;")); } @Test public void testAdaptionWithInfix() { - Path outputPath = Path.of("target/adapter/infix"); - adapterParams = Set.of(INFIX_MATCHING, IGNORE_NON_MATCHED_VAR); - CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); - - adapter.adapt(REF_CD, CON_CD, mappings, Path.of(baseDir + "adapter/infix"), conHwc, outputPath); - - String studentBuilder = readFileWithoutSpace(outputPath, "StudentBuilder.java"); - Assertions.assertEquals( - "publicclassStudentBuilder{protectedStringstudentId;publicStudentBuildersetStudentId(Stringid){Studentstudent=newStudent();}}", - studentBuilder); + Path outputPath = adapt(Set.of(INFIX_MATCHING, IGNORE_NON_MATCHED_VAR), "infix"); + + String studentBuilder = readFileContent(outputPath, "StudentBuilder.java"); + Assertions.assertTrue(studentBuilder.contains("public class StudentBuilder")); + Assertions.assertTrue(studentBuilder.contains("protected String studentId;")); + Assertions.assertTrue( + studentBuilder.contains("public StudentBuilder setStudentId(String id)")); + Assertions.assertTrue(studentBuilder.contains("Student student = new Student();")); } @Test public void testAdaptionWithAnnotation() { - Path outputPath = Path.of("target/adapter/annot"); - adapterParams = Set.of(ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR); - CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); - - adapter.adapt(REF_CD, CON_CD, mappings, Path.of(baseDir + "adapter/annot"), conHwc, outputPath); - - String studentRepository = readFileWithoutSpace(outputPath, "StudentRepository.java"); - Assertions.assertEquals( - "importjava.util.*;publicclassStudentRepositoryextendsRepository{privateSetstudentSet=newHashSet<>();publicOptionalfindStudentByStudentId(Stringid){returnOptional.empty();}publicListgetAllStudentSortedByStudentId(){ListstudentList=newArrayList<>();}publicvoidstore(Studentstudent){studentSet.add(student);}}", - studentRepository); + Path outputPath = adapt(Set.of(ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR), "annot"); + + String studentRepository = readFileContent(outputPath, "StudentRepository.java"); + Assertions.assertTrue( + studentRepository.contains("public class StudentRepository extends Repository")); + Assertions.assertTrue( + studentRepository.contains("private Set studentSet = new HashSet<>();")); + Assertions.assertTrue( + studentRepository.contains("public Optional findStudentByStudentId(String id)")); + Assertions.assertTrue( + studentRepository.contains("public List getAllStudentSortedByStudentId()")); + Assertions.assertTrue(studentRepository.contains("public void store(Student student)")); } @Test public void testAdaptionWithAllMatchingStrategy() { - Path outputPath = Path.of("target/adapter/compose"); - adapterParams = - Set.of(NAME_MATCHING, ANNOTATION_MATCHING, INFIX_MATCHING, IGNORE_NON_MATCHED_VAR); - CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); - - adapter.adapt( - REF_CD, CON_CD, mappings, Path.of(baseDir + "adapter/compose"), conHwc, outputPath); - - String studentRepository = readFileWithoutSpace(outputPath, "StudentRepository.java"); - - Assertions.assertEquals( - "importjava.util.*;publicclassStudentRepositoryextendsRepository{privateSetstudentSet=newHashSet<>();publicOptionalfindStudentByStudentId(Stringid){returnOptional.empty();}publicListgetAllStudentSortedByStudentId(){ListstudentList=newArrayList<>();}publicvoidstore(Studentstudent){studentSet.add(student);}}", - studentRepository); + Path outputPath = + adapt( + Set.of(NAME_MATCHING, ANNOTATION_MATCHING, INFIX_MATCHING, IGNORE_NON_MATCHED_VAR), + "compose"); + + String studentRepository = readFileContent(outputPath, "StudentRepository.java"); + Assertions.assertTrue( + studentRepository.contains("public class StudentRepository extends Repository")); + Assertions.assertTrue(studentRepository.contains("return Optional.empty();")); + Assertions.assertTrue( + studentRepository.contains("List studentList = new ArrayList<>();")); + Assertions.assertTrue(studentRepository.contains("studentSet.add(student);")); } - protected String readFileWithoutSpace(Path dir, String filename) { - Optional file = - JavaLoader.readJavaFile(dir).stream() - .filter(f -> f.getName().endsWith(filename)) - .findFirst(); - Assertions.assertTrue(file.isPresent()); - return JavaLoader.readFileContent(file.get()).replaceAll("\\s+", ""); + private Path adapt(Set adapterParams, String fixtureName) { + Path outputPath = temporaryDirectory.resolve(fixtureName); + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + adapter.adapt( + REF_CD, + CON_CD, + mappings, + Path.of(baseDir + "adapter/" + fixtureName), + conHwc, + outputPath); + return outputPath; } } diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterToolTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterToolTest.java index f12b2233..2a46f312 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterToolTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/CodeAdapterToolTest.java @@ -1,13 +1,69 @@ package de.monticore.codeAdaption; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class CodeAdapterToolTest extends AdapterAbstractTest { + + private static final Path RESOURCES = + Path.of("src/test/resources/de/monticore/codeAdaption"); -public class CodeAdapterToolTest { + @TempDir Path temporaryDirectory; + + @BeforeEach + void setUp() { + initMills(); + } @Test - public void test() { - String cmd = "-i University.cd --reference UserRole.cd --rc code"; - // String cmd = "java -jar AdapterTool.jar -i University.cd --reference UserRole.cd --rc code" - // ; + void commandLineInvocationProducesAdaptedOutput() { + Path output = temporaryDirectory.resolve("cli-output"); + deleteRecursively(output); + + int exitCode = + new CodeAdapterTool() + .execute( + new String[] { + "--reference", + RESOURCES.resolve("App.cd").toString(), + "--concrete", + RESOURCES.resolve("UniApp.cd").toString(), + "--reference-code", + RESOURCES.resolve("adapter/name").toString(), + "--concrete-code", + RESOURCES.resolve("hwc").toString(), + "--output", + output.toString(), + "--mapping", + "ref", + "--matching", + "name", + "--concretize", + "--no-persist-concretized-cd" + }); + + assertEquals(0, exitCode); + assertTrue(Files.exists(findGeneratedFile(output, "Student.java"))); + assertFalse(Files.exists(output.resolve("UniApp.cd"))); + assertGeneratedJavaCompiles(output); + } + + private static Path findGeneratedFile(Path output, String fileName) { + try (var files = Files.walk(output)) { + return files + .filter(Files::isRegularFile) + .filter(path -> path.getFileName().toString().equals(fileName)) + .findFirst() + .orElse(output.resolve(fileName)); + } catch (java.io.IOException exception) { + throw new IllegalStateException("Failed to inspect CLI output", exception); + } } } diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/ConcretizationHelperDependencyTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/ConcretizationHelperDependencyTest.java new file mode 100644 index 00000000..987502b3 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/ConcretizationHelperDependencyTest.java @@ -0,0 +1,179 @@ +package de.monticore.codeAdaption; + +import static de.monticore.cdconformance.CDConfParameter.ADAPTED_NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_CARD_RESTRICTION; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.METHOD_OVERLOADING; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.SRC_TARGET_ASSOC_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.utils.JavaLoader; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class ConcretizationHelperDependencyTest extends AdapterAbstractTest { + + private static final Path FIXTURE = + Path.of("src/test/resources/de/monticore/codeAdaption/concretizationHelpers"); + + @TempDir Path temporaryDirectory; + + @BeforeEach + void setUp() { + initMills(); + } + + @Test + void retainsAndAdaptsTransitiveJavaOnlyHelpers() { + File referenceCD = FIXTURE.resolve("Reference.cd").toFile(); + File concreteCD = FIXTURE.resolve("Concrete.cd").toFile(); + Path referenceCode = FIXTURE.resolve("adapter"); + Path concreteCode = FIXTURE.resolve("concrete"); + Path output = temporaryDirectory.resolve("output"); + CodeAdapter adapter = + new CodeAdapter( + Set.of(NAME_MATCHING, ANNOTATION_MATCHING), + Set.of( + STEREOTYPE_MAPPING, + NAME_MAPPING, + SRC_TARGET_ASSOC_MAPPING, + INHERITANCE, + ALLOW_CARD_RESTRICTION, + METHOD_OVERLOADING, + ADAPTED_NAME_MAPPING, + ALLOW_ADDITIONAL_PARAMETERS)); + + adapter.adapt( + referenceCD, + concreteCD, + Set.of("ref"), + referenceCode, + concreteCode, + output, + true, + true); + + assertTrue(Files.isRegularFile(output.resolve("Concrete/ConcreteService.java"))); + assertTrue(Files.isRegularFile(output.resolve("Concrete/ConcreteServiceTOP.java"))); + assertTrue(Files.isRegularFile(output.resolve("Concrete/ServiceSupport.java"))); + assertTrue(Files.isRegularFile(output.resolve("Concrete/SupportValue.java"))); + assertTrue(Files.isRegularFile(output.resolve("Concrete.cd"))); + assertDoesNotThrow(() -> JavaLoader.parseCD(output.resolve("Concrete.cd").toString())); + assertFalse(Files.exists(output.resolve("Concrete/UnrelatedHelper.java"))); + + String service = readFileContent(output, "ConcreteService.java"); + String support = readFileContent(output, "ServiceSupport.java"); + String value = readFileContent(output, "SupportValue.java"); + assertTrue(service.contains("ServiceSupport support")); + assertTrue(service.contains("ConcreteService decorate()")); + assertTrue(support.contains("ConcreteService decorate(ConcreteService service)")); + assertTrue(value.contains("ConcreteService apply(ConcreteService service)")); + assertFalse(support.contains("Service decorate(Service service)")); + assertFalse(value.contains("Service apply(Service service)")); + + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(output); + } + + @Test + void retainsHelpersWhenAdaptedImplementationBecomesTop() throws Exception { + Path concreteCode = Files.createDirectories(temporaryDirectory.resolve("top-concrete")); + Files.copy(FIXTURE.resolve("concrete/ConcreteService.java"), concreteCode.resolve("ConcreteService.java")); + Path output = temporaryDirectory.resolve("top-output"); + CodeAdapter adapter = + new CodeAdapter( + Set.of(NAME_MATCHING, ANNOTATION_MATCHING), + Set.of( + STEREOTYPE_MAPPING, + NAME_MAPPING, + SRC_TARGET_ASSOC_MAPPING, + INHERITANCE, + ALLOW_CARD_RESTRICTION, + METHOD_OVERLOADING, + ADAPTED_NAME_MAPPING, + ALLOW_ADDITIONAL_PARAMETERS)); + + adapter.adaptWithTopSeparation( + FIXTURE.resolve("Reference.cd").toFile(), + FIXTURE.resolve("Concrete.cd").toFile(), + Set.of("ref"), + FIXTURE.resolve("adapter"), + concreteCode, + output, + true, + true); + + String handwritten = readFileContent(output, "ConcreteService.java"); + String top = readFileContent(output, "ConcreteServiceTOP.java"); + assertTrue(handwritten.contains("extends ConcreteServiceTOP")); + assertTrue(top.contains("ServiceSupport support")); + assertTrue(top.contains("support.decorate(((ConcreteService) (this)))")); + assertTrue(Files.isRegularFile(output.resolve("Concrete/ServiceSupport.java"))); + assertTrue(Files.isRegularFile(output.resolve("Concrete/SupportValue.java"))); + assertTrue(Files.isRegularFile(output.resolve("Concrete.cd"))); + assertDoesNotThrow(() -> JavaLoader.parseCD(output.resolve("Concrete.cd").toString())); + assertFalse(Files.exists(output.resolve("Concrete/UnrelatedHelper.java"))); + assertGeneratedJavaCompiles(output); + } + + @Test + void divergentHelperVariantsFailWithoutPublishingPartialOutput() throws Exception { + Path divergentFixture = FIXTURE.resolve("divergent"); + Path output = temporaryDirectory.resolve("existing-output"); + Files.createDirectories(output); + Path marker = output.resolve("preserved.txt"); + Files.writeString(marker, "previous successful output"); + Path referenceService = divergentFixture.resolve("adapter/Service.java"); + String referenceBefore = Files.readString(referenceService); + + CodeAdapter adapter = + new CodeAdapter( + Set.of(NAME_MATCHING, ANNOTATION_MATCHING), + Set.of( + STEREOTYPE_MAPPING, + NAME_MAPPING, + SRC_TARGET_ASSOC_MAPPING, + INHERITANCE, + ALLOW_CARD_RESTRICTION, + METHOD_OVERLOADING, + ADAPTED_NAME_MAPPING, + ALLOW_ADDITIONAL_PARAMETERS)); + + CodeAdaptationException exception = + assertThrows( + CodeAdaptationException.class, + () -> + adapter.adapt( + divergentFixture.resolve("Reference.cd").toFile(), + divergentFixture.resolve("Concrete.cd").toFile(), + Set.of("ref"), + divergentFixture.resolve("adapter"), + divergentFixture.resolve("concrete"), + output, + true, + true)); + + assertTrue(exception.getMessage().contains("Concrete.ServiceSupport")); + assertTrue(exception.getMessage().contains("divergent outputs")); + assertEquals("previous successful output", Files.readString(marker)); + assertTrue(Files.isRegularFile(output.resolve("Concrete.cd"))); + assertDoesNotThrow(() -> JavaLoader.parseCD(output.resolve("Concrete.cd").toString())); + assertFalse(Files.exists(output.resolve("Concrete/PrimaryService.java"))); + assertFalse(Files.exists(output.resolve("Concrete/BackupService.java"))); + assertEquals(referenceBefore, Files.readString(referenceService)); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/HelperVariantRegistryTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/HelperVariantRegistryTest.java new file mode 100644 index 00000000..d35ed822 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/HelperVariantRegistryTest.java @@ -0,0 +1,95 @@ +package de.monticore.codeAdaption; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class HelperVariantRegistryTest { + @TempDir Path tempDir; + + @Test + void deduplicatesStructurallyIdenticalVariantsAcrossMappingsAndPasses() throws IOException { + HelperVariantRegistry registry = new HelperVariantRegistry(); + ASTOrdinaryCompilationUnit first = + parse("first/Support.java", "package shared; class Support { int value() { return 1; } }"); + ASTOrdinaryCompilationUnit identical = + parse("second/Support.java", "package shared; class Support { int value() { return 1; } }"); + + assertTrue(registry.register("shared.Support", first, "observer", "[Subject=Publisher]")); + assertFalse( + registry.register("shared.Support", identical, "command", "[Command=ReserveCommand]")); + } + + @Test + void ignoresCommentsAndSourcePositionsWhenComparingVariants() throws IOException { + HelperVariantRegistry registry = new HelperVariantRegistry(); + ASTOrdinaryCompilationUnit first = + parse( + "first/Support.java", + "package shared; // first origin\n" + + "class Support { int value() { return 1; } }"); + ASTOrdinaryCompilationUnit relocated = + parse( + "deeply/nested/second/Support.java", + "package shared;\n" + + "/* a different comment at another source position */\n" + + "class Support {\n int value() {\n return 1;\n }\n }"); + + assertTrue(registry.register("shared.Support", first, "observer", "[Subject=Publisher]")); + assertFalse( + registry.register("shared.Support", relocated, "observer", "[Subject=AuditPublisher]")); + } + + @Test + void rejectsDivergentBodiesAndReportsBothOrigins() throws IOException { + HelperVariantRegistry registry = new HelperVariantRegistry(); + ASTOrdinaryCompilationUnit first = + parse("first/Support.java", "package shared; class Support { int value() { return 1; } }"); + ASTOrdinaryCompilationUnit divergent = + parse("second/Support.java", "package shared; class Support { int value() { return 2; } }"); + + assertTrue(registry.register("shared.Support", first, "observer", "[Subject=Publisher]")); + CodeAdaptationException exception = + assertThrows( + CodeAdaptationException.class, + () -> + registry.register( + "shared.Support", divergent, "command", "[Command=ReserveCommand]")); + + assertTrue(exception.getMessage().contains("shared.Support")); + assertTrue(exception.getMessage().contains("mapping 'observer'")); + assertTrue(exception.getMessage().contains("[Subject=Publisher]")); + assertTrue(exception.getMessage().contains("mapping 'command'")); + assertTrue(exception.getMessage().contains("[Command=ReserveCommand]")); + } + + @Test + void storesACloneInsteadOfTheMutableInput() throws IOException { + HelperVariantRegistry registry = new HelperVariantRegistry(); + ASTOrdinaryCompilationUnit first = + parse("first/Support.java", "package shared; class Support { int value() { return 1; } }"); + ASTOrdinaryCompilationUnit originalShape = first.deepClone(); + + assertTrue(registry.register("shared.Support", first, "observer", "[Subject=Publisher]")); + first.getTypeDeclarationList().clear(); + + assertFalse( + registry.register( + "shared.Support", originalShape, "observer", "[Subject=SecondPublisher]")); + } + + private ASTOrdinaryCompilationUnit parse(String relativePath, String source) throws IOException { + Path file = tempDir.resolve(relativePath); + Files.createDirectories(file.getParent()); + Files.writeString(file, source); + return JavaLoader.loadJava(file.toFile()); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/PatternAdapterIntegrationTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/PatternAdapterIntegrationTest.java new file mode 100644 index 00000000..6611eb5c --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/PatternAdapterIntegrationTest.java @@ -0,0 +1,250 @@ +package de.monticore.codeAdaption; + +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STRICT_PARAMETER_ORDER; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.utils.AdapterParam; +import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class PatternAdapterIntegrationTest extends AdapterAbstractTest { + + private static final Path FIXTURE_ROOT = + Path.of("src/test/resources/de/monticore/codeAdaption/evaluation"); + private static final Set DEFAULT_CONFORMANCE_PARAMETERS = + Set.of(NAME_MAPPING, INHERITANCE, STEREOTYPE_MAPPING, STRICT_PARAMETER_ORDER); + private static final Set DEFAULT_ADAPTER_PARAMETERS = + Set.of( + NAME_MATCHING, + ANNOTATION_MATCHING, + IGNORE_NON_MATCHED_VAR, + IGNORE_NON_MATCHED_TYPE); + + @TempDir Path temporaryDirectory; + + @BeforeEach + void setUp() { + initMills(); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("controllerWorkerCases") + void adaptsControllerWorkerPatterns( + String description, String fixtureName, boolean verifyControllerContract) { + Path output = adapt(fixtureName, Set.of("buildPat", "observer"), Set.of()); + + Set generatedFiles = generatedFileNames(output); + assertTrue( + generatedFiles.containsAll( + Set.of( + "ControllerBuilder.java", + "WorkerABuilder.java", + "WorkerBBuilder.java", + "Controller.java", + "WorkerA.java", + "WorkerB.java"))); + + String controllerBuilder = readFileContent(output, "ControllerBuilder.java"); + String workerABuilder = readFileContent(output, "WorkerABuilder.java"); + String workerBBuilder = readFileContent(output, "WorkerBBuilder.java"); + + assertTrue(controllerBuilder.contains("public class ControllerBuilder")); + assertTrue(controllerBuilder.contains("public Controller build()")); + assertContainsEither(controllerBuilder, "return this;", "return this ;"); + assertContainsEither(controllerBuilder, "String idField", "String idfield"); + assertContainsEither(controllerBuilder, "String statusField", "String statusfield"); + + assertTrue(workerABuilder.contains("public class WorkerABuilder")); + assertTrue(workerABuilder.contains("public WorkerA build()")); + assertContainsEither(workerABuilder, "String nameField", "String namefield"); + + assertTrue(workerBBuilder.contains("public class WorkerBBuilder")); + assertTrue(workerBBuilder.contains("public WorkerB build()")); + assertContainsEither(workerBBuilder, "String nameField", "String namefield"); + + String workerA = readFileContent(output, "WorkerA.java"); + String workerB = readFileContent(output, "WorkerB.java"); + assertContainsEither(workerA, "public void update(", "void update("); + assertContainsEither(workerB, "public void update(", "void update("); + + if (verifyControllerContract) { + String controller = readFileContent(output, "Controller.java"); + assertTrue(controller.contains("private String id")); + assertTrue(controller.contains("private String status")); + assertTrue(controller.contains("public Controller(String id, String status)")); + assertTrue(controller.contains("String getId()")); + assertTrue(controller.contains("String getStatus()")); + assertTrue(controller.contains("List observers")); + assertTrue(controller.contains("boolean subscribe(WorkerInterface")); + assertTrue(controller.contains("boolean unsubscribe(WorkerInterface")); + assertTrue(controller.contains("void notifyAll()")); + assertFalse(generatedFiles.contains("OSubject.java")); + assertFalse(generatedFiles.contains("OObserver.java")); + assertFalse(generatedFiles.contains("BuilderBuilder.java")); + } + } + + @Test + @DisplayName("Strategy Pattern") + void adaptsStrategyPattern() { + Path output = adapt("testcase_9_strategy_pattern", Set.of("strategy"), Set.of()); + + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(output); + + String strategy = readFileContent(output, "PaymentStrategy.java"); + String processor = readFileContent(output, "PaymentProcessor.java"); + assertTrue(strategy.contains("interface PaymentStrategy")); + assertTrue(strategy.contains("void pay(double amount)")); + assertTrue(processor.contains("class PaymentProcessor")); + assertTrue(processor.contains("void processPayment(double amount)")); + } + + @Test + @DisplayName("Singleton Pattern") + void adaptsSingletonPattern() { + Path output = adapt("testcase_10_singleton_pattern", Set.of("singleton"), Set.of()); + + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(output); + + String databaseConnection = readFileContent(output, "DatabaseConnection.java"); + String logger = readFileContent(output, "Logger.java"); + assertTrue(databaseConnection.contains("class DatabaseConnection")); + assertTrue(databaseConnection.contains("static DatabaseConnection instance")); + assertTrue(databaseConnection.contains("DatabaseConnection getInstance()")); + assertTrue(logger.contains("class Logger")); + assertTrue(logger.contains("static Logger instance")); + assertTrue(logger.contains("Logger getInstance()")); + } + + @Test + @DisplayName("Template Method Pattern") + void adaptsTemplateMethodPattern() { + Path output = adapt("testcase_11_template_method_pattern", Set.of("template"), Set.of()); + + String dataProcessor = readFileContent(output, "DataProcessor.java"); + assertTrue(dataProcessor.contains("private String source")); + assertTrue(dataProcessor.contains("private String destination")); + assertTrue(dataProcessor.contains("public DataProcessor(String source, String destination)")); + assertTrue(dataProcessor.contains("public abstract void readData()")); + assertTrue(dataProcessor.contains("public abstract void transformData()")); + assertTrue(dataProcessor.contains("public void validateData()")); + assertTrue(dataProcessor.contains("public void templateMethod()")); + assertTrue(dataProcessor.contains("readData();")); + assertTrue(dataProcessor.contains("transformData();")); + assertTrue(dataProcessor.contains("validateData();")); + assertFalse(dataProcessor.contains("primitiveOperation1")); + assertFalse(dataProcessor.contains("primitiveOperation2")); + assertFalse(dataProcessor.contains("void hook()")); + } + + @Test + @DisplayName("Strategy + Singleton Patterns") + void adaptsStrategyAndSingletonTogether() { + Path output = + adapt( + "testcase_12_strategy_singleton_pattern", + Set.of("strategy", "singleton"), + Set.of(ALLOW_ADDITIONAL_PARAMETERS)); + + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(output); + + String manager = readFileContent(output, "ConfigurationManager.java"); + assertTrue(manager.contains("static ConfigurationManager instance")); + assertTrue(manager.contains("ConfigurationManager getInstance()")); + assertTrue(manager.contains("ConfigStorageStrategy storageStrategy")); + assertTrue(manager.contains("setStorageStrategy(ConfigStorageStrategy")); + assertTrue(manager.contains("saveConfig(String key, String value)")); + assertTrue(manager.contains("loadConfig(String key)")); + } + + @Test + @DisplayName("Template + Observer Patterns") + void adaptsTemplateAndObserverTogether() { + Path output = + adapt( + "testcase_13_template_observer_pattern", + Set.of("template", "observer"), + Set.of(ALLOW_ADDITIONAL_PARAMETERS)); + + String dataPipeline = readFileContent(output, "DataPipeline.java"); + String pipelineObserver = readFileContent(output, "PipelineObserver.java"); + String loggingObserver = readFileContent(output, "LoggingObserver.java"); + String metricsObserver = readFileContent(output, "MetricsObserver.java"); + + assertTrue(dataPipeline.contains("List observers")); + assertFalse(dataPipeline.contains("List observers")); + assertFalse(dataPipeline.contains("List observers")); + assertEquals(1, count(dataPipeline, "boolean subscribe(PipelineObserver")); + assertEquals(1, count(dataPipeline, "boolean unsubscribe(PipelineObserver")); + assertTrue(dataPipeline.contains("observer.onPipelineStage(null);")); + assertFalse(dataPipeline.contains("observer.update();")); + assertFalse(dataPipeline.contains("primitiveOperation1")); + assertFalse(dataPipeline.contains("primitiveOperation2")); + + assertFalse(pipelineObserver.contains("void update(")); + assertFalse(loggingObserver.contains("void update(")); + assertFalse(metricsObserver.contains("void update(")); + } + + private Path adapt( + String fixtureName, + Set mappings, + Set additionalConformanceParameters) { + Path fixture = FIXTURE_ROOT.resolve(fixtureName); + Path output = temporaryDirectory.resolve(fixtureName); + Set conformanceParameters = + new LinkedHashSet<>(DEFAULT_CONFORMANCE_PARAMETERS); + conformanceParameters.addAll(additionalConformanceParameters); + + new CodeAdapter(DEFAULT_ADAPTER_PARAMETERS, conformanceParameters) + .adapt( + fixture.resolve("Reference.cd").toFile(), + fixture.resolve("Concrete.cd").toFile(), + mappings, + fixture.resolve("adapter"), + fixture.resolve("concrete"), + output); + return output; + } + + private static Stream controllerWorkerCases() { + return Stream.of( + Arguments.of( + "controller-to-workers", "testcase_7_controller_worker_observer", true), + Arguments.of( + "workers-to-controller", + "testcase_8_controller_worker_observer_reversed", + false)); + } + + private static void assertContainsEither(String content, String first, String second) { + assertTrue(content.contains(first) || content.contains(second), content); + } + + private static int count(String content, String needle) { + return content.split(java.util.regex.Pattern.quote(needle), -1).length - 1; + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/TopCodeComposerTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/TopCodeComposerTest.java new file mode 100644 index 00000000..ce7d6e88 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/TopCodeComposerTest.java @@ -0,0 +1,238 @@ +package de.monticore.codeAdaption; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class TopCodeComposerTest extends AdapterAbstractTest { + + @TempDir Path temporaryDirectory; + + private TopCodeComposer composer; + + @BeforeEach + void setUp() { + initMills(); + composer = new TopCodeComposer(new AdaptedCodeMerger()); + } + + @Test + void emitsAdaptedTypeDirectlyWhenConcreteHwcIsMissing() throws IOException { + ASTOrdinaryCompilationUnit adapted = + javaSource("adapted/Account.java", "package target; class Account { void adapted() {} }"); + + Set result = + composer.compose(Set.of(), linkedSet(adapted), concreteIndex("class Account;")); + + assertEquals(Set.of("Account"), typeNames(result)); + assertTrue(JavaLoader.print(result.iterator().next()).contains("void adapted()")); + } + + @Test + void createsTopCompanionWithoutMergingMembersOrRewritingPublicTypeReferences() + throws IOException { + ASTOrdinaryCompilationUnit concrete = + javaSource( + "concrete/Account.java", + "package target; class Account { int handwritten; void own() {} }"); + ASTOrdinaryCompilationUnit adapted = + javaSource( + "adapted/Account.java", + "package target; class Account { Account() {} Account copy(Account value) { return new Account(); } int adapted; }"); + + Set result = + composer.compose( + linkedSet(concrete), linkedSet(adapted), concreteIndex("class Account;")); + + String handwritten = JavaLoader.print(unitNamed(result, "Account")); + String top = JavaLoader.print(unitNamed(result, "AccountTOP")); + assertTrue(handwritten.contains("class Account extends AccountTOP")); + assertTrue(handwritten.contains("int handwritten")); + assertFalse(handwritten.contains("int adapted")); + assertTrue(top.contains("class AccountTOP")); + assertTrue(top.contains("AccountTOP()")); + assertTrue(top.contains("Account copy(Account value)")); + assertTrue(top.contains("new Account()")); + } + + @Test + void keepsExistingReferenceTopWhenNoConcreteHwcExists() throws IOException { + ASTOrdinaryCompilationUnit adapted = + javaSource( + "adapted/Account.java", "package target; class Account extends AccountTOP {}"); + + Set result = + composer.compose(Set.of(), linkedSet(adapted), concreteIndex("class Account;")); + + assertEquals(Set.of("Account"), typeNames(result)); + assertTrue(JavaLoader.print(result.iterator().next()).contains("extends AccountTOP")); + } + + @Test + void shiftsReferenceTopToTopTopWhenConcreteHwcOwnsThePublicType() throws IOException { + ASTOrdinaryCompilationUnit concrete = + javaSource( + "concrete/Account.java", + "package target; class Account extends AccountTOP { void own() {} }"); + ASTOrdinaryCompilationUnit adapted = + javaSource( + "adapted/Account.java", + "package target; class Account extends AccountTOP { void adapted() {} }"); + + Set result = + composer.compose( + linkedSet(concrete), linkedSet(adapted), concreteIndex("class Account;")); + + String handwritten = JavaLoader.print(unitNamed(result, "Account")); + String top = JavaLoader.print(unitNamed(result, "AccountTOP")); + assertTrue(handwritten.contains("class Account extends AccountTOP")); + assertTrue(top.contains("class AccountTOP extends AccountTOPTOP")); + } + + @Test + void preservesUnrelatedQualifiedTopSuperclassWhenCreatingTopCompanion() throws IOException { + ASTOrdinaryCompilationUnit concrete = + javaSource("concrete/Account.java", "package target; class Account {}"); + ASTOrdinaryCompilationUnit adapted = + javaSource( + "adapted/Account.java", + "package target; class Account extends external.AccountTOP {}"); + + Set result = + composer.compose( + linkedSet(concrete), linkedSet(adapted), concreteIndex("class Account;")); + + String top = JavaLoader.print(unitNamed(result, "AccountTOP")); + assertTrue(top.contains("class AccountTOP extends external.AccountTOP"), top); + assertFalse(top.contains("extends AccountTOPTOP"), top); + } + + @Test + void preservesFluentPublicSelfTypeInGeneratedTopCode() throws IOException { + ASTOrdinaryCompilationUnit concrete = + javaSource( + "concrete/PersonBuilder.java", + "package target; class PersonBuilder extends PersonBuilderTOP { void validate() {} }"); + ASTOrdinaryCompilationUnit adapted = + javaSource( + "adapted/PersonBuilder.java", + "package target; class PersonBuilder {" + + " private String name;" + + " PersonBuilder setName(String name) { this.name = name; return this; }" + + " void decorate(Support support) { support.decorate(this); }" + + " }"); + + TopCodeComposer.CompositionResult result = + composer.composeWithSelfTypeBindings( + linkedSet(concrete), + linkedSet(adapted), + concreteIndex("class PersonBuilder;")); + Path output = temporaryDirectory.resolve("fluent-output"); + JavaLoader.printAST(result.code(), output); + Path packagePath = output.resolve("target"); + Files.writeString( + packagePath.resolve("Support.java"), + "package target; class Support { void decorate(PersonBuilder builder) {} }"); + Files.writeString( + packagePath.resolve("Usage.java"), + "package target; class Usage { void use() {" + + " new PersonBuilder().setName(\"Ada\").validate();" + + " } }"); + + new OutputCodeService().cleanCode(output, result.topToPublicSelfTypes()); + + String top = Files.readString(packagePath.resolve("PersonBuilderTOP.java")); + assertTrue(top.contains("return ((PersonBuilder) (this));"), top); + assertTrue(top.contains("support.decorate(((PersonBuilder) (this)));"), top); + assertGeneratedJavaCompiles(output); + } + + @Test + void rejectsConcreteClassWithUnrelatedSuperclass() throws IOException { + ASTOrdinaryCompilationUnit concrete = + javaSource( + "concrete/Account.java", "package target; class Account extends ExistingBase {}"); + ASTOrdinaryCompilationUnit adapted = + javaSource("adapted/Account.java", "package target; class Account {}"); + + CodeAdaptationException exception = + assertThrows( + CodeAdaptationException.class, + () -> + composer.compose( + linkedSet(concrete), linkedSet(adapted), concreteIndex("class Account;"))); + + assertTrue(exception.getMessage().contains("ExistingBase")); + } + + @Test + void rejectsTopSuperclassImportedFromAnotherPackage() throws IOException { + ASTOrdinaryCompilationUnit concrete = + javaSource( + "concrete/PersonBuilder.java", + "package target; import external.PersonBuilderTOP;" + + " class PersonBuilder extends PersonBuilderTOP {}"); + ASTOrdinaryCompilationUnit adapted = + javaSource("adapted/PersonBuilder.java", "package target; class PersonBuilder {}"); + + CodeAdaptationException exception = + assertThrows( + CodeAdaptationException.class, + () -> + composer.compose( + linkedSet(concrete), + linkedSet(adapted), + concreteIndex("class PersonBuilder;"))); + + assertTrue(exception.getMessage().contains("PersonBuilderTOP")); + } + + private ASTOrdinaryCompilationUnit javaSource(String relativePath, String source) + throws IOException { + Path file = temporaryDirectory.resolve(relativePath); + Files.createDirectories(file.getParent()); + Files.writeString(file, source); + return JavaLoader.loadJava(file.toFile()); + } + + private CDModelIndex concreteIndex(String declarations) throws IOException { + Path model = temporaryDirectory.resolve("Concrete.cd"); + Files.writeString(model, "classdiagram Concrete { " + declarations + " }"); + return CDModelIndex.of(JavaLoader.parseCD(model.toString())); + } + + private ASTOrdinaryCompilationUnit unitNamed( + Set units, String typeName) { + return units.stream() + .filter( + unit -> + unit.getTypeDeclarationList().stream() + .anyMatch(type -> type.getName().equals(typeName))) + .findFirst() + .orElseThrow(); + } + + private Set typeNames(Set units) { + LinkedHashSet names = new LinkedHashSet<>(); + units.forEach( + unit -> unit.getTypeDeclarationList().forEach(type -> names.add(type.getName()))); + return names; + } + + private static LinkedHashSet linkedSet(T... values) { + return new LinkedHashSet<>(java.util.List.of(values)); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/ConcretizationServiceTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/ConcretizationServiceTest.java new file mode 100644 index 00000000..444d8818 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/ConcretizationServiceTest.java @@ -0,0 +1,163 @@ +package de.monticore.codeAdaption.context; + +import static de.monticore.cdconformance.CDConfParameter.ADAPTED_NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.ALLOW_CARD_RESTRICTION; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.METHOD_OVERLOADING; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.SRC_TARGET_ASSOC_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STRICT_PARAMETER_ORDER; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import java.nio.file.Path; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class ConcretizationServiceTest extends AdapterAbstractTest { + + private static final Path ROOT = + Path.of("src/test/resources/de/monticore/codeAdaption/cdconcretization"); + + private Set parameters; + + @BeforeEach + void setUp() { + initMills(); + parameters = + Set.of( + STEREOTYPE_MAPPING, + NAME_MAPPING, + SRC_TARGET_ASSOC_MAPPING, + INHERITANCE, + ALLOW_CARD_RESTRICTION, + METHOD_OVERLOADING, + ADAPTED_NAME_MAPPING, + ALLOW_ADDITIONAL_PARAMETERS); + } + + @Test + void completedInheritanceIsVisibleInTheRebuiltIndex() { + ASTCDCompilationUnit completed = + complete( + "inheritance/AttributeInheritanceRef.cd", + "inheritance/AttributeInheritanceConc.cd"); + CDModelIndex index = CDModelIndex.of(completed); + + assertTrue(index.hasType("Person")); + assertEquals(Set.of("Person"), index.directParentNames("Teacher")); + } + + @Test + void upstreamCompletionDoesNotSynthesizeReverseForEachFields() { + ASTCDCompilationUnit completed = + complete( + "evaluation/staticDelegator/attrWorkaround/StaticDelegatorRef.cd", + "evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsConc.cd"); + CDModelIndex index = CDModelIndex.of(completed); + + assertTrue(index.attribute("MyLanguageMill", "foo").isEmpty()); + assertTrue(index.attribute("MyLanguageMill", "_foo").isPresent()); + } + + @Test + void completedEnumConstantIsVisibleInTheRebuiltIndex() { + ASTCDCompilationUnit completed = + complete("types/enums/EnumMemberMissingRef.cd", "types/enums/EnumMemberMissingConc.cd"); + CDModelIndex index = CDModelIndex.of(completed); + + assertEquals( + Set.of("RED", "GREEN", "YELLOW", "BLUE", "PURPLE"), + index.enums().get(0).getCDEnumConstantList().stream() + .map(constant -> constant.getName()) + .collect(java.util.stream.Collectors.toSet())); + } + + @Test + void completedInterfaceIsVisibleInTheRebuiltIndex() { + ASTCDCompilationUnit completed = + complete("types/valid/TypeMissingRef.cd", "types/valid/TypeMissingConc.cd"); + CDModelIndex index = CDModelIndex.of(completed); + + assertTrue(index.directParentNames("Course").contains("Teachable")); + } + + @Test + void upstreamCompletionDoesNotSynthesizeMultiIncarnationAccountAlias() { + ASTCDCompilationUnit completed = + complete( + "evaluation/banking/BankingRef.cd", + "evaluation/banking/multiInc/BankingConc.cd"); + CDModelIndex index = CDModelIndex.of(completed); + + assertTrue(index.attribute("Transaction", "sourceAccount").isEmpty()); + } + + @Test + void upstreamCompletionDoesNotSynthesizeCrossReferenceForEachMethod() { + ASTCDCompilationUnit completed = + complete( + "evaluation/cross-references/MicroserviceRef.cd", + "evaluation/cross-references/MicroserviceConc.cd"); + CDModelIndex index = CDModelIndex.of(completed); + + assertTrue(index.methods("UserService", "sendToOrderService").isEmpty()); + } + + @ParameterizedTest(name = "allow additional parameters = {0}") + @ValueSource(booleans = {false, true}) + void visitorCompletionProducesAllOverloads(boolean allowAdditionalParameters) { + Set completionParameters = new LinkedHashSet<>(parameters); + if (!allowAdditionalParameters) { + completionParameters.remove(ALLOW_ADDITIONAL_PARAMETERS); + } + completionParameters.add(STRICT_PARAMETER_ORDER); + + assertVisitorCompletion(completionParameters); + } + + private void assertVisitorCompletion(Set completionParameters) { + ASTCDCompilationUnit referenceCD = + JavaLoader.parseCD(ROOT.resolve("evaluation/visitor/VisitorRef.cd").toString()); + ASTCDCompilationUnit concreteCD = + JavaLoader.parseCD(ROOT.resolve("evaluation/visitor/VisitorConc.cd").toString()); + ASTCDCompilationUnit originalConcreteCD = concreteCD.deepClone(); + + ASTCDCompilationUnit completed = + new ConcretizationService(completionParameters) + .completeConcreteCD(concreteCD, referenceCD, Set.of("ref")); + CDModelIndex completedIndex = CDModelIndex.of(completed); + + assertEquals( + Set.of("visit(Node)", "visit(RootNode)", "visit(InnerNode)", "visit(LeafNode)"), + completedIndex.methods("NodeVisitor", "visit").stream() + .map(JavaSourceNames::methodSignature) + .collect(Collectors.toSet())); + assertTrue( + concreteCD.deepEquals(originalConcreteCD, false), + "Concretization must leave the caller-owned concrete AST unchanged"); + assertTrue( + CDModelIndex.of(concreteCD).methods("NodeVisitor").isEmpty(), + "Completed Visitor declarations must exist only in the returned clone"); + } + + private ASTCDCompilationUnit complete(String reference, String concrete) { + ASTCDCompilationUnit referenceCD = JavaLoader.parseCD(ROOT.resolve(reference).toString()); + ASTCDCompilationUnit concreteCD = JavaLoader.parseCD(ROOT.resolve(concrete).toString()); + return new ConcretizationService(parameters) + .completeConcreteCD(concreteCD, referenceCD, Set.of("ref")); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/GroupingMappingServiceTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/GroupingMappingServiceTest.java new file mode 100644 index 00000000..4d508ad4 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/GroupingMappingServiceTest.java @@ -0,0 +1,50 @@ +package de.monticore.codeAdaption.context; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.JavaLoader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class GroupingMappingServiceTest { + @TempDir Path temporaryDirectory; + + @BeforeEach + void initializeMills() { + AdapterAbstractTest.initMills(); + } + + @Test + void mapsConcreteSimpleNamesToGroupingSimpleNamesAndOmitsIdentityMappings() + throws IOException { + Path modelFile = temporaryDirectory.resolve("Model.cd"); + Files.writeString( + modelFile, "classdiagram Model { class Concrete; class Group; class Identity; }"); + ASTCDCompilationUnit model = JavaLoader.parseCD(modelFile.toString()); + var types = model.getCDDefinition().getCDClassesList(); + var group = types.stream().filter(type -> "Group".equals(type.getName())).findFirst().orElseThrow(); + var identity = + types.stream().filter(type -> "Identity".equals(type.getName())).findFirst().orElseThrow(); + IncarnationContext context = + new IncarnationContext( + "mapping", + Map.of(), + Map.of( + StableElementKey.type("Concrete"), + new IncarnationContext.MappedElement(StableElementKey.type("Group"), group.getSymbol()), + StableElementKey.type("Identity"), + new IncarnationContext.MappedElement( + StableElementKey.type("Identity"), identity.getSymbol()))); + + assertEquals(Map.of("Concrete", "Group"), new GroupingMappingService().compute(context)); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/MappingConformanceServiceTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/MappingConformanceServiceTest.java new file mode 100644 index 00000000..c55962e6 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/context/MappingConformanceServiceTest.java @@ -0,0 +1,73 @@ +package de.monticore.codeAdaption.context; + +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class MappingConformanceServiceTest extends AdapterAbstractTest { + + @TempDir Path tempDir; + + @BeforeEach + void setUp() { + initMills(); + LogStub.init(); + } + + @Test + void failedCheckRestoresFailQuickWithoutLeakingErrors() throws IOException { + ASTCDCompilationUnit reference = + parse("Reference.cd", "classdiagram Reference { class Required { int value; } }"); + ASTCDCompilationUnit concrete = + parse("Concrete.cd", "classdiagram Concrete { class Unrelated; }"); + MappingConformanceService service = new MappingConformanceService(Set.of(NAME_MAPPING)); + long errorsBefore = Log.getErrorCount(); + Log.enableFailQuick(true); + + boolean valid = + service.checkOrFalse(service.newChecker(), concrete, reference, "ref"); + + assertFalse(valid); + assertTrue(Log.isFailQuickEnabled()); + assertEquals(errorsBefore, Log.getErrorCount()); + } + + @Test + void manualFallbackDoesNotRetainFailedChecker() throws IOException { + ASTCDCompilationUnit reference = + parse("Reference.cd", "classdiagram Reference { class Required { int value; } }"); + ASTCDCompilationUnit concrete = + parse("Concrete.cd", "classdiagram Concrete { class Unrelated; }"); + MappingConformanceService service = new MappingConformanceService(Set.of(NAME_MAPPING)); + + AdaptationContextFactory.AdaptationContextResult result = + new AdaptationContextFactory(Set.of(NAME_MAPPING), service) + .buildResults( + CDModelIndex.of(reference), CDModelIndex.of(concrete), Set.of("ref"), true) + .get("ref"); + + assertFalse(result.conformanceValid()); + assertEquals(null, result.checker()); + } + + private ASTCDCompilationUnit parse(String name, String source) throws IOException { + Path path = tempDir.resolve(name); + Files.writeString(path, source); + return JavaLoader.parseCD(path.toString()); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/dependency/ReferenceCodeDependencySelectorTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/dependency/ReferenceCodeDependencySelectorTest.java new file mode 100644 index 00000000..2add8dbb --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/dependency/ReferenceCodeDependencySelectorTest.java @@ -0,0 +1,130 @@ +package de.monticore.codeAdaption.dependency; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class ReferenceCodeDependencySelectorTest extends AdapterAbstractTest { + @TempDir Path tempDir; + + @BeforeEach + void setUp() { + initMills(); + } + + @Test + void selectsTransitiveSourceLocalHelpersAndReferenceCDUses() throws IOException { + write( + "p/Service.java", + """ + package p; + class Service { + Entity entity; + ServiceSupport support = new ServiceSupport(); + Class supportType = ServiceSupport.class; + } + """); + write( + "p/ServiceSupport.java", + """ + package p; + import q.SupportValue; + class ServiceSupport { + SupportValue create() { return new SupportValue(); } + } + """); + write( + "q/SupportValue.java", + """ + package q; + public class SupportValue { + Entity convert(Entity value) { return value; } + } + """); + write("p/Unrelated.java", "package p; class Unrelated {}"); + + Set units = JavaLoader.readJavaCode(tempDir); + ReferenceCodeSelection selection = + new ReferenceCodeDependencySelector(tempDir, units, Set.of("Entity")) + .select(Set.of("p.Service")); + + assertEquals(Set.of("p.Service"), selection.rootTypeIdentities()); + assertEquals( + Set.of("p.ServiceSupport", "q.SupportValue"), selection.helperTypeIdentities()); + assertEquals(Set.of("p.ServiceSupport"), selection.dependencyEdges().get("p.Service")); + assertEquals(Set.of("q.SupportValue"), selection.dependencyEdges().get("p.ServiceSupport")); + assertEquals(Set.of("Entity"), selection.referencedCDTypeKeys()); + assertTrue(selection.diagnostics().isEmpty()); + } + + @Test + void allowsPackagePrivateHelperFromSamePackage() throws IOException { + write( + "same/Root.java", + """ + package same; + class Root { PackagePrivateHelper helper; } + """); + write( + "same/PackagePrivateHelper.java", + "package same; class PackagePrivateHelper {}"); + + Set units = JavaLoader.readJavaCode(tempDir); + ReferenceCodeSelection selection = + new ReferenceCodeDependencySelector(tempDir, units, Set.of()) + .select(Set.of("same.Root")); + + assertEquals(Set.of("same.PackagePrivateHelper"), selection.helperTypeIdentities()); + } + + @Test + void ignoresCoincidentalSourceTypeWhenJavaLangTypeIsUsed() throws IOException { + write("root/Root.java", "package root; class Root { String value; }"); + write("unrelated/String.java", "package unrelated; public class String {}"); + + Set units = JavaLoader.readJavaCode(tempDir); + ReferenceCodeSelection selection = + new ReferenceCodeDependencySelector(tempDir, units, Set.of()) + .select(Set.of("root.Root")); + + assertTrue(selection.helperTypeIdentities().isEmpty()); + } + + @Test + void ignoresCoincidentalSourceTypeWhenExternalTypeIsExplicitlyImported() + throws IOException { + write( + "root/Root.java", + """ + package root; + import java.time.Clock; + class Root { Clock clock; } + """); + write("unrelated/Clock.java", "package unrelated; public class Clock {}"); + + Set units = JavaLoader.readJavaCode(tempDir); + ReferenceCodeSelection selection = + new ReferenceCodeDependencySelector(tempDir, units, Set.of()) + .select(Set.of("root.Root")); + + assertTrue(selection.helperTypeIdentities().isEmpty()); + } + + private void write(String relativePath, String source) throws IOException { + Path target = tempDir.resolve(relativePath); + Files.createDirectories(target.getParent()); + Files.writeString(target, source); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/AssociationPatternAdaptationTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/AssociationPatternAdaptationTest.java new file mode 100644 index 00000000..af3419bb --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/AssociationPatternAdaptationTest.java @@ -0,0 +1,110 @@ +package de.monticore.codeAdaption.evaluation; + +import static de.monticore.cdconformance.CDConfParameter.ADAPTED_NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.SRC_TARGET_ASSOC_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.CodeAdapter; +import java.io.File; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class AssociationPatternAdaptationTest extends EvaluationAbstractTest { + + @BeforeEach + void setup() { + initMills(); + configureFixture("association_role_usage_mi"); + adapterParams = + Set.of(NAME_MATCHING, ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR, IGNORE_NON_MATCHED_TYPE); + confParameters = + Set.of( + STEREOTYPE_MAPPING, + NAME_MAPPING, + ADAPTED_NAME_MAPPING, + SRC_TARGET_ASSOC_MAPPING, + INHERITANCE); + } + + @Test + void adaptsMultiIncarnationAssociationRoleUsage() { + deleteRecursively(output); + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + + assertDoesNotThrow( + () -> + adapter.adapt( + referenceCD, + concreteCD, + Set.of("ref"), + refCodePath, + conCodePath, + output, + true, + true)); + + String adaptedTeacher = readFileContent(output, "Teacher.java"); + assertTrue(adaptedTeacher.contains("firstMathDept(MathDept fallback)")); + assertTrue(adaptedTeacher.contains("firstScienceDept(ScienceDept fallback)")); + assertTrue(adaptedTeacher.contains("for (MathDept department : this.mathDepts)")); + assertTrue( + adaptedTeacher.contains( + "for (ScienceDept department : this.scienceDepts)")); + assertFalse(adaptedTeacher.contains("this.departments)")); + + assertFalse(generatedFileNames(output).contains("TeacherTOP.java")); + assertNoAdapterMetadata(output); + } + + @Test + void adaptsBidirectionalAssociationRoleWritesOnBothOwners() { + configureFixture("association_bidirectional_role_write"); + deleteRecursively(output); + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + + assertDoesNotThrow( + () -> + adapter.adapt( + referenceCD, + concreteCD, + Set.of("ref"), + refCodePath, + conCodePath, + output, + true, + true)); + + String adaptedSprint = readFileContent(output, "Sprint.java"); + assertTrue(adaptedSprint.contains("replaceTicket(Ticket replacement)")); + assertTrue(adaptedSprint.contains("this.tickets = replacement")); + assertFalse(adaptedSprint.contains("this.tasks")); + + String adaptedTicket = readFileContent(output, "Ticket.java"); + assertTrue(adaptedTicket.contains("moveToSprint(Sprint replacement)")); + assertTrue(adaptedTicket.contains("this.sprint = replacement")); + assertFalse(adaptedTicket.contains("this.project")); + + assertFalse(generatedFileNames(output).contains("SprintTOP.java")); + assertFalse(generatedFileNames(output).contains("TicketTOP.java")); + assertNoAdapterMetadata(output); + } + + private void configureFixture(String fixture) { + referenceCD = new File(resourcesPath + fixture + "/Reference.cd"); + concreteCD = new File(resourcesPath + fixture + "/Concrete.cd"); + refCodePath = Path.of(resourcesPath + fixture + "/adapter"); + conCodePath = Path.of(resourcesPath + fixture + "/concrete"); + output = temporaryDirectory.resolve(fixture); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase1.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase1.java index caf2445f..021f8c05 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase1.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase1.java @@ -2,8 +2,10 @@ import static de.monticore.cdconformance.CDConfParameter.*; import static de.monticore.codeAdaption.utils.AdapterParam.*; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -import de.monticore.cd4code.CD4CodeMill; import de.monticore.codeAdaption.CodeAdapter; import java.io.File; import java.nio.file.Path; @@ -20,8 +22,9 @@ public void setup() { concreteCD = new File(resourcesPath + "testcase_1/Concrete.cd"); refCodePath = Path.of(resourcesPath + "testcase_1/reference"); conCodePath = Path.of(resourcesPath + "testcase_1/concrete"); - output = Path.of("target/codeAdapter/evaluation/testcase_1/"); - CD4CodeMill.init(); + output = temporaryDirectory.resolve("testcase_1"); + initMills(); + deleteRecursively(output); adapterParams = Set.of(NAME_MATCHING, ANNOTATION_MATCHING, INFIX_MATCHING, IGNORE_NON_MATCHED_VAR); confParameters = Set.of(INHERITANCE, STEREOTYPE_MAPPING, SRC_TARGET_ASSOC_MAPPING); @@ -32,6 +35,18 @@ public void setup() { public void evaluationCodeAdapterCaseStudy1Test() { Set mappings = Set.of("stud", "prof"); CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); - adapter.adapt(referenceCD, concreteCD, mappings, refCodePath, conCodePath, output); + assertDoesNotThrow( + () -> adapter.adapt(referenceCD, concreteCD, mappings, refCodePath, conCodePath, output)); + assertFalse(generatedJavaFilesRecursively(output).isEmpty()); + assertNoAdapterMetadata(output); + assertTrue(generatedFileNames(output).contains("ProfessorRegistration.java")); + assertTrue(generatedFileNames(output).contains("StudentRegistration.java")); + assertTrue( + readFileContent(output, "ProfessorRegistration.java") + .contains("register(Professor professor)")); + assertTrue( + readFileContent(output, "StudentRegistration.java") + .contains("register(Student student)")); + assertGeneratedJavaCompiles(output); } } diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase2a.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase2a.java index a752ad75..886751b3 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase2a.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CodeAdapterTestCase2a.java @@ -2,12 +2,15 @@ import static de.monticore.cdconformance.CDConfParameter.*; import static de.monticore.codeAdaption.utils.AdapterParam.*; -import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import de.monticore.cd4code.CD4CodeMill; import de.monticore.codeAdaption.CodeAdapter; -import de.monticore.codeAdaption.utils.Generator; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Set; import org.junit.jupiter.api.BeforeEach; @@ -22,7 +25,7 @@ public void setup() { concreteCD = new File(resourcesPath + "testcase_2_cd4code/Concrete.cd"); refCodePath = Path.of(resourcesPath + "testcase_2_cd4code/hwc/entity"); conCodePath = Path.of(resourcesPath + "testcase_2_cd4code/concrete"); - output = Path.of("target/codeAdapter/evaluation/testcase_2_cd4code/"); + output = temporaryDirectory.resolve("testcase_2_cd4code"); CD4CodeMill.init(); adapterParams = Set.of(NAME_MATCHING, ANNOTATION_MATCHING, INFIX_MATCHING, IGNORE_NON_MATCHED_VAR); @@ -30,13 +33,86 @@ public void setup() { } @Test - @DisplayName("Evaluation Code Adapter Case Study 2 CD4Code") - public void evaluationCodeAdapterCaseStudy1Test() { + @DisplayName("Evaluation Code Adapter Case Study 2 without concrete Java") + public void adaptsAssociationRoleUsageWithoutRunningARegularGenerator() { Set mappings = Set.of("stud"); CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); - adapter.adapt(referenceCD, concreteCD, mappings, refCodePath, conCodePath, output); + deleteRecursively(output); - Path genCode = Path.of("target/codeAdapter/evaluation/testcase_2_cd4code/generated"); - Generator.generate(concreteCD, output, genCode); + assertDoesNotThrow( + () -> adapter.adaptWithoutConcreteCode(referenceCD, concreteCD, mappings, refCodePath, output)); + + String adaptedStudent = readFileContent(output, "Student.java"); + assertTrue(adaptedStudent.contains("package Concrete;")); + assertTrue(adaptedStudent.contains("class Student extends StudentTOP")); + assertTrue(adaptedStudent.contains("printHiwiRoles()")); + assertTrue(adaptedStudent.contains("for (HiwiRole hiwiRole : this.roles)")); + assertTrue(adaptedStudent.contains("roleNames = hiwiRole.name")); + assertFalse(adaptedStudent.contains("Role role")); + assertFalse(adaptedStudent.contains("printRoles()")); + + assertFalse(generatedFileNames(output).contains("StudentTOP.java")); + assertFalse(generatedFileNames(output).contains("HiwiRole.java")); + assertNoAdapterMetadata(output); + } + + @Test + void suppliedJavaEmptyConcreteDirectoryPreservesResourcesWithoutGeneration() + throws IOException { + Path emptyConcrete = Files.createDirectories(temporaryDirectory.resolve("concrete")); + Files.writeString(emptyConcrete.resolve("settings.txt"), "preserve-me"); + Path generatedOutput = temporaryDirectory.resolve("generated-with-resource"); + + new CodeAdapter(adapterParams, confParameters) + .adapt( + referenceCD, + concreteCD, + Set.of("stud"), + refCodePath, + emptyConcrete, + generatedOutput); + + assertTrue(Files.isRegularFile(generatedOutput.resolve("Concrete/Student.java"))); + assertFalse(Files.isRegularFile(generatedOutput.resolve("Concrete/StudentTOP.java"))); + assertTrue(Files.isRegularFile(generatedOutput.resolve("settings.txt"))); + } + + @Test + void emptyReferenceDirectoryDoesNotGenerateConcreteModel() throws IOException { + Path emptyReference = Files.createDirectories(temporaryDirectory.resolve("empty-reference")); + Path generatedOutput = temporaryDirectory.resolve("model-only-output"); + + new CodeAdapter(adapterParams, confParameters) + .adaptWithoutConcreteCode( + referenceCD, concreteCD, Set.of("stud"), emptyReference, generatedOutput); + + assertTrue(generatedJavaFiles(generatedOutput).isEmpty()); + } + + @Test + void topApiAddsInheritanceAndEmitsAdaptedImplementationAsTop() throws IOException { + Path concrete = Files.createDirectories(temporaryDirectory.resolve("top-concrete")); + Files.writeString( + concrete.resolve("Student.java"), "package Concrete; public class Student {}"); + Files.writeString( + concrete.resolve("StudentTOPTOP.java"), + "package Concrete; import java.util.LinkedHashSet; import java.util.Set; " + + "public abstract class StudentTOPTOP { " + + "protected Set roles = new LinkedHashSet<>(); }"); + Files.writeString( + concrete.resolve("HiwiRole.java"), + "package Concrete; public class HiwiRole { String name; }"); + Path topOutput = temporaryDirectory.resolve("top-output"); + + new CodeAdapter(adapterParams, confParameters) + .adaptWithTopSeparation( + referenceCD, concreteCD, Set.of("stud"), refCodePath, concrete, topOutput); + + String handwrittenStudent = readFileContent(topOutput, "Student.java"); + String adaptedTop = readFileContent(topOutput, "StudentTOP.java"); + assertTrue(handwrittenStudent.contains("class Student extends StudentTOP")); + assertTrue(adaptedTop.contains("class StudentTOP extends StudentTOPTOP")); + assertTrue(adaptedTop.contains("printHiwiRoles()")); + assertGeneratedJavaCompiles(topOutput); } } diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CombinedPatternEvaluationTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CombinedPatternEvaluationTest.java new file mode 100644 index 00000000..2a89b2e6 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/CombinedPatternEvaluationTest.java @@ -0,0 +1,159 @@ +package de.monticore.codeAdaption.evaluation; + +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STRICT_PARAMETER_ORDER; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.CodeAdapter; +import java.io.File; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** Executable evaluation wrappers for combined-pattern resource cases. */ +public class CombinedPatternEvaluationTest extends EvaluationAbstractTest { + + @BeforeEach + public void setup() { + initMills(); + adapterParams = + Set.of(NAME_MATCHING, ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR, IGNORE_NON_MATCHED_TYPE); + confParameters = + Set.of( + NAME_MAPPING, + INHERITANCE, + STEREOTYPE_MAPPING, + STRICT_PARAMETER_ORDER, + ALLOW_ADDITIONAL_PARAMETERS); + } + + @Test + @DisplayName("Evaluation Code Adapter Case Study 14 Adapter + Factory") + public void adapterFactoryCombinedCaseRuns() { + runCombinedCase("testcase_14_adapter_factory_combined", Set.of("adapter", "factory")); + assertEquals( + Set.of("CarrierAdapter.java", "LegacyCarrier.java", "ShippingFactory.java", "ShippingPort.java"), + generatedFileNames(output)); + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(output); + + String carrierAdapter = readFileContent(output, "CarrierAdapter.java"); + String shippingFactory = readFileContent(output, "ShippingFactory.java"); + String shippingPort = readFileContent(output, "ShippingPort.java"); + + assertTrue(carrierAdapter.contains("public class CarrierAdapter implements ShippingPort")); + assertTrue(carrierAdapter.contains("public CarrierAdapter(LegacyCarrier carrier)")); + assertTrue(carrierAdapter.contains("public boolean ship(String label, boolean insured)")); + assertTrue(carrierAdapter.contains("carrier.dispatch(label, insured)")); + assertTrue(shippingFactory.contains("public ShippingPort createCarrier(LegacyCarrier carrier)")); + assertTrue(shippingFactory.contains("return new CarrierAdapter(carrier);")); + assertTrue(shippingPort.contains("boolean ship(String label, boolean insured);")); + assertFalse(carrierAdapter.contains("specificRequest")); + assertFalse(carrierAdapter.contains("boolean request(")); + } + + @Test + @DisplayName("Evaluation Code Adapter Case Study 15 Composite + Decorator") + public void compositeDecoratorCombinedCaseRuns() { + runCombinedCase("testcase_15_composite_decorator_combined", Set.of("composite", "decorator")); + assertEquals( + Set.of( + "BorderDecorator.java", + "GroupNode.java", + "ImageNode.java", + "Renderable.java", + "RenderDecorator.java", + "ShadowDecorator.java", + "TextNode.java"), + generatedFileNames(output)); + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(output); + + String renderable = readFileContent(output, "Renderable.java"); + String groupNode = readFileContent(output, "GroupNode.java"); + String renderDecorator = readFileContent(output, "RenderDecorator.java"); + String borderDecorator = readFileContent(output, "BorderDecorator.java"); + String shadowDecorator = readFileContent(output, "ShadowDecorator.java"); + + assertTrue(renderable.contains("public interface Renderable")); + assertTrue(renderable.contains("String render(int depth);")); + assertTrue(groupNode.contains("public class GroupNode implements Renderable")); + assertTrue(groupNode.contains("List nodes")); + assertTrue(groupNode.contains("public void attach(Renderable node)")); + assertTrue(groupNode.contains("public void detach(Renderable node)")); + assertTrue(groupNode.contains("node.render(depth + 1)")); + assertTrue(renderDecorator.contains("public abstract class RenderDecorator implements Renderable")); + assertTrue(renderDecorator.contains("Renderable wrapped")); + assertTrue(renderDecorator.contains("return wrapped.render(depth);")); + assertTrue(borderDecorator.contains("public class BorderDecorator extends RenderDecorator")); + assertTrue(shadowDecorator.contains("public class ShadowDecorator extends RenderDecorator")); + assertFalse(borderDecorator.contains("abstract class BorderDecorator")); + assertFalse(shadowDecorator.contains("abstract class ShadowDecorator")); + assertFalse(groupNode.contains("Component")); + assertFalse(groupNode.contains("operation(")); + } + + @Test + @DisplayName("Evaluation Code Adapter Case Study 16 Observer + Command") + public void observerCommandCombinedCaseRuns() { + runCombinedCase("testcase_16_observer_command_combined", Set.of("observer", "command")); + assertEquals( + Set.of( + "CommandQueue.java", + "EmailCommand.java", + "EventBus.java", + "SmsCommand.java", + "WorkflowCommand.java"), + generatedFileNames(output)); + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(output); + + String eventBus = readFileContent(output, "EventBus.java"); + String commandQueue = readFileContent(output, "CommandQueue.java"); + String workflowCommand = readFileContent(output, "WorkflowCommand.java"); + String emailCommand = readFileContent(output, "EmailCommand.java"); + String smsCommand = readFileContent(output, "SmsCommand.java"); + + assertTrue(workflowCommand.contains("public interface WorkflowCommand")); + assertTrue(workflowCommand.contains("void run(String event, boolean async);")); + assertTrue(emailCommand.contains("public class EmailCommand implements WorkflowCommand")); + assertTrue(smsCommand.contains("public class SmsCommand implements WorkflowCommand")); + assertTrue(eventBus.contains("List listeners")); + assertTrue(eventBus.contains("public void subscribe(WorkflowCommand command)")); + assertTrue(eventBus.contains("public void unsubscribe(WorkflowCommand command)")); + assertTrue(eventBus.contains("public void publish(String event)")); + assertTrue(eventBus.contains("listener.run(event, false)")); + assertTrue(commandQueue.contains("List backlog")); + assertTrue(commandQueue.contains("public void schedule(WorkflowCommand command)")); + assertTrue(commandQueue.contains("public void drain(String event)")); + assertTrue(commandQueue.contains("command.run(event, false)")); + assertFalse(eventBus.contains("void update(")); + assertFalse(eventBus.contains("void notifyAll(")); + assertFalse(commandQueue.contains("execute(")); + assertFalse(commandQueue.contains("runAll(")); + } + + private void runCombinedCase(String caseName, Set mappings) { + referenceCD = new File(resourcesPath + caseName + "/Reference.cd"); + concreteCD = new File(resourcesPath + caseName + "/Concrete.cd"); + refCodePath = Path.of(resourcesPath + caseName + "/adapter"); + conCodePath = Path.of(resourcesPath + caseName + "/concrete"); + output = temporaryDirectory.resolve(caseName); + + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + assertDoesNotThrow( + () -> adapter.adapt(referenceCD, concreteCD, mappings, refCodePath, conCodePath, output)); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/ConfCheckerEvalTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/ConfCheckerEvalTest.java deleted file mode 100644 index b5a1f290..00000000 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/ConfCheckerEvalTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package de.monticore.codeAdaption.evaluation; - -import static de.monticore.cdconformance.CDConfParameter.*; - -import de.monticore.cd4code.CD4CodeMill; -import de.monticore.cdbasis._ast.ASTCDCompilationUnit; -import de.monticore.cdconformance.CDConformanceChecker; -import de.monticore.codeAdaption.utils.JavaLoader; -import de.se_rwth.commons.logging.Log; -import java.io.File; -import java.nio.file.Path; -import java.util.Set; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class ConfCheckerEvalTest extends EvaluationAbstractTest { - @BeforeEach - public void setup() { - referenceCD = new File(resourcesPath + "confCheckingCD/Reference.cd"); - concreteCD = new File(resourcesPath + "confCheckingCD/Concrete.cd"); - refCodePath = Path.of(resourcesPath + "confCheckingCD/reference"); - conCodePath = Path.of(resourcesPath + "confCheckingCD/concrete"); - output = Path.of("target/codeAdapter/evaluation/confCheckingCD/"); - CD4CodeMill.init(); - Log.init(); - confParameters = Set.of(INHERITANCE, STEREOTYPE_MAPPING, SRC_TARGET_ASSOC_MAPPING); - } - - @Test - @DisplayName("Evaluation conformance checker for class diagrams") - public void evaluationConformanceCheckerCDtest() { - long start = System.currentTimeMillis(); - Set mappings = Set.of("prof", "stud"); - ASTCDCompilationUnit conCD = JavaLoader.loadCD(concreteCD); - ASTCDCompilationUnit refCD = JavaLoader.loadCD(referenceCD); - - CDConformanceChecker conformanceChecker = new CDConformanceChecker(confParameters); - Assertions.assertTrue(conformanceChecker.checkConformance(conCD, refCD, mappings)); - double duration = (System.currentTimeMillis() - start) / 1000.0; - System.out.println("duration: " + duration); - } - - @Test - @DisplayName("Evaluation conformance checker design patterns") - public void evaluationConformanceCheckerCDtest2() { - long start = System.currentTimeMillis(); - Set mappings = Set.of("ci", "re"); - ASTCDCompilationUnit conCD = JavaLoader.loadCD( - new File(resourcesPath + "design-patterns/Graphic.cd")); - ASTCDCompilationUnit refCD = JavaLoader.loadCD( - new File(resourcesPath + "design-patterns/Composition.cd")); - - CDConformanceChecker conformanceChecker = new CDConformanceChecker(confParameters); - Assertions.assertTrue(conformanceChecker.checkConformance(conCD, refCD, mappings)); - - mappings = Set.of("npg"); - refCD = JavaLoader.loadCD(new File(resourcesPath + "design-patterns/Adapter.cd")); - Assertions.assertTrue(conformanceChecker.checkConformance(conCD, refCD, mappings)); - - - double duration = (System.currentTimeMillis() - start) / 1000.0; - System.out.println("duration: " + duration); - } -} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/DesignPatternTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/DesignPatternTest.java new file mode 100644 index 00000000..eeba1072 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/DesignPatternTest.java @@ -0,0 +1,84 @@ +package de.monticore.codeAdaption.evaluation; + +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.SRC_TARGET_ASSOC_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.INFIX_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.codeAdaption.CodeAdapter; +import java.io.File; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class DesignPatternTest extends EvaluationAbstractTest { + @BeforeEach + public void setup() { + initMills(); + adapterParams = + Set.of(NAME_MATCHING, ANNOTATION_MATCHING, INFIX_MATCHING, IGNORE_NON_MATCHED_VAR); + confParameters = Set.of(INHERITANCE, STEREOTYPE_MAPPING, SRC_TARGET_ASSOC_MAPPING); + } + + @Test + @DisplayName("Evaluation paper example: Composite pattern") + public void adaptsCompositePatternForBothMappings() { + configureFixture("Composition", "composition"); + + adaptWithoutRegularGeneration(Set.of("ci", "re")); + + assertTrue( + generatedFileNames(output) + .containsAll(Set.of("Circle.java", "Picture.java", "Rectangle.java"))); + assertFalse(generatedFileNames(output).contains("Graphic.java")); + String picture = readFileContent(output, "Picture.java"); + assertTrue(picture.contains("class Picture implements Graphic")); + assertTrue(picture.contains("void render()")); + assertTrue(picture.contains("List graphicList")); + assertFalse(picture.contains("Component")); + assertFalse(picture.contains("execute(")); + assertNoAdapterMetadata(output); + } + + @Test + @DisplayName("Evaluation paper example: Adapter pattern") + public void adaptsAdapterPattern() { + configureFixture("Adapter", "adapter"); + + adaptWithoutRegularGeneration(Set.of("npg")); + + assertTrue( + generatedFileNames(output) + .containsAll(Set.of("NPGAdapter.java", "NPGImage.java"))); + assertFalse(generatedFileNames(output).contains("FlexImage.java")); + String adapter = readFileContent(output, "NPGAdapter.java"); + assertTrue(adapter.contains("class NPGAdapter implements FlexImage")); + assertTrue(adapter.contains("void flex()")); + assertFalse(adapter.contains("Target")); + assertFalse(adapter.contains("operation(")); + assertNoAdapterMetadata(output); + } + + private void configureFixture(String referenceName, String codeDirectory) { + referenceCD = new File(resourcesPath + "design_patterns/" + referenceName + ".cd"); + concreteCD = new File(resourcesPath + "design_patterns/DesignPatterns.cd"); + refCodePath = Path.of(resourcesPath + "design_patterns/hwc/" + codeDirectory); + conCodePath = Path.of(resourcesPath + "design_patterns/concrete"); + output = temporaryDirectory.resolve("design_patterns").resolve(codeDirectory); + } + + private void adaptWithoutRegularGeneration(Set mappings) { + deleteRecursively(output); + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + assertDoesNotThrow( + () -> adapter.adapt(referenceCD, concreteCD, mappings, refCodePath, conCodePath, output)); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/EvaluationAbstractTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/EvaluationAbstractTest.java index 0c46345a..e3ac304e 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/EvaluationAbstractTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/EvaluationAbstractTest.java @@ -6,8 +6,11 @@ import java.io.File; import java.nio.file.Path; import java.util.Set; +import org.junit.jupiter.api.io.TempDir; public abstract class EvaluationAbstractTest extends AdapterAbstractTest { + @TempDir protected Path temporaryDirectory; + protected final String resourcesPath = "src/test/resources/de/monticore/codeAdaption/evaluation/"; protected File referenceCD; protected File concreteCD; diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/LargeFulfillmentSystemEvaluationTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/LargeFulfillmentSystemEvaluationTest.java new file mode 100644 index 00000000..9a162d12 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/evaluation/LargeFulfillmentSystemEvaluationTest.java @@ -0,0 +1,302 @@ +package de.monticore.codeAdaption.evaluation; + +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STRICT_PARAMETER_ORDER; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE_MEMBER; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.*; + +import de.monticore.codeAdaption.CodeAdapter; +import java.io.File; +import java.io.StringWriter; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import javax.tools.JavaCompiler; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** End-to-end evaluation of four interacting patterns in a fulfilment workflow. */ +public class LargeFulfillmentSystemEvaluationTest extends EvaluationAbstractTest { + + private static final String CASE_NAME = "testcase_17_fulfillment_platform"; + + private static final Set EXPECTED_TYPES = + Set.of( + "AuditLog.java", + "AuditTrailListener.java", + "BookCarrierCommand.java", + "CarrierGateway.java", + "CarrierGatewayFactory.java", + "CustomerNotificationListener.java", + "CustomerNotifier.java", + "DeliveryItem.java", + "DeliveryOrder.java", + "FastestRoutePolicy.java", + "FulfillmentCommand.java", + "FulfillmentCommandQueue.java", + "FulfillmentCoordinator.java", + "FulfillmentScenario.java", + "InventoryLedger.java", + "LegacyCarrierApi.java", + "LegacyCarrierGatewayAdapter.java", + "OperationsDashboardListener.java", + "RecordShipmentCommand.java", + "ReserveInventoryCommand.java", + "RoutePlanner.java", + "RoutePolicy.java", + "ShipmentEventListener.java", + "ShipmentEventPublisher.java", + "ShipmentRecord.java", + "ShipmentRepository.java", + "SustainableRoutePolicy.java"); + + @BeforeEach + public void setup() { + initMills(); + adapterParams = + Set.of( + NAME_MATCHING, + ANNOTATION_MATCHING, + IGNORE_NON_MATCHED_VAR, + IGNORE_NON_MATCHED_TYPE, + IGNORE_NON_MATCHED_TYPE_MEMBER); + confParameters = + Set.of( + NAME_MAPPING, + INHERITANCE, + STEREOTYPE_MAPPING, + STRICT_PARAMETER_ORDER, + ALLOW_ADDITIONAL_PARAMETERS); + } + + @Test + @DisplayName("Evaluation Code Adapter Case Study 17 Fulfilment Platform") + public void adaptsCompleteFulfillmentPlatform() { + configureFixture(); + deleteRecursively(output); + assertFixtureRequiresAdaptation(); + + CodeAdapter adapter = new CodeAdapter(adapterParams, confParameters); + assertDoesNotThrow( + () -> + adapter.adapt( + referenceCD, + concreteCD, + Set.of("observer", "command", "strategy", "adapter"), + refCodePath, + conCodePath, + output)); + + assertEquals(EXPECTED_TYPES, generatedFileNames(output)); + assertNoAdapterMetadata(output); + assertGeneratedJavaCompiles(generatedJavaFilesRecursively(output)); + + assertObserverAdaptation(); + assertCommandAdaptation(); + assertStrategyAdaptation(); + assertAdapterAndFactoryAdaptation(); + assertSubsystemInteraction(); + assertObsoleteReferenceNamesAreAbsent(); + assertDoesNotThrow(this::assertScenarioBehavior); + } + + /** + * Guards the evaluation against becoming a copy-through test. Each mapped pattern deliberately + * starts with at least one missing concrete member that only adapted reference code can supply. + */ + private void assertFixtureRequiresAdaptation() { + assertFalse( + readFileContent(conCodePath, "ShipmentEventPublisher.java") + .contains("List listeners")); + assertFalse( + readFileContent(conCodePath, "ShipmentEventPublisher.java") + .contains("void publishShipment(")); + assertFalse( + readFileContent(conCodePath, "FulfillmentCommandQueue.java") + .contains("List commands")); + assertFalse( + readFileContent(conCodePath, "RoutePlanner.java").contains("private RoutePolicy policy;")); + assertFalse( + readFileContent(conCodePath, "LegacyCarrierGatewayAdapter.java") + .contains("createConsignment(")); + } + + private void configureFixture() { + referenceCD = new File(resourcesPath + CASE_NAME + "/Reference.cd"); + concreteCD = new File(resourcesPath + CASE_NAME + "/Concrete.cd"); + refCodePath = Path.of(resourcesPath + CASE_NAME + "/adapter"); + conCodePath = Path.of(resourcesPath + CASE_NAME + "/concrete"); + output = temporaryDirectory.resolve(CASE_NAME); + } + + private void assertObserverAdaptation() { + String listener = readFileContent(output, "ShipmentEventListener.java"); + String publisher = readFileContent(output, "ShipmentEventPublisher.java"); + String auditListener = readFileContent(output, "AuditTrailListener.java"); + + assertTrue(listener.contains("public interface ShipmentEventListener")); + assertTrue(listener.contains("void onShipmentEvent(ShipmentRecord shipment);")); + assertTrue(publisher.contains("List listeners")); + assertTrue(publisher.contains("void subscribe(ShipmentEventListener listener)")); + assertTrue(publisher.contains("void publishShipment(ShipmentRecord shipment)")); + assertTrue(publisher.contains("observer.onShipmentEvent(shipment)")); + assertTrue(auditListener.contains("implements ShipmentEventListener")); + } + + private void assertCommandAdaptation() { + String command = readFileContent(output, "FulfillmentCommand.java"); + String queue = readFileContent(output, "FulfillmentCommandQueue.java"); + String recordCommand = readFileContent(output, "RecordShipmentCommand.java"); + + assertTrue(command.contains("public interface FulfillmentCommand")); + assertTrue(command.contains("boolean run();")); + assertTrue(queue.contains("List commands")); + assertTrue(queue.contains("void schedule(FulfillmentCommand command)")); + assertTrue(queue.contains("boolean drain()")); + assertTrue(queue.contains("command.run()")); + assertTrue(recordCommand.contains("publisher.publishShipment(shipment)")); + } + + private void assertStrategyAdaptation() { + String policy = readFileContent(output, "RoutePolicy.java"); + String planner = readFileContent(output, "RoutePlanner.java"); + + assertTrue(policy.contains("public interface RoutePolicy")); + assertTrue(policy.contains("String selectRoute(DeliveryOrder order);")); + assertTrue(planner.contains("RoutePolicy policy")); + assertTrue(planner.contains("void usePolicy(RoutePolicy policy)")); + assertTrue(planner.contains("String planRoute(DeliveryOrder order)")); + assertTrue(planner.contains("policy.selectRoute(order)")); + } + + private void assertAdapterAndFactoryAdaptation() { + String gateway = readFileContent(output, "CarrierGateway.java"); + String carrierAdapter = readFileContent(output, "LegacyCarrierGatewayAdapter.java"); + String factory = readFileContent(output, "CarrierGatewayFactory.java"); + + assertTrue(gateway.contains("public interface CarrierGateway")); + assertTrue( + gateway.contains("String createConsignment(DeliveryOrder order, String route);")); + assertTrue(carrierAdapter.contains("implements CarrierGateway")); + assertTrue(carrierAdapter.contains("LegacyCarrierApi legacyCarrier")); + assertTrue( + carrierAdapter.contains("legacyCarrier.createLegacyConsignment(order, route)")); + assertTrue(factory.contains("CarrierGateway createGateway(LegacyCarrierApi legacyCarrier)")); + assertTrue(factory.contains("return new LegacyCarrierGatewayAdapter(legacyCarrier);")); + } + + private void assertSubsystemInteraction() { + String coordinator = readFileContent(output, "FulfillmentCoordinator.java"); + String scenario = readFileContent(output, "FulfillmentScenario.java"); + + assertTrue(coordinator.contains("routePlanner.planRoute(order)")); + assertTrue(coordinator.contains("new BookCarrierCommand(carrierGateway, order, route)")); + assertTrue(coordinator.contains("commandQueue.schedule(new ReserveInventoryCommand")); + assertTrue(coordinator.contains("new RecordShipmentCommand")); + assertTrue(coordinator.contains("commandQueue.drain()")); + assertTrue(coordinator.contains("ShipmentEventPublisher eventPublisher")); + + assertTrue(scenario.contains("public static boolean run()")); + assertTrue(scenario.contains("publisher.subscribe(new AuditTrailListener")); + assertTrue(scenario.contains("new CarrierGatewayFactory().createGateway")); + assertTrue(scenario.contains("coordinator.fulfill(order)")); + assertTrue(scenario.contains("inventory.reservedUnits")); + assertTrue(scenario.contains("shipments.containsOrder")); + assertTrue(scenario.contains("auditLog.containsOrder")); + assertTrue(scenario.contains("notifier.notified")); + } + + private void assertObsoleteReferenceNamesAreAbsent() { + String allSources = + String.join( + "\n", + generatedJavaFilesRecursively(output).stream() + .map(path -> readFileContent(output, path.getFileName().toString())) + .toList()); + + for (String obsoleteDeclaration : + Set.of( + "interface Observer", + "class Subject", + "interface Command {", + "class Invoker", + "interface Strategy {", + "class Context", + "interface Target", + "class Adaptee", + "class Adapter")) { + assertFalse( + allSources.contains(obsoleteDeclaration), + () -> "Obsolete reference declaration remains: " + obsoleteDeclaration); + } + for (String obsoleteCall : + Set.of( + ".update(", + "notifyObservers(", + ".execute(", + "runAll(", + "specificRequest(", + ".request(")) { + assertFalse( + allSources.contains(obsoleteCall), + () -> "Obsolete reference call remains: " + obsoleteCall); + } + } + + /** Compiles the generated platform and invokes its two subsystem-level behavioral oracles. */ + private void assertScenarioBehavior() throws Exception { + List javaFiles = generatedJavaFilesRecursively(output); + Path classes = temporaryDirectory.resolve("fulfillment-classes"); + Files.createDirectories(classes); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + assertNotNull(compiler, "Tests must run on a JDK"); + StringWriter compilerOutput = new StringWriter(); + try (StandardJavaFileManager fileManager = + compiler.getStandardFileManager(null, null, StandardCharsets.UTF_8)) { + Boolean compiled = + compiler + .getTask( + compilerOutput, + fileManager, + null, + List.of( + "-d", + classes.toString(), + "-classpath", + System.getProperty("java.class.path")), + null, + fileManager.getJavaFileObjectsFromPaths(javaFiles)) + .call(); + assertEquals( + Boolean.TRUE, + compiled, + () -> "Generated fulfilment platform does not compile:\n" + compilerOutput); + } + + try (URLClassLoader loader = + new URLClassLoader(new URL[] {classes.toUri().toURL()}, getClass().getClassLoader())) { + Class scenario = + Class.forName( + "de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete" + + ".FulfillmentScenario", + true, + loader); + assertEquals(Boolean.TRUE, scenario.getMethod("run").invoke(null)); + assertEquals(Boolean.TRUE, scenario.getMethod("runQueueFailureAndReuse").invoke(null)); + } + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/BasicUpdateHandlerTestAccess.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/BasicUpdateHandlerTestAccess.java new file mode 100644 index 00000000..c3118776 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/BasicUpdateHandlerTestAccess.java @@ -0,0 +1,36 @@ +package de.monticore.codeAdaption.handler; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.codeAdaption.handler.multiIncarnation.IncarnationContext; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.symboltable.ISymbol; +import java.util.Map; +import java.util.Optional; + +/** Test access to package-private handler resolution without a production inheritance hook. */ +public final class BasicUpdateHandlerTestAccess { + + private BasicUpdateHandlerTestAccess() {} + + public static Optional resolve( + IncarnationContext incarnationContext, + ASTCDCompilationUnit reference, + ASTCDCompilationUnit concrete, + Map selection, + boolean useCommonParentForMultipleIncarnations, + ISymbol symbol) { + BasicUpdateHandler handler = + new BasicUpdateHandler( + CDModelIndex.of(reference), + CDModelIndex.of(concrete), + CDModelIndex.of(concrete), + null, + null, + null, + incarnationContext, + selection, + useCommonParentForMultipleIncarnations); + return handler.getSymbolFromContext(symbol); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/JavaTypeUpdateServiceTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/JavaTypeUpdateServiceTest.java new file mode 100644 index 00000000..14c30a2c --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/JavaTypeUpdateServiceTest.java @@ -0,0 +1,85 @@ +package de.monticore.codeAdaption.handler; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class JavaTypeUpdateServiceTest { + @TempDir Path temporaryDirectory; + + @BeforeEach + void initializeMills() { + AdapterAbstractTest.initMills(); + } + + @Test + void findsInterfacesIntroducedOnACompletedSuperclass() throws IOException { + CDModelIndex input = + index( + "Input.cd", + "classdiagram Input { class BankAccount; " + + "class SavingsAccount extends BankAccount; }"); + CDModelIndex completed = + index( + "Completed.cd", + "import java.lang.String; classdiagram Completed { " + + "interface Auditable { String generateReport(); } " + + "abstract class BankAccount implements Auditable; " + + "class SavingsAccount extends BankAccount; }"); + + Set inputInterfaces = + JavaTypeUpdateService.effectiveInterfaceNames(input, "SavingsAccount"); + Set completedInterfaces = + JavaTypeUpdateService.effectiveInterfaceNames(completed, "SavingsAccount"); + completedInterfaces.removeAll(inputInterfaces); + + assertEquals(Set.of("Auditable"), completedInterfaces); + } + + @ParameterizedTest(name = "{0} implements {1}: {2}") + @MethodSource("implementationReturnCases") + void checksImplementationReturnCompatibility( + String actualReturn, String requiredReturn, boolean compatible) throws IOException { + CDModelIndex index = + index( + "Returns.cd", + "classdiagram Returns { class Parent; class Child extends Parent; class Other; }"); + + assertEquals( + compatible, + JavaTypeUpdateService.isCompatibleImplementationReturn( + index, actualReturn, requiredReturn)); + } + + private static Stream implementationReturnCases() { + return Stream.of( + Arguments.of("int", "int", true), + Arguments.of("Child", "Parent", true), + Arguments.of("String", "Object", true), + Arguments.of("Child[]", "Object", true), + Arguments.of("Parent", "Child", false), + Arguments.of("Other", "Parent", false), + Arguments.of("int", "long", false), + Arguments.of("void", "Object", false), + Arguments.of("Object", "void", false)); + } + + private CDModelIndex index(String fileName, String source) throws IOException { + Path cd = temporaryDirectory.resolve(fileName); + Files.writeString(cd, source); + return CDModelIndex.of(JavaLoader.parseCD(cd.toString())); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/AdaptationConflictDetectorTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/AdaptationConflictDetectorTest.java new file mode 100644 index 00000000..afecbf7b --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/AdaptationConflictDetectorTest.java @@ -0,0 +1,260 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static de.monticore.codeAdaption.utils.AdapterParam.NAME_MATCHING; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.CodeAdaptationException; +import de.monticore.codeAdaption.CodeAdapter; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.se_rwth.commons.logging.LogStub; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class AdaptationConflictDetectorTest extends AdapterAbstractTest { + private static final Path ROOT = + Path.of("src/test/resources/de/monticore/codeAdaption/conflicts"); + private static final Path EVALUATION_ROOT = + Path.of("src/test/resources/de/monticore/codeAdaption/evaluation"); + private static final Set MAPPINGS = Set.of("ref"); + + @TempDir Path temporaryDirectory; + + private Set confParams; + + @BeforeEach + public void setup() { + LogStub.init(); + initMills(); + confParams = Set.of(STEREOTYPE_MAPPING, NAME_MAPPING, INHERITANCE); + } + + @ParameterizedTest(name = "{0} -> {2}") + @CsvSource({ + "AmbiguousOverloadRef.cd, AmbiguousOverloadConc.cd, ambiguous overloaded", + "TypeKindRef.cd, TypeKindConc.cd, type-kind mismatch", + "FieldConflictRef.cd, FieldConflictConc.cd, inherited field conflict", + "MethodConflictRef.cd, MethodConflictConc.cd, duplicate method", + "EnumOrderRef.cd, EnumOrderConc.cd, enum order conflict", + "AssociationCardinalityRef.cd, AssociationCardinalityConc.cd, cardinality", + "AssociationAmbiguousDirectionRef.cd, AssociationAmbiguousDirectionConc.cd, ambiguous association direction", + "AssociationRoleConflictRef.cd, AssociationRoleConflictConc.cd, association role field conflict", + "AnyRef.cd, AnyConc.cd, underspecified attribute type" + }) + void detectsInvalidManualAdaptation( + String reference, String concrete, String expectedMessagePart) { + assertConflict(reference, concrete, expectedMessagePart); + } + + @Test + public void acceptsDeterministicExplicitMappings() { + assertDoesNotThrow(() -> validate("ValidManualRef.cd", "ValidManualConc.cd")); + } + + @Test + public void rejectsUnknownExplicitTargetsAndOwnerlessMemberMappings() { + CodeAdaptationException exception = + assertThrows( + CodeAdaptationException.class, + () -> validate("UnknownExplicitRef.cd", "UnknownExplicitConc.cd")); + + assertTrue(exception.getMessage().contains("unresolved explicit type stereotype")); + assertTrue(exception.getMessage().contains("unresolved explicit field stereotype")); + assertTrue(exception.getMessage().contains("unresolved explicit method stereotype")); + } + + @Test + public void acceptsEvaluationTestcase1ObserverChainRoles() { + ASTCDCompilationUnit refCD = + JavaLoader.parseCD(EVALUATION_ROOT.resolve("testcase_1/Reference.cd").toString()); + ASTCDCompilationUnit conCD = + JavaLoader.parseCD(EVALUATION_ROOT.resolve("testcase_1/Concrete.cd").toString()); + Set mappings = Set.of("stud", "prof"); + Map contexts = new java.util.LinkedHashMap<>(); + for (String mapping : mappings) { + contexts.put( + mapping, + new ManualIncarnationContextBuilder( + CDModelIndex.of(refCD), CDModelIndex.of(conCD), confParams) + .buildContextForMapping(mapping)); + } + + assertDoesNotThrow( + () -> + AdaptationConflictDetector.validate( + CDModelIndex.of(refCD), + CDModelIndex.of(conCD), + mappings, + contexts, + confParams, + false)); + } + + @Test + public void derivesAttributeForEachMappings() { + IncarnationContext context = context("ForEachAttributeRef.cd", "ForEachAttributeConc.cd"); + + assertEquals( + 2, + context + .getIncarnations(StableElementKey.field("Builder", "attribute", "any")) + .size()); + } + + @Test + public void derivesMethodForEachMappingsFromAttributeIncarnations() { + IncarnationContext context = context("ForEachMethodRef.cd", "ForEachMethodConc.cd"); + + assertEquals( + 2, + context + .getIncarnations(StableElementKey.method("DataClass", "getAttribute", List.of(), "any")) + .size()); + } + + @Test + public void derivesExactMethodForEachMappingsFromAttributeIncarnations() { + IncarnationContext context = + context("ForEachMethodExactRef.cd", "ForEachMethodExactConc.cd"); + + assertEquals( + 2, + context + .getIncarnations(StableElementKey.method("Builder", "copy", List.of(), "any")) + .size()); + } + + @Test + public void doesNotDeriveMethodForEachMappingsFromAccidentalSubstrings() { + IncarnationContext context = + context("ForEachMethodSubstringRef.cd", "ForEachMethodSubstringConc.cd"); + + assertEquals( + 0, + context + .getIncarnations( + StableElementKey.method("DataClass", "getAttributable", List.of(), "any")) + .size()); + } + + @Test + public void derivesTypeForEachMappings() { + IncarnationContext context = context("ForEachTypeRef.cd", "ForEachTypeConc.cd"); + + assertEquals(2, context.getIncarnations(StableElementKey.type("DataClassBuilder")).size()); + } + + @Test + public void detectsMissingForEachMappingBeforeCleaningExistingOutput() throws IOException { + Path output = temporaryDirectory.resolve("forEachConflictPreserve"); + Files.createDirectories(output); + Path marker = output.resolve("marker.txt"); + Files.writeString(marker, "keep"); + + CodeAdapter adapter = + new CodeAdapter( + Set.of(NAME_MATCHING, ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR, IGNORE_NON_MATCHED_TYPE), + confParams); + + CodeAdaptationException exception = + assertThrows( + CodeAdaptationException.class, + () -> + adapter.adapt( + ROOT.resolve("ForEachMissingRef.cd").toFile(), + ROOT.resolve("ForEachMissingConc.cd").toFile(), + MAPPINGS, + ROOT.resolve("adapter"), + ROOT.resolve("concrete"), + output, + false, + true)); + + assertTrue(exception.getMessage().contains("missing forEach incarnation")); + assertTrue(Files.exists(marker), "Conflict detection must run before output cleanup"); + } + + @Test + public void codeAdapterFailsBeforeCleaningExistingOutput() throws IOException { + Path output = temporaryDirectory.resolve("conflictDetectorPreserve"); + Files.createDirectories(output); + Path marker = output.resolve("marker.txt"); + Files.writeString(marker, "keep"); + + CodeAdapter adapter = + new CodeAdapter( + Set.of(NAME_MATCHING, ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR, IGNORE_NON_MATCHED_TYPE), + confParams); + + assertThrows( + CodeAdaptationException.class, + () -> + adapter.adapt( + ROOT.resolve("AnyRef.cd").toFile(), + ROOT.resolve("AnyConc.cd").toFile(), + MAPPINGS, + ROOT.resolve("adapter"), + ROOT.resolve("concrete"), + output, + false, + true)); + + assertTrue(Files.exists(marker), "Conflict detection must run before output cleanup"); + } + + private void assertConflict(String reference, String concrete, String expectedMessagePart) { + CodeAdaptationException exception = + assertThrows(CodeAdaptationException.class, () -> validate(reference, concrete)); + assertTrue( + exception.getMessage().contains(expectedMessagePart), + () -> "Expected message to contain '" + expectedMessagePart + "' but was: " + + exception.getMessage()); + } + + private void validate(String reference, String concrete) { + ASTCDCompilationUnit refCD = JavaLoader.parseCD(ROOT.resolve(reference).toString()); + ASTCDCompilationUnit conCD = JavaLoader.parseCD(ROOT.resolve(concrete).toString()); + IncarnationContext context = context(refCD, conCD); + AdaptationConflictDetector.validate( + CDModelIndex.of(refCD), + CDModelIndex.of(conCD), + MAPPINGS, + Map.of("ref", context), + confParams, + false); + } + + private IncarnationContext context(String reference, String concrete) { + ASTCDCompilationUnit refCD = JavaLoader.parseCD(ROOT.resolve(reference).toString()); + ASTCDCompilationUnit conCD = JavaLoader.parseCD(ROOT.resolve(concrete).toString()); + return context(refCD, conCD); + } + + private IncarnationContext context(ASTCDCompilationUnit refCD, ASTCDCompilationUnit conCD) { + ManualIncarnationContextBuilder builder = + new ManualIncarnationContextBuilder( + CDModelIndex.of(refCD), CDModelIndex.of(conCD), confParams); + return builder.buildContextForMapping("ref"); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContextBuilderTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContextBuilderTest.java new file mode 100644 index 00000000..481cfd17 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/IncarnationContextBuilderTest.java @@ -0,0 +1,338 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import static de.monticore.cdconformance.CDConfParameter.ALLOW_ADDITIONAL_PARAMETERS; +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STRICT_PARAMETER_ORDER; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdconcretization.ConcretizationCompleter; +import de.monticore.cdconformance.CDConfParameter; +import de.monticore.cdconformance.CDConformanceChecker; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.handler.BasicUpdateHandlerTestAccess; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.cddiff.CDDiffUtil; +import java.io.File; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class IncarnationContextBuilderTest extends AdapterAbstractTest { + private Set confParameters; + + @BeforeEach + public void setup() { + initMills(); + confParameters = + Set.of( + NAME_MAPPING, + INHERITANCE, + STEREOTYPE_MAPPING, + STRICT_PARAMETER_ORDER, + ALLOW_ADDITIONAL_PARAMETERS); + } + + @Test + @DisplayName("Builds owner-aware field mappings from stereotypes") + public void buildsOwnerAwareFieldMappings() { + ASTCDCompilationUnit refCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Reference.cd")); + ASTCDCompilationUnit conCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Concrete.cd")); + + IncarnationContext context = build(refCD, conCD, "buildPat"); + StableElementKey referenceField = StableElementKey.field("Builder", "attribute", "any"); + + List targets = + context.getIncarnations(referenceField).stream() + .map(IncarnationContext.MappedElement::key) + .collect(Collectors.toList()); + + assertTrue(targets.contains(StableElementKey.field("Person", "name", "String"))); + assertTrue(targets.contains(StableElementKey.field("Person", "age", "int"))); + assertTrue(targets.contains(StableElementKey.field("Person", "tags", "List"))); + assertTrue(targets.contains(StableElementKey.field("Task", "status", "String"))); + assertEquals(11, targets.size()); + } + + @Test + @DisplayName("Builds signature-aware method mappings and grouping types") + public void buildsSignatureAwareMethodMappingsAndGroupingTypes() { + ASTCDCompilationUnit refCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Reference.cd")); + ASTCDCompilationUnit conCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Concrete.cd")); + + IncarnationContext context = build(refCD, conCD, "observer"); + StableElementKey referenceMethod = StableElementKey.method("Observer", "update", List.of(), "void"); + + List targets = + context.getIncarnations(referenceMethod).stream() + .map(IncarnationContext.MappedElement::key) + .collect(Collectors.toList()); + + assertTrue( + targets.contains( + StableElementKey.method("PipelineObserver", "onPipelineStage", List.of("String"), "void"))); + assertTrue( + targets.contains( + StableElementKey.method("LoggingObserver", "onPipelineStage", List.of("String"), "void"))); + assertTrue( + targets.contains( + StableElementKey.method("MetricsObserver", "onPipelineStage", List.of("String"), "void"))); + assertEquals( + "PipelineObserver", + context + .getGroupingFor(StableElementKey.type("LoggingObserver")) + .orElseThrow() + .key() + .getName()); + assertEquals( + "PipelineObserver", + context + .getGroupingFor(StableElementKey.type("MetricsObserver")) + .orElseThrow() + .key() + .getName()); + } + + @Test + @DisplayName("Can build mappings from concretization plus conformance without stereotype overlay") + public void buildsCheckerOnlyContextAfterConcretization() throws Exception { + ASTCDCompilationUnit refCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingRef.cd")); + ASTCDCompilationUnit conCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingConc.cd")); + + ConcretizationCompleter completer = new ConcretizationCompleter(confParameters); + completer.completeCD(conCD, refCD, "ref"); + + CDConformanceChecker checker = new CDConformanceChecker(confParameters); + assertTrue(checker.checkConformance(conCD, refCD, "ref")); + + IncarnationContext context = + new IncarnationContextBuilder(checker, CDModelIndex.of(refCD), CDModelIndex.of(conCD)) + .buildContextForMapping("ref", false); + + ASTCDType referenceCourse = findType(refCD, "Course"); + List incarnations = + context.getIncarnations(StableElementKey.type(referenceCourse)).stream() + .map(element -> element.key().getName()) + .collect(Collectors.toList()); + + assertEquals(List.of("Course"), incarnations); + } + + @Test + @DisplayName("Can disable manual stereotype overlay when checker mappings are absent") + public void canDisableManualStereotypeOverlay() { + ASTCDCompilationUnit refCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Reference.cd")); + ASTCDCompilationUnit conCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Concrete.cd")); + CDConformanceChecker checker = new CDConformanceChecker(confParameters); + + IncarnationContext checkerOnly = + new IncarnationContextBuilder(checker, CDModelIndex.of(refCD), CDModelIndex.of(conCD)) + .buildContextForMapping("observer", false); + IncarnationContext manualFallback = + new IncarnationContextBuilder(checker, CDModelIndex.of(refCD), CDModelIndex.of(conCD)) + .buildContextForMapping("observer", true); + + assertTrue(checkerOnly.getMappings().isEmpty()); + assertFalse(manualFallback.getMappings().isEmpty()); + } + + @Test + @DisplayName("Common-parent switch controls whether concrete incarnations collapse to a grouping type") + public void commonParentSwitchControlsGroupingResolution() { + ASTCDCompilationUnit refCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Reference.cd")); + ASTCDCompilationUnit conCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Concrete.cd")); + + ASTCDType observer = findType(refCD, "Observer"); + ASTCDType pipelineObserver = findType(conCD, "PipelineObserver"); + ASTCDType loggingObserver = findType(conCD, "LoggingObserver"); + ASTCDType metricsObserver = findType(conCD, "MetricsObserver"); + + Map> mappings = + new LinkedHashMap<>(); + mappings.put( + StableElementKey.type(observer), + List.of( + new IncarnationContext.MappedElement( + StableElementKey.type(loggingObserver), loggingObserver.getSymbol()), + new IncarnationContext.MappedElement( + StableElementKey.type(metricsObserver), metricsObserver.getSymbol()))); + + IncarnationContext.MappedElement grouping = + new IncarnationContext.MappedElement( + StableElementKey.type(pipelineObserver), pipelineObserver.getSymbol()); + + IncarnationContext context = + new IncarnationContext( + "observer", + mappings, + Map.of( + StableElementKey.type(loggingObserver), grouping, + StableElementKey.type(metricsObserver), grouping)); + + assertEquals( + "PipelineObserver", + resolve(context, null, null, Map.of(), true, observer.getSymbol()).orElseThrow().getName()); + assertEquals( + "LoggingObserver", + resolve(context, null, null, Map.of(), false, observer.getSymbol()).orElseThrow().getName()); + } + + @Test + @DisplayName("Stable keys resolve mappings across separately parsed equivalent CDs") + public void resolvesMappingsAcrossSeparateParses() { + File referenceFile = + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Reference.cd"); + File concreteFile = + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Concrete.cd"); + ASTCDCompilationUnit firstReference = JavaLoader.loadCD(referenceFile); + ASTCDCompilationUnit firstConcrete = JavaLoader.loadCD(concreteFile); + IncarnationContext context = build(firstReference, firstConcrete, "buildPat"); + + ASTCDCompilationUnit secondReference = JavaLoader.loadCD(referenceFile); + CDModelIndex secondIndex = CDModelIndex.of(secondReference); + ASTCDType secondBuilder = findType(secondReference, "Builder"); + StableElementKey typeKey = + StableElementKey.fromSymbol(secondBuilder.getSymbol(), secondIndex).orElseThrow(); + StableElementKey fieldKey = + StableElementKey.fromSymbol( + secondBuilder.getCDAttributeList().get(0).getSymbol(), secondIndex) + .orElseThrow(); + StableElementKey methodKey = + StableElementKey.fromSymbol( + secondBuilder.getCDMethodList().get(0).getSymbol(), secondIndex) + .orElseThrow(); + + assertFalse(context.getIncarnations(typeKey).isEmpty()); + assertEquals(11, context.getIncarnations(fieldKey).size()); + assertFalse(context.getIncarnations(methodKey).isEmpty()); + assertThrows( + UnsupportedOperationException.class, + () -> context.getMappings().put(StableElementKey.type("Other"), List.of())); + assertThrows( + UnsupportedOperationException.class, + () -> context.getIncarnations(fieldKey).clear()); + } + + @Test + @DisplayName("Pass selection resolves types and members by stable key across separate parses") + public void passSelectionUsesStableKeysAndSelectedOwner() { + File referenceFile = + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Reference.cd"); + File concreteFile = + new File( + "src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Concrete.cd"); + ASTCDCompilationUnit contextReference = JavaLoader.loadCD(referenceFile); + ASTCDCompilationUnit concrete = JavaLoader.loadCD(concreteFile); + IncarnationContext context = build(contextReference, concrete, "buildPat"); + + IncarnationContext.MappedElement selectedTask = + context.getIncarnations(StableElementKey.type("Builder")).stream() + .filter(element -> element.key().equals(StableElementKey.type("Task"))) + .findFirst() + .orElseThrow(); + ASTCDCompilationUnit separatelyParsedReference = JavaLoader.loadCD(referenceFile); + ASTCDType builder = findType(separatelyParsedReference, "Builder"); + Map selection = + Map.of(StableElementKey.type("Builder"), selectedTask); + assertEquals( + "Task", + resolve(context, separatelyParsedReference, concrete, selection, false, builder.getSymbol()) + .orElseThrow() + .getName()); + var selectedField = + resolve( + context, + separatelyParsedReference, + concrete, + selection, + false, + builder.getCDAttributeList().get(0).getSymbol()) + .orElseThrow(); + assertEquals( + "Task", + StableElementKey.fromSymbol(selectedField, CDModelIndex.of(concrete)) + .orElseThrow() + .getOwnerType() + .orElseThrow()); + } + + private IncarnationContext build(ASTCDCompilationUnit refCD, ASTCDCompilationUnit conCD, String mapping) { + CDConformanceChecker checker = new CDConformanceChecker(confParameters); + try { + checker.checkConformance(conCD, refCD, mapping); + } catch (Throwable ignored) { + // The context builder must still overlay stereotypes when conformance is partial. + } + return new IncarnationContextBuilder(checker, CDModelIndex.of(refCD), CDModelIndex.of(conCD)) + .buildContextForMapping(mapping); + } + + private ASTCDType findType(ASTCDCompilationUnit cd, String name) { + return CDDiffUtil.getAllCDTypes(cd).stream() + .filter(type -> type.getName().equals(name)) + .findFirst() + .orElseThrow(); + } + + private static Optional resolve( + IncarnationContext incarnationContext, + ASTCDCompilationUnit reference, + ASTCDCompilationUnit concrete, + Map selection, + boolean useCommonParentForMultipleIncarnations, + de.monticore.symboltable.ISymbol symbol) { + return BasicUpdateHandlerTestAccess.resolve( + incarnationContext, + reference, + concrete, + selection, + useCommonParentForMultipleIncarnations, + symbol); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/MappingRegressionTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/MappingRegressionTest.java new file mode 100644 index 00000000..1c784e3d --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/handler/multiIncarnation/MappingRegressionTest.java @@ -0,0 +1,234 @@ +package de.monticore.codeAdaption.handler.multiIncarnation; + +import static de.monticore.cdconformance.CDConfParameter.INHERITANCE; +import static de.monticore.cdconformance.CDConfParameter.NAME_MAPPING; +import static de.monticore.cdconformance.CDConfParameter.STEREOTYPE_MAPPING; +import static de.monticore.codeAdaption.utils.AdapterParam.ANNOTATION_MATCHING; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_TYPE; +import static de.monticore.codeAdaption.utils.AdapterParam.IGNORE_NON_MATCHED_VAR; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.CodeAdaptationException; +import de.monticore.codeAdaption.CodeAdapter; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.se_rwth.commons.logging.Log; +import de.se_rwth.commons.logging.LogStub; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** Regression coverage for stable mapping identities, grouping, and projected members. */ +class MappingRegressionTest extends AdapterAbstractTest { + @TempDir Path temporaryDirectory; + + @BeforeEach + void initialize() { + LogStub.init(); + initMills(); + Log.enableFailQuick(false); + } + + @Test + void qualifiedParameterTypesMustProduceDifferentStableMethodKeys() { + StableElementKey alpha = + StableElementKey.method("Service", "handle", List.of("alpha.Role"), "void"); + StableElementKey beta = + StableElementKey.method("Service", "handle", List.of("beta.Role"), "void"); + + assertNotEquals( + alpha, + beta, + "Distinct qualified overloads must not collapse to the same mapping/rewrite key"); + } + + @Test + void commonParentOptionMustNotHideASingleClassInterfaceKindMismatch() throws Exception { + ASTCDCompilationUnit reference = + parseCd("KindReference.cd", "classdiagram KindReference { interface Port; }"); + ASTCDCompilationUnit concrete = + parseCd( + "KindConcrete.cd", + "classdiagram KindConcrete { class Parent; class ConcretePort extends Parent; }"); + ASTCDType concretePort = CDModelIndex.of(concrete).type("ConcretePort").orElseThrow(); + StableElementKey referenceKey = StableElementKey.type("Port"); + IncarnationContext context = + new IncarnationContext( + "ref", + Map.of( + referenceKey, + List.of( + new IncarnationContext.MappedElement( + StableElementKey.type(concretePort), concretePort.getSymbol()))), + Map.of()); + + assertThrows( + CodeAdaptationException.class, + () -> + AdaptationConflictDetector.validate( + CDModelIndex.of(reference), + CDModelIndex.of(concrete), + Set.of("ref"), + Map.of("ref", context), + Set.of(), + true), + "The option is for exact multi-incarnation groups, not arbitrary single mappings"); + } + + @Test + void oneConcreteIncarnationMustNotSilentlyChooseBetweenTwoExactGroupingInterfaces() + throws Exception { + ASTCDCompilationUnit reference = + parseCd("GroupingReference.cd", "classdiagram GroupingReference { class R1; class R2; }"); + ASTCDCompilationUnit concrete = + parseCd( + "GroupingConcrete.cd", + "classdiagram GroupingConcrete { " + + "interface G1; interface G2; " + + "class A implements G1, G2; " + + "class B implements G1; " + + "class C implements G2; }"); + CDModelIndex concreteIndex = CDModelIndex.of(concrete); + Map> mappings = + new LinkedHashMap<>(); + mappings.put( + StableElementKey.type("R1"), + List.of(mapped(concreteIndex, "A"), mapped(concreteIndex, "B"))); + mappings.put( + StableElementKey.type("R2"), + List.of(mapped(concreteIndex, "A"), mapped(concreteIndex, "C"))); + + IncarnationMappingSupport support = + new IncarnationMappingSupport(CDModelIndex.of(reference), concreteIndex); + + assertThrows( + IllegalStateException.class, + () -> support.assembleContext("mapping", mappings), + "A shared incarnation in incompatible exact groups must be diagnosed, not overwritten"); + } + + @Test + void knownExternalCovariantReturnMustBeAccepted() throws Exception { + Path referenceCd = + write( + "CovariantReference.cd", + "import java.util.List; classdiagram CovariantReference { " + + "interface Provider { public List values(); } " + + "class Service implements Provider; }"); + Path concreteCd = + write( + "CovariantConcrete.cd", + "import java.util.ArrayList; classdiagram CovariantConcrete { " + + "<> class ConcreteService { " + + "<> public ArrayList values(); } }"); + Path referenceJava = Files.createDirectory(temporaryDirectory.resolve("covariant-reference")); + Path output = temporaryDirectory.resolve("covariant-output"); + Files.writeString( + referenceJava.resolve("Service.java"), + "import de.monticore.codeAdaption.utils.Adapt;\n" + + "import java.util.List;\n" + + "@Adapt(ref={\"Service\"}, template=\"${}\")\n" + + "public class Service {\n" + + " @Adapt(ref={\"Provider.values\"}, template=\"${}\")\n" + + " public List values() { return new java.util.ArrayList(); }\n" + + "}\n"); + + assertDoesNotThrow( + () -> + new CodeAdapter( + Set.of( + ANNOTATION_MATCHING, + IGNORE_NON_MATCHED_VAR, + IGNORE_NON_MATCHED_TYPE), + Set.of(STEREOTYPE_MAPPING, NAME_MAPPING, INHERITANCE)) + .adaptWithoutConcreteCode( + referenceCd.toFile(), + concreteCd.toFile(), + Set.of("ref"), + referenceJava, + output, + true, + true), + "A completed ArrayList implementation must satisfy a List interface contract"); + + Path generated = + Files.walk(output) + .filter(path -> path.getFileName().toString().equals("ConcreteService.java")) + .findFirst() + .orElseThrow(); + String source = Files.readString(generated); + assertTrue(source.contains("List values("), source); + } + + @Test + void mappedMethodRenameMustSurviveCompletedMemberProjection() throws Exception { + Path referenceCd = + write( + "RenameReference.cd", + "classdiagram RenameReference { class Service { void execute(); } }"); + Path concreteCd = + write( + "RenameConcrete.cd", + "classdiagram RenameConcrete { " + + "<> class ConcreteService { " + + "<> void run(); } }"); + Path referenceJava = Files.createDirectory(temporaryDirectory.resolve("reference-java")); + Path output = temporaryDirectory.resolve("output"); + Files.writeString( + referenceJava.resolve("Service.java"), + "import de.monticore.codeAdaption.utils.Adapt;\n" + + "@Adapt(ref={\"Service\"}, template=\"${}\")\n" + + "public class Service {\n" + + " @Adapt(ref={\"Service.execute\"}, template=\"${}\")\n" + + " public void execute() { System.out.println(\"kept\"); }\n" + + "}\n"); + + new CodeAdapter( + Set.of(ANNOTATION_MATCHING, IGNORE_NON_MATCHED_VAR, IGNORE_NON_MATCHED_TYPE), + Set.of(STEREOTYPE_MAPPING, NAME_MAPPING, INHERITANCE)) + .adaptWithoutConcreteCode( + referenceCd.toFile(), + concreteCd.toFile(), + Set.of("ref"), + referenceJava, + output, + false, + true); + + Path generated = + Files.walk(output) + .filter(path -> path.getFileName().toString().equals("ConcreteService.java")) + .findFirst() + .orElseThrow(); + String source = Files.readString(generated); + assertTrue(source.contains("void run("), source); + assertTrue(source.contains("kept"), source); + } + + private ASTCDCompilationUnit parseCd(String fileName, String source) throws Exception { + return JavaLoader.parseCD(write(fileName, source).toString()); + } + + private Path write(String fileName, String source) throws Exception { + Path file = temporaryDirectory.resolve(fileName); + Files.writeString(file, source); + return file; + } + + private static IncarnationContext.MappedElement mapped(CDModelIndex index, String typeName) { + ASTCDType type = index.type(typeName).orElseThrow(); + return new IncarnationContext.MappedElement(StableElementKey.type(type), type.getSymbol()); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/AnnotMatcherTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/AnnotMatcherTest.java index 711f5ea3..aeffcde7 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/AnnotMatcherTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/AnnotMatcherTest.java @@ -1,16 +1,22 @@ package de.monticore.codeAdaption.matcher; +import de.monticore.codeAdaption.matcher.annotMatcher.AnnotVariableMatcher; import de.monticore.codeAdaption.utils.AdapterParam; +import de.monticore.codeAdaption.utils.JavaLoader; import de.monticore.codeAdaption.validator.CodeValidator; import de.monticore.java.javadsl._ast.ASTFieldDeclaration; import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; import de.monticore.java.javadsl._ast.ASTTypeDeclaration; import de.monticore.javalight._ast.ASTMethodDeclaration; import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; +import java.io.File; +import java.util.List; import java.util.Optional; import java.util.Set; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class AnnotMatcherTest extends MatcherAbstractTest { private CodeValidator validator; @@ -21,75 +27,82 @@ public void init(String javaFile) { validator = new CodeValidator(cd, Set.of(AdapterParam.ANNOTATION_MATCHING)); } - @Test - public void TestTypeAnnotationMatching() { - init("/annotMatcher/EntityRepository.java"); - ASTTypeDeclaration entityRepos = collector.getAllTypeDeclarations().get(0); - Optional matching = validator.getMatchedType(entityRepos); - - Assertions.assertTrue(matching.isPresent()); - Assertions.assertEquals(1, matching.get().getReferences().size()); - Assertions.assertEquals("Entity", matching.get().getReferences().get(0).getName()); - Assertions.assertEquals("${}Repository", matching.get().getTemplate()); - Assertions.assertTrue(matching.get().mustBePerform()); - } - - @Test - public void TestMethodAnnotationMatching() { + @ParameterizedTest(name = "annotation matching for {0}") + @ValueSource(strings = {"type", "method", "field", "local", "parameter"}) + void matchesAnnotatedElements(String elementKind) { init("/annotMatcher/EntityRepository.java"); - ASTTypeDeclaration entityRepos = collector.getAllTypeDeclarations().get(0); - ASTMethodDeclaration method = collector.getAllMethodDeclarations(entityRepos).get(0); + ASTTypeDeclaration type = collector.getAllTypeDeclarations().get(0); + ExpectedMatching expected = expectedMatching(elementKind, type); + Optional matching = expected.matching(); - Optional matching = validator.getMatchedMethod(entityRepos, method); Assertions.assertTrue(matching.isPresent()); - Assertions.assertEquals(2, matching.get().getReferences().size()); - Assertions.assertEquals("Entity", matching.get().getReferences().get(0).getName()); - Assertions.assertEquals("id", matching.get().getReferences().get(1).getName()); - Assertions.assertEquals("find${}By${cap_first}", matching.get().getTemplate()); + Assertions.assertEquals( + expected.referenceNames(), + matching.get().getReferences().stream().map(value -> value.getName()).toList()); + Assertions.assertEquals(expected.template(), matching.get().getTemplate()); Assertions.assertTrue(matching.get().mustBePerform()); } @Test - public void TestAttributeAnnotationMatching() { + void variableMatcherUsesReplacedReferenceCD() { init("/annotMatcher/EntityRepository.java"); - ASTTypeDeclaration entityRepos = collector.getAllTypeDeclarations().get(0); - ASTFieldDeclaration attribute = collector.getAllFieldDeclarations(entityRepos).get(0); + var unrelatedCD = + JavaLoader.loadCD( + new File( + "src/test/resources/de/monticore/codeAdaption/validator/Validator.cd")); + AnnotVariableMatcher matcher = new AnnotVariableMatcher(unrelatedCD); + matcher.setReferenceCD(cd); - Optional matching = validator.getMatchedField(entityRepos, attribute); - Assertions.assertTrue(matching.isPresent()); - Assertions.assertEquals(1, matching.get().getReferences().size()); - Assertions.assertEquals("Entity", matching.get().getReferences().get(0).getName()); - Assertions.assertEquals("${uncap_first}Set", matching.get().getTemplate()); - Assertions.assertTrue(matching.get().mustBePerform()); - } - - @Test - public void TestLocalVariableAnnotationMatching() { - init("/annotMatcher/EntityRepository.java"); ASTTypeDeclaration type = collector.getAllTypeDeclarations().get(0); ASTMethodDeclaration method = collector.getAllMethodDeclarations(type).get(2); - ASTLocalVariableDeclaration localVar = collector.getAllLocVariables(type, method).get(0); + ASTLocalVariableDeclaration local = collector.getAllLocVariables(type, method).get(0); + Optional matching = matcher.getMatchedLocalVariable(type, method, local); - Optional matching = validator.getMatchedLocalVariable(type, method, localVar); Assertions.assertTrue(matching.isPresent()); - Assertions.assertEquals(1, matching.get().getReferences().size()); - Assertions.assertEquals("Entity", matching.get().getReferences().get(0).getName()); - Assertions.assertEquals("${uncap_first}List", matching.get().getTemplate()); - Assertions.assertTrue(matching.get().mustBePerform()); + Assertions.assertEquals( + List.of("Entity"), + matching.get().getReferences().stream().map(value -> value.getName()).toList()); } - @Test - public void TestFormalParameterAnnotationMatching() { - init("/annotMatcher/EntityRepository.java"); - ASTTypeDeclaration type = collector.getAllTypeDeclarations().get(0); - ASTMethodDeclaration method = collector.getAllMethodDeclarations(type).get(3); - ASTFormalParameter param = collector.getAllParameters(type, method).get(0); - - Optional matching = validator.getMatchedParameter(type, method, param); - Assertions.assertTrue(matching.isPresent()); - Assertions.assertEquals(1, matching.get().getReferences().size()); - Assertions.assertEquals("Entity", matching.get().getReferences().get(0).getName()); - Assertions.assertEquals("${uncap_first}", matching.get().getTemplate()); - Assertions.assertTrue(matching.get().mustBePerform()); + private ExpectedMatching expectedMatching(String elementKind, ASTTypeDeclaration type) { + return switch (elementKind) { + case "type" -> + new ExpectedMatching( + validator.getMatchedType(type), List.of("Entity"), "${}Repository"); + case "method" -> { + ASTMethodDeclaration method = collector.getAllMethodDeclarations(type).get(0); + yield new ExpectedMatching( + validator.getMatchedMethod(type, method), + List.of("Entity", "id"), + "find${}By${cap_first}"); + } + case "field" -> { + ASTFieldDeclaration field = collector.getAllFieldDeclarations(type).get(0); + yield new ExpectedMatching( + validator.getMatchedField(type, field), + List.of("Entity"), + "${uncap_first}Set"); + } + case "local" -> { + ASTMethodDeclaration method = collector.getAllMethodDeclarations(type).get(2); + ASTLocalVariableDeclaration local = collector.getAllLocVariables(type, method).get(0); + yield new ExpectedMatching( + validator.getMatchedLocalVariable(type, method, local), + List.of("Entity"), + "${uncap_first}List"); + } + case "parameter" -> { + ASTMethodDeclaration method = collector.getAllMethodDeclarations(type).get(3); + ASTFormalParameter parameter = collector.getAllParameters(type, method).get(0); + yield new ExpectedMatching( + validator.getMatchedParameter(type, method, parameter), + List.of("Entity"), + "${uncap_first}"); + } + default -> throw new IllegalArgumentException("Unknown element kind: " + elementKind); + }; } + + private record ExpectedMatching( + Optional matching, List referenceNames, String template) {} } diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/InfixMatcherTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/InfixMatcherTest.java index b454a55e..5ce4471f 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/InfixMatcherTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/matcher/InfixMatcherTest.java @@ -3,12 +3,15 @@ import static de.monticore.codeAdaption.utils.AdapterParam.INFIX_MATCHING; import de.monticore.codeAdaption.validator.CodeValidator; +import de.monticore.codeAdaption.utils.AdapterUtils; +import de.monticore.cdbasis._ast.ASTCDType; import de.monticore.java.javadsl._ast.ASTFieldDeclaration; import de.monticore.java.javadsl._ast.ASTLocalVariableDeclaration; import de.monticore.java.javadsl._ast.ASTTypeDeclaration; import de.monticore.javalight._ast.ASTMethodDeclaration; import de.monticore.statements.mccommonstatements._ast.ASTFormalParameter; import java.util.Optional; +import java.util.List; import java.util.Set; import org.junit.jupiter.api.*; @@ -92,4 +95,78 @@ public void TestFormalParameterAnnotationMatching() { Assertions.assertEquals("${uncap_first}", matching.get().getTemplate()); Assertions.assertTrue(matching.get().mustBePerform()); } + + @Test + void repeatedInfixesProduceOneReferencePerPlaceholder() { + init("/infixMatcher/EntityRepository.java"); + ASTCDType entity = + AdapterUtils.getAllCDTypes(cd).stream() + .filter(type -> "Entity".equals(type.getName())) + .findFirst() + .orElseThrow(); + + CodeMatching matching = + MatcherHelper.mkMatchingFromInfixRef( + List.of(entity.getSymbol()), "EntityToEntity") + .orElseThrow(); + + Assertions.assertEquals("${}To${}", matching.getTemplate()); + Assertions.assertEquals(2, matching.getReferences().size()); + Assertions.assertEquals( + "EntityToEntity", + MatcherHelper.fillTemplate(matching.getTemplate(), matching.getReferences())); + } + + @Test + void referencesAreOrderedByTheirNonOverlappingPositions() { + init("/infixMatcher/EntityRepository.java"); + ASTCDType entity = + AdapterUtils.getAllCDTypes(cd).stream() + .filter(type -> "Entity".equals(type.getName())) + .findFirst() + .orElseThrow(); + ASTCDType application = + AdapterUtils.getAllCDTypes(cd).stream() + .filter(type -> "Application".equals(type.getName())) + .findFirst() + .orElseThrow(); + + List references = + MatcherHelper.cleanReferences( + "EntityApplicationEntity", List.of(application.getSymbol(), entity.getSymbol())); + + Assertions.assertEquals(List.of("Entity", "Application", "Entity"), + references.stream().map(de.monticore.symboltable.ISymbol::getName).toList()); + } + + @Test + void unmatchedTemplatePlaceholdersRemainVisible() { + init("/infixMatcher/EntityRepository.java"); + ASTCDType entity = + AdapterUtils.getAllCDTypes(cd).stream() + .filter(type -> "Entity".equals(type.getName())) + .findFirst() + .orElseThrow(); + + Assertions.assertEquals( + "Entity${cap_first}", + MatcherHelper.fillTemplate("${}${cap_first}", List.of(entity.getSymbol()))); + } + + @Test + void caseInsensitiveInfixMatchProducesAReplacementTemplate() { + init("/infixMatcher/EntityRepository.java"); + ASTCDType entity = + AdapterUtils.getAllCDTypes(cd).stream() + .filter(type -> "Entity".equals(type.getName())) + .findFirst() + .orElseThrow(); + + CodeMatching matching = + MatcherHelper.mkMatchingFromInfixRef( + List.of(entity.getSymbol()), "ENTITYRepository") + .orElseThrow(); + + Assertions.assertEquals("${}Repository", matching.getTemplate()); + } } diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationFixtureWorkspace.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationFixtureWorkspace.java new file mode 100644 index 00000000..36d7cdb4 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationFixtureWorkspace.java @@ -0,0 +1,209 @@ +package de.monticore.codeAdaption.testutil; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +/** Builds isolated Java fixture directories for cdconcretization tests. */ +public final class CDConcretizationFixtureWorkspace { + + private static final Path RESOURCE_ROOT = Path.of(CDConcretizationTestCase.RESOURCE_ROOT); + private static final Path SHARED_JAVA_ROOT = RESOURCE_ROOT.resolve("_shared").resolve("java"); + private static final Path INDEX = RESOURCE_ROOT.resolve("_shared").resolve("shared-fixtures.tsv"); + private static final Path WORKSPACE_ROOT = Path.of("target/test-fixtures/cdconcretization"); + private static final Pattern PACKAGE_DECLARATION = + Pattern.compile("^\\s*package\\s+[^;\\r\\n]+;\\s*$"); + private static final Pattern PACKAGE_LINE_IN_SOURCE = + Pattern.compile("(?m)^\\s*package\\s+[^;\\r\\n]+;"); + + private static final Map> ENTRIES_BY_CASE = loadIndex(); + + private CDConcretizationFixtureWorkspace() {} + + public static CDConcretizationTestCase materialize(CDConcretizationTestCase testCase) { + Path caseRoot = safeResolve(WORKSPACE_ROOT, testCase.id()); + Path adapterTarget = caseRoot.resolve("adapter"); + Path concreteTarget = caseRoot.resolve("concrete"); + + try { + deleteRecursively(caseRoot); + Files.createDirectories(adapterTarget); + Files.createDirectories(concreteTarget); + + Set sharedOutputs = new LinkedHashSet<>(); + for (SharedFixtureEntry entry : + ENTRIES_BY_CASE.getOrDefault(relativeConcreteCd(testCase), List.of())) { + Path roleTarget = roleTarget(entry.role(), adapterTarget, concreteTarget); + String outputKey = entry.role() + ":" + normalizeRelative(entry.output()); + if (!sharedOutputs.add(outputKey)) { + throw new IllegalStateException( + "Duplicate shared fixture output for " + relativeConcreteCd(testCase) + ": " + outputKey); + } + writeSharedFixture(entry, roleTarget); + } + + copyLocalOverlay(testCase.adapterPath(), adapterTarget); + copyLocalOverlay(testCase.concretePath(), concreteTarget); + } catch (IOException e) { + throw new IllegalStateException( + "Failed to materialize cdconcretization fixture for " + testCase.displayName(), e); + } + + return new CDConcretizationTestCase( + testCase.id(), + testCase.displayName(), + testCase.refCd(), + testCase.concCd(), + adapterTarget, + concreteTarget, + testCase.outputPath(), + testCase.mappings(), + testCase.strictParameterOrder(), + testCase.enabled()); + } + + private static void writeSharedFixture(SharedFixtureEntry entry, Path roleTarget) + throws IOException { + Path sharedSource = safeResolve(RESOURCE_ROOT, entry.shared()); + if (!sharedSource.startsWith(SHARED_JAVA_ROOT.normalize())) { + throw new IllegalStateException("Shared fixture source is outside _shared/java: " + entry.shared()); + } + if (!Files.isRegularFile(sharedSource)) { + throw new IllegalStateException("Missing shared fixture source: " + entry.shared()); + } + + Path output = safeResolve(roleTarget, entry.output()); + String source = Files.readString(sharedSource, StandardCharsets.UTF_8); + Matcher matcher = PACKAGE_LINE_IN_SOURCE.matcher(source); + if (!matcher.find()) { + throw new IllegalStateException("Shared fixture has no package declaration: " + entry.shared()); + } + + String rendered = matcher.replaceFirst(Matcher.quoteReplacement(entry.packageLine())); + Files.createDirectories(output.getParent()); + Files.writeString(output, rendered, StandardCharsets.UTF_8); + } + + private static void copyLocalOverlay(Path sourceRoot, Path targetRoot) throws IOException { + if (!Files.exists(sourceRoot)) { + return; + } + try (Stream paths = Files.walk(sourceRoot)) { + for (Path source : paths.filter(Files::isRegularFile).sorted().toList()) { + Path relative = sourceRoot.relativize(source); + Path target = safeResolve(targetRoot, relative.toString()); + Files.createDirectories(target.getParent()); + Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); + } + } + } + + private static Map> loadIndex() { + if (!Files.exists(INDEX)) { + return Map.of(); + } + + Map> result = new LinkedHashMap<>(); + Set seenOutputs = new LinkedHashSet<>(); + try { + List lines = Files.readAllLines(INDEX, StandardCharsets.UTF_8); + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + if (line.isBlank() || line.startsWith("#")) { + continue; + } + String[] parts = line.split("\\t", 5); + if (parts.length != 5) { + throw new IllegalStateException("Invalid shared fixture index row " + (i + 1)); + } + + SharedFixtureEntry entry = + new SharedFixtureEntry(parts[0], parts[1], parts[2], parts[3], parts[4]); + validateEntry(entry, i + 1); + + String outputKey = + entry.concreteCd() + + ":" + + entry.role() + + ":" + + normalizeRelative(entry.output()); + if (!seenOutputs.add(outputKey)) { + throw new IllegalStateException("Duplicate shared fixture output in row " + (i + 1)); + } + result.computeIfAbsent(entry.concreteCd(), ignored -> new ArrayList<>()).add(entry); + } + } catch (IOException e) { + throw new IllegalStateException("Failed to read shared fixture index " + INDEX, e); + } + result.replaceAll((ignored, entries) -> List.copyOf(entries)); + return Map.copyOf(result); + } + + private static void validateEntry(SharedFixtureEntry entry, int row) { + safeResolve(RESOURCE_ROOT, entry.concreteCd()); + safeResolve(RESOURCE_ROOT, entry.shared()); + safeResolve(Path.of("unused"), entry.output()); + + if (!entry.role().equals("adapter") && !entry.role().equals("concrete")) { + throw new IllegalStateException("Invalid role in shared fixture index row " + row); + } + if (!PACKAGE_DECLARATION.matcher(entry.packageLine()).matches()) { + throw new IllegalStateException("Invalid package declaration in shared fixture index row " + row); + } + } + + private static Path roleTarget(String role, Path adapterTarget, Path concreteTarget) { + return role.equals("adapter") ? adapterTarget : concreteTarget; + } + + private static String relativeConcreteCd(CDConcretizationTestCase testCase) { + return RESOURCE_ROOT.relativize(testCase.concCd()).toString().replace('\\', '/'); + } + + private static Path safeResolve(Path root, String relativePath) { + if (relativePath == null || relativePath.isBlank()) { + throw new IllegalStateException("Fixture path must not be blank."); + } + Path normalizedRoot = root.normalize(); + Path resolved = normalizedRoot.resolve(relativePath.replace('/', java.io.File.separatorChar)).normalize(); + if (!resolved.startsWith(normalizedRoot)) { + throw new IllegalStateException("Fixture path escapes root: " + relativePath); + } + return resolved; + } + + private static String normalizeRelative(String path) { + return safeResolve(Path.of("unused"), path).toString().replace('\\', '/'); + } + + private static void deleteRecursively(Path path) throws IOException { + Path normalizedRoot = WORKSPACE_ROOT.normalize(); + Path normalizedPath = path.normalize(); + if (!normalizedPath.startsWith(normalizedRoot)) { + throw new IllegalArgumentException("Refusing to delete outside fixture workspace: " + path); + } + if (!Files.exists(normalizedPath)) { + return; + } + try (Stream paths = Files.walk(normalizedPath)) { + for (Path p : paths.sorted(Comparator.reverseOrder()).toList()) { + Files.deleteIfExists(p); + } + } + } + + private record SharedFixtureEntry( + String concreteCd, String role, String shared, String output, String packageLine) {} +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationTestCase.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationTestCase.java new file mode 100644 index 00000000..137e9a08 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationTestCase.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.testutil; + +import java.nio.file.Path; +import java.util.Set; + +/** Describes one cdconcretization adaptation test fixture. */ +public record CDConcretizationTestCase( + String id, + String displayName, + Path refCd, + Path concCd, + Path adapterPath, + Path concretePath, + Path outputPath, + Set mappings, + boolean strictParameterOrder, + boolean enabled) { + + public static final String RESOURCE_ROOT = + "src/test/resources/de/monticore/codeAdaption/cdconcretization/"; + + public static final String OUTPUT_ROOT = "target/adapter/cdconcretization/"; + + public CDConcretizationTestCase { + mappings = Set.copyOf(mappings); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationTestCases.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationTestCases.java new file mode 100644 index 00000000..d1567fab --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/CDConcretizationTestCases.java @@ -0,0 +1,346 @@ +package de.monticore.codeAdaption.testutil; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +/** Registry of all cdconcretization adaptation test fixtures. */ +public final class CDConcretizationTestCases { + + private static final Path RESOURCE_ROOT = Path.of(CDConcretizationTestCase.RESOURCE_ROOT); + + private static final Map REF_OVERRIDES = + Map.ofEntries( + Map.entry( + "evaluation/getter-setter/DataModelConc.cd", + "evaluation/getter-setter/GetterRef.cd"), + Map.entry( + "evaluation/builder/DataModelConc.cd", "evaluation/builder/BuilderAndMillRef.cd"), + Map.entry( + "evaluation/mill/LanguageInfrastructureConc.cd", "evaluation/mill/MillRef.cd"), + Map.entry( + "methods/forEach/ForEachAttributeSameReturnTypeClassMIConc.cd", + "methods/forEach/ForEachAttributeSameReturnTypeRef.cd"), + Map.entry( + "types/forEach/ForEachTypeNoTargetIncConc.cd", "types/forEach/ForEachTypeRef.cd"), + Map.entry( + "attributes/forEach/ForEachAttributeInheritanceConc.cd", + "attributes/forEach/ForEachAttributeRef.cd"), + Map.entry( + "attributes/forEach/ForEachAttributeDifferentNameClassMIConc.cd", + "attributes/forEach/ForEachAttributeDifferentNameRef.cd"), + Map.entry( + "evaluation/banking/singleInc/BankingConc.cd", "evaluation/banking/BankingRef.cd"), + Map.entry( + "evaluation/banking/multiInc/BankingConc.cd", "evaluation/banking/BankingRef.cd"), + Map.entry( + "evaluation/banking/usageByExtension/BankingConc.cd", + "evaluation/banking/BankingRef.cd"), + Map.entry( + "evaluation/staticDelegator/attrWorkaround/StaticExistsConc.cd", + "evaluation/staticDelegator/attrWorkaround/StaticDelegatorRef.cd"), + Map.entry( + "evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsConc.cd", + "evaluation/staticDelegator/attrWorkaround/StaticDelegatorRef.cd"), + Map.entry( + "evaluation/observer/mutualObservers/MutualObserversConc.cd", + "evaluation/observer/ObserverRef.cd"), + Map.entry( + "attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesConc.cd", + "attributes/underspecified/AttributeTypeUnderspecifiedRef.cd"), + Map.entry( + "attributes/underspecified/AttributeTypeUnderspecifiedNoIncConc.cd", + "attributes/underspecified/AttributeTypeUnderspecifiedRef.cd"), + Map.entry( + "methods/underspecified/ParameterTypeUnderspecifiedIncarnatedConc.cd", + "methods/underspecified/ParameterTypeUnderspecifiedRef.cd"), + Map.entry( + "methods/underspecified/ParameterTypeUnderspecifiedNoIncConc.cd", + "methods/underspecified/ParameterTypeUnderspecifiedRef.cd"), + Map.entry( + "methods/underspecified/ReturnTypeUnderspecifiedIncarnatedConc.cd", + "methods/underspecified/ReturnTypeUnderspecifiedRef.cd"), + Map.entry( + "methods/underspecified/ReturnTypeUnderspecifiedNoIncConc.cd", + "methods/underspecified/ReturnTypeUnderspecifiedRef.cd"), + Map.entry( + "multipleIncarnation/BothAssocSidesMIOneAssocExistsConc.cd", + "multipleIncarnation/BothAssocSidesMIRef.cd")); + + private static final Set EXPECTED_FAILURE_CASES = + Set.of( + "attributes/underspecified/AttributeTypeUnderspecifiedNoIncConc.cd", + "methods/underspecified/ParameterTypeUnderspecifiedNoIncConc.cd", + "methods/underspecified/ReturnTypeUnderspecifiedNoIncConc.cd"); + + private static final Map UNSUPPORTED_CASES = + Map.of( + "attributes/forEach/ForEachAttributeInheritanceConc.cd", + "Upstream cdconcretization does not derive forEach bindings across inherited attribute owners", + "attributes/forEach/ForEachAttributeNoTargetIncConc.cd", + "Upstream cdconcretization requires unimplemented matchStructure/optional-member semantics", + "evaluation/staticDelegator/StaticDelegatorConc.cd", + "Upstream cdconcretization does not implement method-target forEach completion"); + + private static final Map> MAPPING_OVERRIDES = + Map.of( + "evaluation/observer/mutualObservers/MutualObserversConc.cd", + Set.of("ref1", "ref2")); + + private static final Map STRICT_PARAMETER_ORDER = + Map.ofEntries( + Map.entry("evaluation/builder/DataModelConc.cd", true), + Map.entry("evaluation/getter-setter/DataModelConc.cd", false), + Map.entry("evaluation/visitor/VisitorConc.cd", true), + Map.entry("evaluation/banking2/BankingConc.cd", true), + Map.entry("evaluation/mill/LanguageInfrastructureConc.cd", true), + Map.entry("evaluation/repository/DomainModel.cd", true), + Map.entry("evaluation/crud-backend/DomainModel.cd", true), + Map.entry("evaluation/paper-examples/repository/EcommerceDomain.cd", true), + Map.entry("methods/multiIncarnation/ParameterTypeMIConc.cd", true), + Map.entry("methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd", true), + Map.entry("methods/forEach/ForEachAttributeConc.cd", true), + Map.entry("methods/forEach/ForEachAttributeMultipleParametersConc.cd", true), + Map.entry("methods/forEach/ForEachTypeSameParameterTypeConc.cd", true), + Map.entry("methods/forEach/ForEachTypeSameParameterTypeNoNameMatchConc.cd", true), + Map.entry("evaluation/staticDelegator/StaticDelegatorConc.cd", true), + Map.entry("evaluation/staticDelegator/attrWorkaround/StaticExistsConc.cd", true), + Map.entry("evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsConc.cd", true), + Map.entry("evaluation/transitiveDependencies/TransitiveDependenciesConc.cd", true), + Map.entry("evaluation/crud-backend/CRUDBackendConc.cd", true)); + + private static final Map EXPECTED_OUTPUT_OVERRIDES = + Map.of( + "evaluation/builder/DataModelConc.cd", "evaluation/builder/BuilderAndMillOut.cd", + "evaluation/getter-setter/DataModelConc.cd", "evaluation/getter-setter/GetterOut.cd", + "evaluation/macoco/EmptyConc.cd", "evaluation/macoco/EmptyConcOut.cd", + "evaluation/mill/LanguageInfrastructureConc.cd", "evaluation/mill/MillOut.cd"); + + /** Upstream asserts these completed CDs directly against their reference CDs. */ + private static final Set REFERENCE_AS_EXPECTED_OUTPUT_CASES = + Set.of( + "attributes/valid/AttributesMissingConc.cd", + "attributes/valid/TwoAttributesMissingConc.cd", + "attributes/valid/TwoAttributesMissingOneMatchConc.cd", + "inheritance/MissingInheritanceConc.cd", + "methods/basic/valid/ClassEmptyConc.cd", + "methods/basic/valid/ClassMissingConc.cd", + "methods/basic/valid/MethodMissingConc.cd", + "methods/basic/valid/MultipleMethodsMissingConc.cd"); + + private static final Map STRUCTURAL_ORACLE_EXCLUSIONS = + Map.ofEntries( + Map.entry( + "associations/AssociationMissingSimpleConc.cd", + "Upstream test is disabled until its association semantics issue is clarified"), + Map.entry( + "inheritance/AttributeTypeMismatchConc.cd", + "Upstream cdconcretization incorrectly accepts an inherited attribute with an incompatible type"), + Map.entry( + "evaluation/banking/multiInc/BankingConc.cd", + "Upstream test is disabled because bind mappings are not considered while " + + "completing associations"), + Map.entry( + "evaluation/cross-references/MicroserviceConc.cd", + "Upstream test is disabled because forEach cannot express global cross-incarnation references"), + Map.entry( + "evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsConc.cd", + "Upstream test is disabled because forEach completion is not bidirectional")); + + private CDConcretizationTestCases() {} + + public static List allCases() { + try { + List cases = new ArrayList<>(); + try (Stream paths = Files.walk(RESOURCE_ROOT)) { + paths + .filter(Files::isRegularFile) + .map(RESOURCE_ROOT::relativize) + .map(path -> path.toString().replace('\\', '/')) + .filter(path -> path.endsWith("Conc.cd")) + .sorted() + .forEach(path -> cases.add(buildCase(path))); + } + cases.sort(Comparator.comparing(CDConcretizationTestCase::id)); + return List.copyOf(cases); + } catch (IOException e) { + throw new IllegalStateException("Failed to discover cdconcretization test cases", e); + } + } + + static CDConcretizationTestCase buildCase(String concRelativePath) { + String refRelativePath = resolveRefPath(concRelativePath); + String caseBase = concRelativePath.substring(concRelativePath.lastIndexOf('/') + 1); + caseBase = caseBase.substring(0, caseBase.length() - "Conc.cd".length()); + String id = concRelativePath.replace('/', '_').replace(".cd", ""); + + Path refCd = RESOURCE_ROOT.resolve(refRelativePath); + Path concCd = RESOURCE_ROOT.resolve(concRelativePath); + + boolean sharedRef = !parent(refRelativePath).equals(parent(concRelativePath)); + boolean sharedReferenceFile = refRelativePath.endsWith("Reference.cd"); + + Path adapterPath; + if (sharedRef || sharedReferenceFile) { + adapterPath = resolveDirectory(parent(refRelativePath)).resolve("adapter"); + } else { + adapterPath = + resolveDirectory(parent(concRelativePath)).resolve("adapter").resolve(caseBase); + } + + Path concretePath; + if (sharedRef) { + concretePath = resolveDirectory(parent(concRelativePath)).resolve("concrete").resolve(caseBase); + } else if (sharedReferenceFile) { + concretePath = + resolveDirectory(parent(concRelativePath)).resolve("concrete").resolve(caseBase); + } else { + concretePath = resolveDirectory(parent(concRelativePath)).resolve("concrete"); + } + + Path outputPath = Path.of(CDConcretizationTestCase.OUTPUT_ROOT + id); + + return new CDConcretizationTestCase( + id, + concRelativePath.replace("Conc.cd", ""), + refCd, + concCd, + adapterPath, + concretePath, + outputPath, + MAPPING_OVERRIDES.getOrDefault(concRelativePath, Set.of("ref")), + STRICT_PARAMETER_ORDER.getOrDefault(concRelativePath, false), + !EXPECTED_FAILURE_CASES.contains(concRelativePath) + && !UNSUPPORTED_CASES.containsKey(concRelativePath)); + } + + public static List enabledCases() { + String requestedCase = System.getenv("CDCONCRETIZATION_CASE"); + Set requestedCases = + requestedCase == null || requestedCase.isBlank() + ? Set.of() + : Set.of(requestedCase.split(",")); + return allCases().stream() + .filter(CDConcretizationTestCase::enabled) + .filter( + testCase -> + requestedCases.isEmpty() + || requestedCases.contains(testCase.id()) + || requestedCases.contains(testCase.displayName())) + .toList(); + } + + public static List expectedFailureCases() { + return allCases().stream() + .filter(testCase -> EXPECTED_FAILURE_CASES.contains(relativeConcretePath(testCase))) + .toList(); + } + + public static List unsupportedCases() { + return allCases().stream() + .filter(testCase -> UNSUPPORTED_CASES.containsKey(relativeConcretePath(testCase))) + .toList(); + } + + public static String unsupportedReason(CDConcretizationTestCase testCase) { + return UNSUPPORTED_CASES.get(relativeConcretePath(testCase)); + } + + public static Optional expectedOutputModel(CDConcretizationTestCase testCase) { + String concretePath = relativeConcretePath(testCase); + if (STRUCTURAL_ORACLE_EXCLUSIONS.containsKey(concretePath)) { + return Optional.empty(); + } + if (REFERENCE_AS_EXPECTED_OUTPUT_CASES.contains(concretePath)) { + return Optional.of(testCase.refCd()); + } + String expectedPath = + EXPECTED_OUTPUT_OVERRIDES.getOrDefault( + concretePath, concretePath.replace("Conc.cd", "Out.cd")); + Path expected = RESOURCE_ROOT.resolve(expectedPath); + return Files.isRegularFile(expected) ? Optional.of(expected) : Optional.empty(); + } + + public static Optional noExpectedOutputReason(CDConcretizationTestCase testCase) { + return Optional.ofNullable(STRUCTURAL_ORACLE_EXCLUSIONS.get(relativeConcretePath(testCase))); + } + + static String resolveRefPath(String concRelativePath) { + if (REF_OVERRIDES.containsKey(concRelativePath)) { + return REF_OVERRIDES.get(concRelativePath); + } + + String concDir = parent(concRelativePath); + String caseBase = + concRelativePath + .substring(concRelativePath.lastIndexOf('/') + 1) + .replace("Conc.cd", ""); + + String pairedRef = concDir.isEmpty() ? caseBase + "Ref.cd" : concDir + "/" + caseBase + "Ref.cd"; + if (Files.exists(RESOURCE_ROOT.resolve(pairedRef))) { + return pairedRef; + } + + String localReference = concDir.isEmpty() ? "Reference.cd" : concDir + "/Reference.cd"; + if (Files.exists(RESOURCE_ROOT.resolve(localReference))) { + return localReference; + } + + String parentReference = + parent(concDir).isEmpty() ? "Reference.cd" : parent(concDir) + "/Reference.cd"; + if (Files.exists(RESOURCE_ROOT.resolve(parentReference))) { + return parentReference; + } + + String singleRefInDir = findSingleRefInDirectory(concDir); + if (singleRefInDir != null) { + return singleRefInDir; + } + + throw new IllegalStateException( + "Could not resolve reference CD for concrete fixture: " + concRelativePath); + } + + private static String findSingleRefInDirectory(String concDir) { + Path directory = resolveDirectory(concDir); + if (!Files.isDirectory(directory)) { + return null; + } + try (Stream refs = Files.list(directory)) { + List refFiles = + refs.map(Path::getFileName) + .map(Path::toString) + .filter(name -> name.endsWith("Ref.cd")) + .sorted() + .toList(); + if (refFiles.size() == 1) { + return concDir.isEmpty() ? refFiles.get(0) : concDir + "/" + refFiles.get(0); + } + } catch (IOException ignored) { + return null; + } + return null; + } + + private static String parent(String relativePath) { + int slash = relativePath.lastIndexOf('/'); + return slash < 0 ? "" : relativePath.substring(0, slash); + } + + private static String relativeConcretePath(CDConcretizationTestCase testCase) { + return testCase.concCd().toString() + .replace('\\', '/') + .replace(CDConcretizationTestCase.RESOURCE_ROOT, ""); + } + + private static Path resolveDirectory(String relativeDir) { + return relativeDir.isEmpty() ? RESOURCE_ROOT : RESOURCE_ROOT.resolve(relativeDir); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/GeneratedJavaStructureOracle.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/GeneratedJavaStructureOracle.java new file mode 100644 index 00000000..00a9a285 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/testutil/GeneratedJavaStructureOracle.java @@ -0,0 +1,305 @@ +package de.monticore.codeAdaption.testutil; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.cdinterfaceandenum._ast.ASTCDEnum; +import de.monticore.codeAdaption.utils.CDModelIndex; +import de.monticore.codeAdaption.utils.CDTypeRelations; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.JavaSourceNames; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTClassDeclaration; +import de.monticore.java.javadsl._ast.ASTEnumConstantDeclaration; +import de.monticore.java.javadsl._ast.ASTEnumDeclaration; +import de.monticore.java.javadsl._ast.ASTInterfaceDeclaration; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.java.javadsl._visitor.JavaDSLTraverser; +import de.monticore.java.javadsl._visitor.JavaDSLVisitor2; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Structural oracle for Java-expressible elements materialized in adapter output. */ +public final class GeneratedJavaStructureOracle { + + private GeneratedJavaStructureOracle() {} + + public static void assertMatches( + Path originalConcreteCd, Path expectedOutputCd, List generatedJava) { + CDModelIndex original = CDModelIndex.of(JavaLoader.parseCD(originalConcreteCd.toString())); + CDModelIndex expected = CDModelIndex.of(JavaLoader.parseCD(expectedOutputCd.toString())); + Map> generatedBySimpleName = new LinkedHashMap<>(); + for (Path source : generatedJava) { + ASTOrdinaryCompilationUnit unit = JavaLoader.loadJava(source.toFile()); + JavaAstElemCollector collector = collect(unit); + for (ASTTypeDeclaration type : unit.getTypeDeclarationList()) { + generatedBySimpleName + .computeIfAbsent(type.getName(), ignored -> new ArrayList<>()) + .add(GeneratedType.from(type, collector, source)); + } + } + + for (ASTCDType expectedType : expected.types()) { + ASTCDType originalType = original.type(expectedType.getName()).orElse(null); + List candidates = + generatedBySimpleName.getOrDefault(expectedType.getName(), List.of()); + if (candidates.isEmpty()) { + // Types without handwritten/adapted Java are supplied later by CD4Code and are represented + // by strict compilation stubs rather than adapter output. When a type is materialized here, + // all completion deltas below are mandatory. + continue; + } + if (candidates.size() > 1) { + fail( + "Expected type '" + + expectedType.getName() + + "' is ambiguous in generated Java: " + + candidates.stream().map(GeneratedType::source).toList()); + } + assertType(originalType, expectedType, candidates.get(0)); + } + } + + private static void assertType( + ASTCDType original, ASTCDType expected, GeneratedType actual) { + String context = expected.getName() + " in " + actual.source(); + assertEquals(kind(expected), actual.kind(), "Wrong type kind for " + context); + + Map originalFields = fields(original); + Map expectedFields = new LinkedHashMap<>(); + expected + .getCDAttributeList() + .forEach( + field -> + expectedFields.put( + field.getName(), JavaSourceNames.printNormalizedFieldType(field))); + expectedFields.forEach( + (name, type) -> + { + if (!type.equals(originalFields.get(name))) { + assertEquals( + type, + actual.fields().get(name), + "Missing or mistyped completed field '" + name + "' on " + context); + } + }); + + Map originalMethods = methods(original); + Map expectedMethods = new LinkedHashMap<>(); + expected + .getCDMethodList() + .forEach( + method -> + expectedMethods.put( + JavaSourceNames.methodSignature(method), + JavaSourceNames.printNormalizedReturnType(method))); + expectedMethods.forEach( + (signature, returnType) -> + { + if (!returnType.equals(originalMethods.get(signature))) { + assertEquals( + returnType, + actual.methods().get(signature), + "Missing or mistyped completed method '" + signature + "' on " + context); + } + }); + + String originalSuperclass = + original == null + ? null + : CDTypeRelations.firstSuperclassName(original) + .map(JavaSourceNames::simpleName) + .orElse(null); + String expectedSuperclass = + expected.getSuperclassList().size() == 1 + ? CDTypeRelations.firstSuperclassName(expected) + .map(JavaSourceNames::simpleName) + .orElse(null) + : null; + if (expected.getSuperclassList().size() <= 1 + && !java.util.Objects.equals(originalSuperclass, expectedSuperclass)) { + assertEquals(expectedSuperclass, actual.superclass(), "Wrong completed superclass for " + context); + } + Set originalInterfaces = interfaces(original); + Set expectedInterfaces = + CDTypeRelations.interfaceNames(expected).stream() + .map(JavaSourceNames::simpleName) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + Set addedInterfaces = difference(expectedInterfaces, originalInterfaces); + assertTrue( + actual.interfaces().containsAll(addedInterfaces), + () -> + "Missing interfaces " + + difference(addedInterfaces, actual.interfaces()) + + " on " + + context); + + if (expected instanceof ASTCDEnum expectedEnum) { + List originalConstants = + original instanceof ASTCDEnum originalEnum + ? originalEnum.getCDEnumConstantList().stream() + .map(constant -> constant.getName()) + .toList() + : List.of(); + List constants = + expectedEnum.getCDEnumConstantList().stream().map(constant -> constant.getName()).toList(); + if (!constants.equals(originalConstants)) { + assertEquals(constants, actual.enumConstants(), "Wrong completed enum constants for " + context); + } + } + } + + private static Map fields(ASTCDType type) { + Map result = new LinkedHashMap<>(); + if (type != null) { + type.getCDAttributeList() + .forEach( + field -> + result.put(field.getName(), JavaSourceNames.printNormalizedFieldType(field))); + } + return result; + } + + private static Map methods(ASTCDType type) { + Map result = new LinkedHashMap<>(); + if (type != null) { + type.getCDMethodList() + .forEach( + method -> + result.put( + JavaSourceNames.methodSignature(method), + JavaSourceNames.printNormalizedReturnType(method))); + } + return result; + } + + private static Set interfaces(ASTCDType type) { + if (type == null) { + return Set.of(); + } + return CDTypeRelations.interfaceNames(type).stream() + .map(JavaSourceNames::simpleName) + .collect(java.util.stream.Collectors.toCollection(LinkedHashSet::new)); + } + + private static String kind(ASTCDType type) { + if (type instanceof de.monticore.cdbasis._ast.ASTCDClass) { + return "class"; + } + if (type instanceof de.monticore.cdinterfaceandenum._ast.ASTCDInterface) { + return "interface"; + } + return "enum"; + } + + private static Set difference(Set expected, Set actual) { + Set missing = new LinkedHashSet<>(expected); + missing.removeAll(actual); + return missing; + } + + private static JavaAstElemCollector collect(ASTOrdinaryCompilationUnit unit) { + JavaAstElemCollector collector = new JavaAstElemCollector(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(collector); + unit.accept(traverser); + return collector; + } + + private record GeneratedType( + String kind, + Map fields, + Map methods, + String superclass, + Set interfaces, + List enumConstants, + Path source) { + + private static GeneratedType from( + ASTTypeDeclaration type, JavaAstElemCollector collector, Path source) { + Map fields = new LinkedHashMap<>(); + collector + .getAllFieldDeclarations(type) + .forEach( + field -> + field + .getVariableDeclaratorList() + .forEach( + variable -> + fields.put( + variable.getDeclarator().getName(), + JavaSourceNames.printNormalizedType(field.getMCType())))); + + Map methods = new LinkedHashMap<>(); + for (ASTMethodDeclaration method : collector.getAllMethodDeclarations(type)) { + List parameterTypes = + collector.getAllParameters(type, method).stream() + .map(parameter -> JavaSourceNames.printNormalizedType(parameter.getMCType())) + .toList(); + methods.put( + method.getName() + "(" + String.join(",", parameterTypes) + ")", + JavaSourceNames.normalizeType(JavaLoader.print(method.getMCReturnType()))); + } + + String kind; + String superclass = null; + Set interfaces = new LinkedHashSet<>(); + List enumConstants = List.of(); + if (type instanceof ASTClassDeclaration classDeclaration) { + kind = "class"; + if (classDeclaration.isPresentSuperClass()) { + superclass = JavaSourceNames.simpleName(JavaLoader.print(classDeclaration.getSuperClass())); + } + classDeclaration.getImplementedInterfaceList().stream() + .map(JavaLoader::print) + .map(JavaSourceNames::simpleName) + .forEach(interfaces::add); + } else if (type instanceof ASTInterfaceDeclaration interfaceDeclaration) { + kind = "interface"; + interfaceDeclaration.getExtendedInterfaceList().stream() + .map(JavaLoader::print) + .map(JavaSourceNames::simpleName) + .forEach(interfaces::add); + } else if (type instanceof ASTEnumDeclaration enumDeclaration) { + kind = "enum"; + enumDeclaration.getImplementedInterfaceList().stream() + .map(JavaLoader::print) + .map(JavaSourceNames::simpleName) + .forEach(interfaces::add); + List constants = new ArrayList<>(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL( + new JavaDSLVisitor2() { + @Override + public void visit(ASTEnumConstantDeclaration constant) { + constants.add(constant.getName()); + } + }); + enumDeclaration.accept(traverser); + enumConstants = List.copyOf(constants); + } else { + assertFalse(true, "Unsupported generated Java type " + type.getClass().getName()); + throw new AssertionError(); + } + return new GeneratedType( + kind, + Map.copyOf(fields), + Map.copyOf(methods), + superclass, + Set.copyOf(interfaces), + enumConstants, + source); + } + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/updater/CodeUpdaterMillTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/updater/CodeUpdaterMillTest.java new file mode 100644 index 00000000..9c302497 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/updater/CodeUpdaterMillTest.java @@ -0,0 +1,53 @@ +package de.monticore.codeAdaption.updater; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; + +import de.monticore.codeAdaption.updater.spoonUpdater.SpoonUpdater; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +class CodeUpdaterMillTest { + + @AfterEach + void restoreDefaultProvider() { + CodeUpdaterMill.init(); + } + + @Test + void reusesCurrentUpdaterUntilReset() { + AtomicInteger createdUpdaters = new AtomicInteger(); + CodeUpdaterMill.init( + () -> { + createdUpdaters.incrementAndGet(); + return new SpoonUpdater(); + }); + + CodeUpdater first = CodeUpdaterMill.getUpdater(); + + assertSame(first, CodeUpdaterMill.getUpdater()); + assertEquals(1, createdUpdaters.get()); + + CodeUpdaterMill.reset(); + CodeUpdater second = CodeUpdaterMill.getUpdater(); + + assertNotSame(first, second); + assertEquals(2, createdUpdaters.get()); + } + + @Test + void initRestoresDefaultSpoonProviderAndClearsCurrentUpdater() { + CodeUpdater customUpdater = new SpoonUpdater(); + CodeUpdaterMill.init(() -> customUpdater); + assertSame(customUpdater, CodeUpdaterMill.getUpdater()); + + CodeUpdaterMill.init(); + CodeUpdater defaultUpdater = CodeUpdaterMill.getUpdater(); + + assertInstanceOf(SpoonUpdater.class, defaultUpdater); + assertNotSame(customUpdater, defaultUpdater); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonServicesTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonServicesTest.java new file mode 100644 index 00000000..90cf4b81 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/updater/spoonUpdater/SpoonServicesTest.java @@ -0,0 +1,1019 @@ +package de.monticore.codeAdaption.updater.spoonUpdater; + +import de.monticore.codeAdaption.updater.CodeUpdater.MethodBodySpec; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import de.monticore.cdbasis._ast.ASTCDType; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.handler.multiIncarnation.StableElementKey; +import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.java.javadsl.JavaDSLMill; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._ast.ASTTypeDeclaration; +import de.monticore.java.javadsl._visitor.JavaDSLTraverser; +import de.monticore.javalight._ast.ASTMethodDeclaration; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.reference.CtTypeReference; +import spoon.reflect.visitor.filter.TypeFilter; + +class SpoonServicesTest { + @TempDir Path temporaryDirectory; + + @BeforeEach + void initializeMill() { + AdapterAbstractTest.initMills(); + } + + @Test + void reloadReplacesTheModelAndTypeReferencesAreClonedFromCachedPrototypes() throws IOException { + Path first = sourceDirectory("first", "First"); + Path second = sourceDirectory("second", "Second"); + SpoonWorkspace workspace = new SpoonWorkspace(); + + workspace.load(first); + assertEquals("First", workspace.model().getAllTypes().iterator().next().getSimpleName()); + CtTypeReference firstReference = workspace.createTypeReference("List"); + CtTypeReference secondReference = workspace.createTypeReference("List"); + assertNotSame(firstReference, secondReference); + + workspace.load(second); + assertEquals("Second", workspace.model().getAllTypes().iterator().next().getSimpleName()); + } + + @Test + void executableRewriteRegistryNormalizesSignaturesAndValidatesMethodKeys() { + ExecutableRewriteRegistry registry = new ExecutableRewriteRegistry(); + registry.registerConcreteSignature("run", List.of("java.lang.String", "any[]")); + + assertEquals(List.of("String", "Object[]"), registry.unambiguousLegacyParameters("run")); + assertThrows( + IllegalArgumentException.class, + () -> + registry.registerRewrite( + StableElementKey.type("NotAMethod"), + StableElementKey.method("Owner", "run", List.of()))); + } + + @Test + void typeResolutionUsesTheSourcePathWhenPackagesContainTheSameSimpleName() + throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("packages")); + Path first = javaSource(sources, "alpha", "Shared", "class Shared {}"); + javaSource(sources, "beta", "Shared", "class Shared {}"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + + ASTTypeDeclaration sourceType = loadType(first, "Shared"); + + assertEquals("alpha.Shared", resolver.getSpoonType(sourceType).getQualifiedName()); + } + + @Test + void methodResolutionRequiresAnExactDeclaredName() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("methods")); + Path source = + javaSource( + sources, + "sample", + "Child", + "class Base { void run() {} } class Child extends Base { void targetRun() {} }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + ASTTypeDeclaration child = loadType(source, "Child"); + ASTMethodDeclaration targetRun = loadMethod(source, "Child", "targetRun"); + + assertEquals("targetRun", resolver.getSpoonMethod(child, targetRun).getSimpleName()); + } + + @Test + void clonedMethodParametersRemainBoundToTheClonedBody() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("generation")); + Path source = + javaSource( + sources, + "sample", + "Echo", + "class Echo { String echo(String value) { return value; } " + + "String combine(String kept, String removed) { return kept + removed; } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration echo = loadType(source, "Echo"); + ASTMethodDeclaration template = loadMethod(source, "Echo", "echo"); + + generation.addMethod( + echo, + template, + "renamedEcho", + List.of("String"), + List.of("renamedValue"), + "String", + false, + MethodBodySpec.empty()); + + String generated = + resolver.getSpoonType(echo).getMethodsByName("renamedEcho").get(0).toString(); + assertTrue(generated.contains("return renamedValue"), generated); + + generation.addMethod( + echo, + loadMethod(source, "Echo", "combine"), + "reduced", + List.of("String"), + List.of("kept"), + "String", + false, + MethodBodySpec.safeDefault()); + generation.addMethod( + echo, + template, + "expanded", + List.of("String", "int"), + List.of("value", "ignored"), + "String", + false, + MethodBodySpec.empty()); + + String reduced = resolver.getSpoonType(echo).getMethodsByName("reduced").get(0).toString(); + String expanded = resolver.getSpoonType(echo).getMethodsByName("expanded").get(0).toString(); + assertTrue(reduced.contains("return null"), reduced); + assertFalse(reduced.contains("removed"), reduced); + assertTrue(expanded.contains("return value"), expanded); + } + + @Test + void clonedPrimitiveMethodReplacesNullWithACompilableDefault() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("primitive-return")); + Path source = + javaSource( + sources, + "sample", + "Template", + "class Template { Object value() { return null; } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration type = loadType(source, "Template"); + ASTMethodDeclaration template = loadMethod(source, "Template", "value"); + + generation.addMethod( + type, + template, + "number", + List.of(), + List.of(), + "int", + false, + MethodBodySpec.empty()); + + String generated = resolver.getSpoonType(type).getMethodsByName("number").get(0).toString(); + assertTrue(generated.contains("return 0"), generated); + } + + @Test + void completedModelProjectionCreatesAndRepairsDeclarationsWithoutTemplates() + throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("completion-delta")); + Path source = + javaSource( + sources, + "sample", + "Child", + "class Parent {} interface Contract {} enum Colour { RED, BLUE } " + + "class Child { Object value; }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration child = loadType(source, "Child"); + ASTTypeDeclaration colour = loadType(source, "Colour"); + + generation.addField(child, null, "value", "int", false); + generation.addField(child, null, "added", "String", false); + generation.addMethod( + child, + null, + "number", + List.of(), + List.of(), + "int", + false, + MethodBodySpec.empty()); + generation.addSuperType(child, "Parent", false); + generation.addSuperType(child, "Contract", true); + generation.addEnumConstant(colour, "GREEN", 1); + + var spoonChild = resolver.getSpoonType(child); + assertEquals("int", spoonChild.getField("value").getType().getSimpleName()); + assertEquals("String", spoonChild.getField("added").getType().getSimpleName()); + assertTrue(spoonChild.getMethodsByName("number").get(0).toString().contains("return 0")); + assertEquals("Parent", spoonChild.getSuperclass().getSimpleName()); + assertTrue( + spoonChild.getSuperInterfaces().stream() + .anyMatch(type -> "Contract".equals(type.getSimpleName()))); + assertEquals( + List.of("RED", "GREEN", "BLUE"), + ((spoon.reflect.declaration.CtEnum) resolver.getSpoonType(colour)) + .getEnumValues().stream() + .map(value -> value.getSimpleName()) + .toList()); + } + + @Test + void concreteInterfaceContractsArePublicAndReceiveSafeBodies() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("interface-contracts")); + Path source = + javaSource( + sources, + "sample", + "Implementation", + "class Implementation { String existing() { return \"kept\"; } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration implementation = loadType(source, "Implementation"); + + generation.setTypeAbstract(implementation, true); + assertTrue( + resolver + .getSpoonType(implementation) + .hasModifier(spoon.reflect.declaration.ModifierKind.ABSTRACT)); + generation.setTypeAbstract(implementation, false); + assertFalse( + resolver + .getSpoonType(implementation) + .hasModifier(spoon.reflect.declaration.ModifierKind.ABSTRACT)); + + generation.addMethod( + implementation, + null, + "existing", + List.of(), + List.of(), + "String", + false, + MethodBodySpec.interfaceContract()); + generation.addMethod( + implementation, + null, + "enabled", + List.of(), + List.of(), + "boolean", + false, + MethodBodySpec.interfaceContract()); + generation.addMethod( + implementation, + null, + "description", + List.of(), + List.of(), + "String", + false, + MethodBodySpec.interfaceContract()); + generation.addMethod( + implementation, + null, + "reset", + List.of(), + List.of(), + "void", + false, + MethodBodySpec.interfaceContract()); + + var type = resolver.getSpoonType(implementation); + String existing = type.getMethodsByName("existing").get(0).toString(); + assertTrue(existing.contains("public"), existing); + assertTrue(existing.contains("return \"kept\""), existing); + assertTrue(type.getMethodsByName("enabled").get(0).toString().contains("return false")); + assertTrue(type.getMethodsByName("description").get(0).toString().contains("return null")); + assertNotNull(type.getMethodsByName("reset").get(0).getBody()); + } + + @Test + void interfaceContractPreservesExistingCovariantReturnAndBody() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("covariant-contract")); + Path source = + javaSource( + sources, + "sample", + "Implementation", + "class Implementation { protected String value() { return \"kept\"; } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration implementation = loadType(source, "Implementation"); + + generation.addMethod( + implementation, + null, + "value", + List.of(), + List.of(), + "Object", + false, + MethodBodySpec.interfaceContract()); + + var method = resolver.getSpoonType(implementation).getMethodsByName("value").get(0); + assertEquals("String", method.getType().getSimpleName()); + assertTrue(method.hasModifier(spoon.reflect.declaration.ModifierKind.PUBLIC)); + assertFalse(method.hasModifier(spoon.reflect.declaration.ModifierKind.PROTECTED)); + assertTrue(method.toString().contains("return \"kept\""), method.toString()); + } + + @Test + void interfaceContractRejectsIncompatibleOrStaticExistingMethod() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("invalid-contract")); + Path source = + javaSource( + sources, + "sample", + "Implementation", + "class Implementation { Integer value() { return 1; } " + + "static String utility() { return \"kept\"; } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration implementation = loadType(source, "Implementation"); + + assertThrows( + IllegalStateException.class, + () -> + generation.addMethod( + implementation, + null, + "value", + List.of(), + List.of(), + "String", + false, + MethodBodySpec.interfaceContract())); + assertThrows( + IllegalStateException.class, + () -> + generation.addMethod( + implementation, + null, + "utility", + List.of(), + List.of(), + "String", + false, + MethodBodySpec.interfaceContract())); + + assertEquals( + "Integer", + resolver + .getSpoonType(implementation) + .getMethodsByName("value") + .get(0) + .getType() + .getSimpleName()); + assertTrue( + resolver + .getSpoonType(implementation) + .getMethodsByName("utility") + .get(0) + .hasModifier(spoon.reflect.declaration.ModifierKind.STATIC)); + } + + @Test + void interfaceContractUsesPreservedReturnForSynthesizedBody() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("bodyless-contract")); + Path source = + javaSource( + sources, + "sample", + "Implementation", + "abstract class Implementation { protected abstract String value(); }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration implementation = loadType(source, "Implementation"); + + generation.addMethod( + implementation, + null, + "value", + List.of(), + List.of(), + "Object", + false, + MethodBodySpec.interfaceContract()); + + var method = resolver.getSpoonType(implementation).getMethodsByName("value").get(0); + assertEquals("String", method.getType().getSimpleName()); + assertFalse(method.hasModifier(spoon.reflect.declaration.ModifierKind.ABSTRACT)); + assertTrue(method.toString().contains("return null"), method.toString()); + } + + @Test + void interfaceContractKeepsACompatibleInheritedImplementation() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("inherited-contract")); + Path source = + javaSource( + sources, + "sample", + "Child", + "class Base { public String value() { return \"base\"; } } class Child extends Base {}"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration childAst = loadType(source, "Child"); + + generation.addMethod( + childAst, + null, + "value", + List.of(), + List.of(), + "Object", + false, + MethodBodySpec.interfaceContract()); + + var child = resolver.getSpoonType(childAst); + var base = workspace.model().getAllTypes().stream() + .filter(type -> "Base".equals(type.getSimpleName())) + .findFirst() + .orElseThrow(); + assertTrue(child.getMethods().stream().noneMatch(method -> "value".equals(method.getSimpleName()))); + assertTrue(base.getMethodsByName("value").get(0).toString().contains("return \"base\"")); + } + + @Test + void generatedMethodLookupDistinguishesQualifiedParameterTypes() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("qualified-overload")); + javaSource(sources, "alpha", "Value", "public class Value {}"); + javaSource(sources, "beta", "Value", "public class Value {}"); + Path source = + javaSource( + sources, + "sample", + "Service", + "class Service { void run(alpha.Value value) {} }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonGenerationService generation = new SpoonGenerationService(workspace, resolver); + ASTTypeDeclaration service = loadType(source, "Service"); + + generation.addMethod( + service, + null, + "run", + List.of("beta.Value"), + List.of("value"), + "void", + false, + MethodBodySpec.empty()); + + assertEquals(2, resolver.getSpoonType(service).getMethodsByName("run").size()); + } + + @Test + void typeRewriteDoesNotTouchResolvedLibraryTypesWithTheSameSimpleName() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("library-type")); + javaSource( + sources, + "sample", + "UsesDate", + "class UsesDate { java.util.Date createdAt; }"); + Path cd = temporaryDirectory.resolve("Model.cd"); + Files.writeString(cd, "classdiagram Model { class Date; }"); + ASTCDType date = + JavaLoader.parseCD(cd.toString()).getCDDefinition().getCDClassesList().get(0); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonTransformationService transformations = services(workspace).transformations(); + + transformations.updateCDType(date, "RenamedDate"); + + var renderedType = workspace.model().getAllTypes().iterator().next(); + assertEquals( + "java.util.Date", + renderedType.getField("createdAt").getType().getQualifiedName()); + } + + @Test + void typeRewriteUpdatesUnresolvedTypesFromTheAdapterPackage() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("adapter-type")); + javaSource( + sources, + "sample", + "UsesRole", + "class UsesRole { Role role; Role first(Role fallback) { return fallback; } }"); + Path cd = temporaryDirectory.resolve("Reference.cd"); + Files.writeString(cd, "classdiagram Reference { class Role; }"); + ASTCDType role = + JavaLoader.parseCD(cd.toString()).getCDDefinition().getCDClassesList().get(0); + SpoonWorkspace workspace = loadedWorkspace(sources); + SpoonTransformationService transformations = services(workspace).transformations(); + + transformations.updateCDType(role, "HiwiRole"); + + CtTypeReference fieldType = + workspace.model().getAllTypes().iterator().next().getField("role").getType(); + assertEquals("HiwiRole", fieldType.getSimpleName()); + assertTrue( + workspace.model().getAllTypes().iterator().next().toString().contains("HiwiRole first")); + } + + @Test + void methodRenameUpdatesOnlyTheSelectedOverload() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("overload-rename")); + Path source = + javaSource( + sources, + "sample", + "Service", + "class Service { void run(String value) {} void run(int first, int second) {} " + + "void call() { run(\"x\"); run(1, 2); } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + + services + .transformations() + .updateMethod(loadType(source, "Service"), loadMethod(source, "Service", "run", 1), "execute"); + + String rendered = workspace.model().getAllTypes().stream() + .filter(type -> "Service".equals(type.getSimpleName())) + .findFirst() + .orElseThrow() + .toString(); + assertTrue(rendered.contains("execute(\"x\")"), rendered); + assertTrue(rendered.contains("run(1, 2)"), rendered); + assertEquals(1, workspace.model().getAllTypes().iterator().next().getMethodsByName("run").size()); + } + + @Test + void methodRenameUpdatesStaticAndInheritedInvocations() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("static-inherited")); + Path source = + javaSource( + sources, + "sample", + "Base", + "class Base { static void create() {} void run() {} } " + + "class Child extends Base { void call() { run(); Base.create(); } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + ASTTypeDeclaration base = loadType(source, "Base"); + + services.transformations().updateMethod(base, loadMethod(source, "Base", "run"), "execute"); + services.transformations().updateMethod(base, loadMethod(source, "Base", "create"), "make"); + + String rendered = workspace.model().getAllTypes().stream() + .filter(type -> "Child".equals(type.getSimpleName())) + .findFirst() + .orElseThrow() + .toString(); + assertTrue(rendered.contains("execute()"), rendered); + assertTrue(rendered.contains("Base.make()"), rendered); + } + + @Test + void stableMethodRewriteSelectsTheOverloadAndAddsACompatibleArgument() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("signature-repair")); + javaSource( + sources, + "sample", + "Service", + "class Item {} class SpecialItem extends Item {} class OtherItem extends Item {} " + + "class Service { void target(String value) {} void target(int value) {} " + + "void call(SpecialItem special) { target(\"x\"); target(1); } " + + "void exact(Item item, SpecialItem special) { target(\"y\"); } " + + "void ambiguous(SpecialItem first, OtherItem second) { target(\"z\"); } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + services + .repairs() + .registerMethodRewrite( + StableElementKey.method("Service", "target", List.of("String")), + StableElementKey.method("Service", "execute", List.of("String", "Item"))); + + services.repairs().prepareForPrint(Map.of()); + + String rendered = workspace.model().getAllTypes().stream() + .filter(type -> "Service".equals(type.getSimpleName())) + .findFirst() + .orElseThrow() + .toString(); + assertTrue(rendered.contains("execute(\"x\", special)"), rendered); + assertTrue(rendered.contains("execute(\"y\", item)"), rendered); + assertTrue(rendered.contains("execute(\"z\", null)"), rendered); + assertTrue(rendered.contains("target(1)"), rendered); + } + + @Test + void missingArgumentRejectsDifferentlyQualifiedTypeWithSameSimpleName() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("ambiguous-argument")); + javaSource(sources, "alpha", "Role", "public class Role {}"); + javaSource(sources, "beta", "Role", "public class Role {}"); + javaSource( + sources, + "sample", + "Service", + "class Service { void targetRole(String value) {} " + + "void qualified(alpha.Role role) { targetRole(\"y\"); } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + services + .repairs() + .registerMethodRewrite( + StableElementKey.method("Service", "targetRole", List.of("String")), + StableElementKey.method("Service", "executeRole", List.of("String", "beta.Role"))); + + services.repairs().prepareForPrint(Map.of()); + + String rendered = workspace.model().getAllTypes().stream() + .filter(type -> "Service".equals(type.getSimpleName())) + .findFirst() + .orElseThrow() + .toString(); + assertTrue(rendered.contains("executeRole(\"y\", null)"), rendered); + } + + @Test + void missingArgumentReusesUniqueImportedParametersForUnqualifiedStableTypes() + throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("imported-argument")); + javaSource( + sources, + "sample", + "Service", + "import java.util.Date; import java.util.List; " + + "class Service { void target(String value) {} " + + "void imported(Date date, List values) { target(\"x\"); } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + services + .repairs() + .registerMethodRewrite( + StableElementKey.method("Service", "target", List.of("String")), + StableElementKey.method( + "Service", "execute", List.of("String", "Date", "List"))); + + services.repairs().prepareForPrint(Map.of()); + + String rendered = workspace.model().getAllTypes().stream() + .filter(type -> "Service".equals(type.getSimpleName())) + .findFirst() + .orElseThrow() + .toString(); + assertTrue(rendered.contains("execute(\"x\", date, values)"), rendered); + } + + @Test + void groupingRunsBeforeOwnerAwareExecutableRepair() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("grouped-owner")); + javaSource( + sources, + "sample", + "ConcreteService", + "class ConcreteService { void target(String value) {} } " + + "class Caller { ConcreteService service; " + + "void call(String suffix) { service.target(\"x\"); } }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + services.transformations().setGroupingMappings(Map.of("ConcreteService", "ServiceGroup")); + services + .repairs() + .registerMethodRewrite( + StableElementKey.method("ReferenceService", "target", List.of("String")), + StableElementKey.method( + "ConcreteService", "execute", List.of("String", "String"))); + + services.transformations().prepareForPrint(); + + String caller = workspace.model().getAllTypes().stream() + .filter(type -> "Caller".equals(type.getSimpleName())) + .findFirst() + .orElseThrow() + .toString(); + assertTrue(caller.contains("ServiceGroup service"), caller); + assertTrue(caller.contains("service.execute(\"x\", suffix)"), caller); + } + + @Test + void constructorCallsFollowTypeReferenceRewrites() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("constructor-type")); + javaSource( + sources, + "sample", + "Factory", + "class Factory { Role create() { return new Role(); } }"); + Path cd = temporaryDirectory.resolve("ConstructorReference.cd"); + Files.writeString(cd, "classdiagram Reference { class Role; }"); + ASTCDType role = JavaLoader.parseCD(cd.toString()).getCDDefinition().getCDClassesList().get(0); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + + services.transformations().updateCDType(role, "HiwiRole"); + + String rendered = workspace.model().getAllTypes().iterator().next().toString(); + assertTrue(rendered.contains("HiwiRole create()"), rendered); + assertTrue(rendered.contains("new HiwiRole()"), rendered); + } + + @Test + void executablePreparationRemovesOnlyIllegalInterfaceBodies() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("interface-methods")); + javaSource( + sources, + "sample", + "Contract", + "interface Contract { void run(); default void keep() {} static void utility() {} }"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + var contract = workspace.model().getAllTypes().iterator().next(); + contract.getMethodsByName("run").get(0).setBody(workspace.factory().createBlock()); + + services.repairs().prepareForPrint(Map.of()); + + assertNull(contract.getMethodsByName("run").get(0).getBody()); + assertNotNull(contract.getMethodsByName("keep").get(0).getBody()); + assertNotNull(contract.getMethodsByName("utility").get(0).getBody()); + } + + @Test + void associationRoleRewriteDoesNotChangeAFieldOnAnotherReceiver() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("association-receiver")); + Path ownerSource = + javaSource( + sources, + "sample", + "Owner", + """ + class Other { + int children; + } + class Owner { + int children; + int read(Other other) { + return this.children + other.children; + } + } + """); + ASTTypeDeclaration owner = loadType(ownerSource, "Owner"); + SpoonUpdater updater = updater(sources); + + updater.updateAssociationRole(owner, "children", "kids"); + String generated = print(updater, "association-output", "Owner.java"); + + assertTrue(generated.contains("this.kids"), generated); + assertTrue(generated.contains("other.children"), generated); + assertFalse(generated.contains("other.kids"), generated); + } + + @Test + void executableRepairDistinguishesQualifiedOwnersWithTheSameSimpleName() + throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("qualified-owners")); + Path alphaService = + javaSource( + sources, + "alpha", + "Service", + "public class Service { public void run(String value) {} }"); + javaSource( + sources, + "beta", + "Service", + "public class Service { public void run(String value) {} }"); + javaSource( + sources, + "sample", + "Caller", + """ + class Caller { + alpha.Service alphaService; + beta.Service betaService; + void call() { + alphaService.run("alpha"); + betaService.run("beta"); + } + } + """); + ASTTypeDeclaration service = loadType(alphaService, "Service"); + ASTMethodDeclaration run = loadMethod(alphaService, "Service", "run"); + SpoonUpdater updater = updater(sources); + + updater.updateMethod(service, run, "execute"); + updater.registerMethodRewrite( + service, + StableElementKey.method("alpha.Service", "run", List.of("String")), + StableElementKey.method("alpha.Service", "execute", List.of("String"))); + String caller = print(updater, "qualified-owner-output", "Caller.java"); + + assertTrue(caller.contains("alphaService.execute(\"alpha\")"), caller); + assertTrue(caller.contains("betaService.run(\"beta\")"), caller); + assertFalse(caller.contains("betaService.execute(\"beta\")"), caller); + } + + @Test + void unqualifiedExecutableOwnerUsesTheQualifiedReceiverBeforeRewrite() throws IOException { + Path sources = + Files.createDirectory(temporaryDirectory.resolve("unqualified-executable-owner")); + Path alphaService = + javaSource( + sources, + "alpha", + "Service", + "public class Service { public void run(String value) {} }"); + javaSource( + sources, + "beta", + "Service", + "public class Service { public void run(String value) {} }"); + javaSource( + sources, + "sample", + "Caller", + """ + class Caller { + alpha.Service alphaService; + beta.Service betaService; + void call() { + alphaService.run("alpha"); + betaService.run("beta"); + } + } + """); + ASTTypeDeclaration service = loadType(alphaService, "Service"); + ASTMethodDeclaration run = loadMethod(alphaService, "Service", "run"); + SpoonWorkspace workspace = loadedWorkspace(sources); + Services services = services(workspace); + + services.transformations().updateMethod(service, run, "execute"); + services + .repairs() + .registerMethodRewrite( + StableElementKey.method("alpha.Service", "run", List.of("String")), + StableElementKey.method("alpha.Service", "execute", List.of("String"))); + for (CtInvocation invocation : + workspace.model().getElements(new TypeFilter<>(CtInvocation.class))) { + if ("run".equals(invocation.getExecutable().getSimpleName()) + || "execute".equals(invocation.getExecutable().getSimpleName())) { + invocation + .getExecutable() + .setDeclaringType(invocation.getFactory().Type().createReference("Service")); + } + } + services.transformations().prepareForPrint(); + String caller = print(workspace, "unqualified-owner-output", "Caller.java"); + + assertTrue(caller.contains("alphaService.execute(\"alpha\")"), caller); + assertTrue(caller.contains("betaService.run(\"beta\")"), caller); + assertFalse(caller.contains("betaService.execute(\"beta\")"), caller); + } + + @Test + void superinterfaceDeduplicationUsesQualifiedIdentity() throws IOException { + Path sources = Files.createDirectory(temporaryDirectory.resolve("qualified-interfaces")); + javaSource(sources, "alpha", "Tag", "public interface Tag {}"); + javaSource(sources, "beta", "Tag", "public interface Tag {}"); + Path implementation = + javaSource( + sources, + "sample", + "Implementation", + "class Implementation implements alpha.Tag {}"); + ASTTypeDeclaration type = loadType(implementation, "Implementation"); + SpoonUpdater updater = updater(sources); + + updater.addSuperType(type, "beta.Tag", true); + String generated = print(updater, "qualified-interface-output", "Implementation.java"); + + assertTrue(generated.contains("alpha.Tag"), generated); + assertTrue(generated.contains("beta.Tag"), generated); + } + + private Path sourceDirectory(String directoryName, String typeName) throws IOException { + Path directory = Files.createDirectory(temporaryDirectory.resolve(directoryName)); + Files.writeString( + directory.resolve(typeName + ".java"), + "class " + typeName + " {}", + StandardCharsets.UTF_8); + return directory; + } + + private SpoonUpdater updater(Path sources) { + SpoonUpdater updater = new SpoonUpdater(); + updater.setCodePath(sources); + return updater; + } + + private SpoonWorkspace loadedWorkspace(Path sources) throws IOException { + SpoonWorkspace workspace = new SpoonWorkspace(); + workspace.load(sources); + return workspace; + } + + private String print(SpoonUpdater updater, String outputName, String fileName) + throws IOException { + Path output = temporaryDirectory.resolve(outputName); + updater.setOutputDirectory(output); + updater.printCode(); + return generatedSource(output, fileName); + } + + private String print(SpoonWorkspace workspace, String outputName, String fileName) + throws IOException { + Path output = temporaryDirectory.resolve(outputName); + workspace.setOutputDirectory(output); + workspace.print(); + return generatedSource(output, fileName); + } + + private String generatedSource(Path output, String fileName) throws IOException { + try (var files = Files.walk(output)) { + Path generated = + files + .filter(Files::isRegularFile) + .filter(path -> fileName.equals(path.getFileName().toString())) + .findFirst() + .orElseThrow(); + return Files.readString(generated, StandardCharsets.UTF_8); + } + } + + private Path javaSource( + Path sourceRoot, String packageName, String typeName, String declaration) throws IOException { + Path directory = Files.createDirectories(sourceRoot.resolve(packageName.replace('.', '/'))); + Path source = directory.resolve(typeName + ".java"); + Files.writeString( + source, + "package " + packageName + "; " + declaration, + StandardCharsets.UTF_8); + return source; + } + + private ASTTypeDeclaration loadType(Path source, String typeName) { + JavaAstElemCollector collector = collect(source); + return collector.getAllTypeDeclarations().stream() + .filter(type -> typeName.equals(type.getName())) + .findFirst() + .orElseThrow(); + } + + private ASTMethodDeclaration loadMethod(Path source, String typeName, String methodName) { + JavaAstElemCollector collector = collect(source); + ASTTypeDeclaration type = + collector.getAllTypeDeclarations().stream() + .filter(candidate -> typeName.equals(candidate.getName())) + .findFirst() + .orElseThrow(); + return collector.getAllMethodDeclarations(type).stream() + .filter(method -> methodName.equals(method.getName())) + .findFirst() + .orElseThrow(); + } + + private ASTMethodDeclaration loadMethod( + Path source, String typeName, String methodName, int parameterCount) { + JavaAstElemCollector collector = collect(source); + ASTTypeDeclaration type = + collector.getAllTypeDeclarations().stream() + .filter(candidate -> typeName.equals(candidate.getName())) + .findFirst() + .orElseThrow(); + return collector.getAllMethodDeclarations(type).stream() + .filter(method -> methodName.equals(method.getName())) + .filter(method -> collector.getAllParameters(type, method).size() == parameterCount) + .findFirst() + .orElseThrow(); + } + + private Services services(SpoonWorkspace workspace) { + SpoonElementResolver resolver = new SpoonElementResolver(workspace::model); + SpoonExecutableRepairService repairs = new SpoonExecutableRepairService(workspace, resolver); + return new Services( + repairs, new SpoonTransformationService(workspace, resolver, repairs)); + } + + private record Services( + SpoonExecutableRepairService repairs, SpoonTransformationService transformations) {} + + private JavaAstElemCollector collect(Path source) { + ASTOrdinaryCompilationUnit ast = JavaLoader.loadJava(source.toFile()); + JavaAstElemCollector collector = new JavaAstElemCollector(); + JavaDSLTraverser traverser = JavaDSLMill.traverser(); + traverser.add4JavaDSL(collector); + ast.accept(traverser); + return collector; + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/AdaptReferenceTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/AdaptReferenceTest.java new file mode 100644 index 00000000..eacfafb4 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/AdaptReferenceTest.java @@ -0,0 +1,66 @@ +package de.monticore.codeAdaption.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.java.javadsl.JavaDSLMill; +import de.se_rwth.commons.logging.LogStub; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class AdaptReferenceTest { + + @BeforeEach + void setUp() { + JavaDSLMill.init(); + JavaDSLMill.globalScope().clear(); + LogStub.init(); + LogStub.enableFailQuick(false); + } + + @Test + void parsesQualifiedOwnerWithoutConfusingParameterTypePackages() { + AdaptReference reference = + AdaptReference.parse("model.Order.update(java.time.Instant)").orElseThrow(); + + assertEquals("model.Order", reference.owner().orElseThrow()); + assertEquals("update", reference.memberName()); + assertEquals("update(Instant)", reference.methodSignature().orElseThrow()); + } + + @Test + void normalizesNestedGenericMethodParametersThroughJavaDsl() { + AdaptReference reference = + AdaptReference.parse("Outer.Inner.update(Map>)").orElseThrow(); + + assertEquals("Outer.Inner", reference.owner().orElseThrow()); + assertEquals("update(Map>)", reference.memberReference()); + } + + @Test + void distinguishesFieldsAndMethods() { + AdaptReference field = AdaptReference.parse("model.Order.number").orElseThrow(); + AdaptReference method = AdaptReference.parse("update()").orElseThrow(); + + assertFalse(field.isMethod()); + assertEquals("number", field.memberName()); + assertTrue(method.isMethod()); + assertTrue(method.owner().isEmpty()); + } + + @Test + void rejectsMalformedReferences() { + assertTrue(AdaptReference.parse("malformed(reference").isEmpty()); + assertTrue(AdaptReference.parse(".field").isEmpty()); + assertTrue(AdaptReference.parse("Owner.").isEmpty()); + } + + @Test + void annotationNamesRequireAnExactMatch() { + assertTrue(AdaptAnnotationNames.matches("Adapt")); + assertTrue(AdaptAnnotationNames.matches("de.monticore.codeAdaption.utils.Adapt")); + assertFalse(AdaptAnnotationNames.matches("NotAdapt")); + assertFalse(AdaptAnnotationNames.matches("example.NotAdapt")); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/CDImportProjectorTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/CDImportProjectorTest.java new file mode 100644 index 00000000..3f1cc5ba --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/CDImportProjectorTest.java @@ -0,0 +1,195 @@ +package de.monticore.codeAdaption.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.cdbasis._ast.ASTCDCompilationUnit; +import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class CDImportProjectorTest extends AdapterAbstractTest { + + @TempDir Path tempDir; + + @BeforeEach + void setUp() { + initMills(); + } + + @Test + void projectsOnlyExplicitImportsUsedByTheJavaUnit() throws IOException { + ASTCDCompilationUnit reference = + cd( + "Reference", + """ + import alpha.types.Widget; + import alpha.time.Date; + """); + ASTCDCompilationUnit concrete = + cd( + "Concrete", + """ + import beta.time.Date; + import beta.types.Unused; + """); + ASTOrdinaryCompilationUnit unit = java("Holder", "class Holder { Widget value; }"); + + CDImportProjector.project(Set.of(unit), reference, concrete); + + assertEquals(List.of("alpha.types.Widget"), imports(unit)); + } + + @Test + void preservesAnExistingJavaBindingOnCdCollision() throws IOException { + ASTCDCompilationUnit reference = cd("Reference", "import org.joda.time.Date;"); + ASTOrdinaryCompilationUnit unit = + java( + "Holder", + """ + import java.util.Date; + class Holder { Date value; } + """); + + CDImportProjector.project(Set.of(unit), reference); + + assertEquals(List.of("java.util.Date"), imports(unit)); + } + + @Test + void rejectsUsedReferenceConcreteImportCollisionDeterministically() throws IOException { + ASTCDCompilationUnit reference = cd("Reference", "import z.reference.Date;"); + ASTCDCompilationUnit concrete = cd("Concrete", "import a.concrete.Date;"); + ASTOrdinaryCompilationUnit unit = java("Holder", "class Holder { Date value; }"); + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, + () -> CDImportProjector.project(Set.of(unit), reference, concrete)); + + assertTrue(exception.getMessage().contains("a.concrete.Date, z.reference.Date")); + assertEquals(List.of(), imports(unit)); + } + + @Test + void deduplicatesTheSameExplicitImportAcrossJavaAndCds() throws IOException { + ASTCDCompilationUnit reference = cd("Reference", "import java.util.List;"); + ASTCDCompilationUnit concrete = cd("Concrete", "import java.util.List;"); + ASTOrdinaryCompilationUnit unit = + java( + "Holder", + """ + import java.util.List; + class Holder { List values; } + """); + + CDImportProjector.project(Set.of(unit), reference, concrete); + + assertEquals(List.of("java.util.List"), imports(unit)); + } + + @Test + void preservesAndDeduplicatesCdWildcardImports() throws IOException { + ASTCDCompilationUnit reference = cd("Reference", "import java.util.*;"); + ASTCDCompilationUnit concrete = cd("Concrete", "import java.util.*;"); + ASTOrdinaryCompilationUnit unit = java("Holder", "class Holder {}"); + + CDImportProjector.project(Set.of(unit), reference, concrete); + + assertEquals(List.of("java.util.*"), imports(unit)); + assertTrue(unit.getImportDeclarationList().get(0).isSTAR()); + } + + @Test + void projectsJavaLangSubpackageAndNestedTypeImports() throws IOException { + ASTCDCompilationUnit cd = + cd( + "JavaLangImports", + """ + import java.lang.reflect.Method; + import java.lang.Thread.State; + """); + ASTOrdinaryCompilationUnit unit = + java( + "Holder", + """ + class Holder { + Method method; + State state; + } + """); + + CDImportProjector.project(Set.of(unit), cd); + + assertEquals(List.of("java.lang.reflect.Method", "java.lang.Thread.State"), imports(unit)); + } + + @Test + void ignoresTypeNamesMentionedOnlyInStringsAndComments() throws IOException { + ASTCDCompilationUnit reference = cd("ReferenceImports", "import alpha.Customer;"); + ASTCDCompilationUnit concrete = cd("ConcreteImports", "import beta.Customer;"); + ASTOrdinaryCompilationUnit unit = + java( + "Message", + """ + class Message { + // Customer is only documentation, not a type use. + String text = "Customer"; + } + """); + + CDImportProjector.project(Set.of(unit), reference, concrete); + + assertEquals(List.of(), imports(unit)); + } + + @Test + void ignoresNonTypeIdentifiersThatMatchImportedTypeNames() throws IOException { + ASTCDCompilationUnit reference = + cd("ReferenceIdentifierImports", "import alpha.Customer;"); + ASTCDCompilationUnit concrete = + cd("ConcreteIdentifierImports", "import beta.Customer;"); + ASTOrdinaryCompilationUnit unit = + java( + "Identifier", + """ + class Identifier { + int Customer; + void Customer() {} + } + """); + + CDImportProjector.project(Set.of(unit), reference, concrete); + + assertEquals(List.of(), imports(unit)); + } + + private ASTCDCompilationUnit cd(String name, String imports) throws IOException { + Path file = tempDir.resolve(name + ".cd"); + Files.writeString(file, imports + System.lineSeparator() + "classdiagram " + name + " {}"); + return JavaLoader.parseCD(file.toString()); + } + + private ASTOrdinaryCompilationUnit java(String name, String source) throws IOException { + Path file = tempDir.resolve(name + ".java"); + Files.writeString(file, source); + return JavaLoader.loadJava(file.toFile()); + } + + private static List imports(ASTOrdinaryCompilationUnit unit) { + return unit.getImportDeclarationList().stream() + .map( + importDeclaration -> + importDeclaration.getMCQualifiedName().getQName() + + (importDeclaration.isSTAR() ? ".*" : "")) + .toList(); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaAstElemCollectorTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaAstElemCollectorTest.java index 64590171..5003eddc 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaAstElemCollectorTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaAstElemCollectorTest.java @@ -34,7 +34,7 @@ public void testTypeOperationCollection() { ast.accept(traverser); // check a type collection - Assertions.assertEquals(collector.getAllTypeDeclarations().size(), 1); + Assertions.assertEquals(1, collector.getAllTypeDeclarations().size()); // check type element collection ASTTypeDeclaration t = collector.getAllTypeDeclarations().get(0); diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaSourceNamesTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaSourceNamesTest.java new file mode 100644 index 00000000..43cef09c --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaSourceNamesTest.java @@ -0,0 +1,91 @@ +package de.monticore.codeAdaption.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import de.se_rwth.commons.logging.LogStub; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class JavaSourceNamesTest { + + @BeforeEach + void setUp() { + LogStub.init(); + LogStub.enableFailQuick(false); + } + + @Test + void normalizesQualifiedGenericArrayAndAnyTypes() { + assertEquals( + "Map>", + JavaSourceNames.normalizeType("java.util.Map>")); + assertEquals("Object", JavaSourceNames.normalizeType("any")); + assertEquals("String[]", JavaSourceNames.normalizeType("java.lang.String []")); + assertEquals("int[]", JavaSourceNames.normalizeType("int[]")); + assertEquals("void", JavaSourceNames.normalizeType("void")); + assertEquals("Optional", JavaSourceNames.normalizeType("Optional")); + assertEquals("List", JavaSourceNames.normalizeType("List")); + assertEquals( + "List", + JavaSourceNames.normalizeType("java.util.List")); + } + + @Test + void replacesSimpleTypeNamesInsideNestedGenericTypes() { + String replaced = + JavaSourceNames.replaceSimpleTypeNames( + "Map>", + simple -> "Person".equals(simple) ? Optional.of("Named") : Optional.empty()); + + assertEquals("Map>", replaced); + } + + @Test + void preservesQualifiedNamesWhenReplacingParsedTypeLeaves() { + String replaced = + JavaSourceNames.replaceSimpleTypeNames( + "java.util.Map>", + simple -> "Person".equals(simple) ? Optional.of("Named") : Optional.empty()); + + assertEquals("java.util.Map>", replaced); + } + + @Test + void extractsQualifiedIdentityFromNestedGenericAndWildcardTypes() { + assertEquals( + List.of( + new JavaSourceNames.TypeReferenceName("java.util.Map", "Map", true), + new JavaSourceNames.TypeReferenceName("a.User", "User", true), + new JavaSourceNames.TypeReferenceName("java.util.List", "List", true), + new JavaSourceNames.TypeReferenceName("b.User", "User", true)), + JavaSourceNames.typeReferences( + "java.util.Map>")); + } + + @Test + void extractsSimpleNameThroughTypeParserWhenPossible() { + assertEquals("Entry", JavaSourceNames.simpleName("java.util.Map.Entry")); + assertEquals("List", JavaSourceNames.simpleName("java.util.List[]")); + assertEquals("update", JavaSourceNames.simpleName("update()")); + } + + @Test + void normalizesMethodParametersThroughTheTypeParser() { + assertEquals( + Optional.of("update(Map>,int[],long)"), + JavaMethodSignatures.parseNormalized( + " update(java.util.Map>, int[], long) ")); + assertEquals(Optional.of("update()"), JavaMethodSignatures.parseNormalized(" update( ) ")); + } + + @Test + void rejectsMalformedMethodSignatures() { + assertEquals( + Optional.empty(), + JavaMethodSignatures.parseNormalized("update(Map,int)")); + assertEquals( + Optional.empty(), JavaMethodSignatures.parseNormalized("update(String) trailing")); + } +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaSourcePostProcessorTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaSourcePostProcessorTest.java new file mode 100644 index 00000000..37075cd3 --- /dev/null +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/utils/JavaSourcePostProcessorTest.java @@ -0,0 +1,239 @@ +package de.monticore.codeAdaption.utils; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class JavaSourcePostProcessorTest { + + @TempDir Path tempDir; + + @Test + void preservesCommentsAndLiteralsWhileRemovingInvalidImports() { + String source = + """ + package demo; + import Person; + import de.monticore.codeAdaption.utils.Adapt; + import java.util.HashMap; + + public class Example { + String url = "http://example/a,b"; + String block = "/* not a comment */"; + java.util.Map map = new HashMap<>(); + + void m() { + int x=1; // keep comma,a and equals= + /* keep generic List text */ + } + } + """; + + String cleaned = JavaSourcePostProcessor.process(source); + + assertFalse(cleaned.contains("import Person;")); + assertFalse(cleaned.contains("import de.monticore.codeAdaption.utils.Adapt;")); + assertTrue(cleaned.contains("import java.util.HashMap;")); + assertTrue(cleaned.contains("\"http://example/a,b\"")); + assertTrue(cleaned.contains("int x=1; // keep comma,a and equals=")); + assertTrue(cleaned.contains("/* keep generic List text */")); + } + + @Test + void removesInvalidImportsAndPreservesExistingValidImportsWithoutAddingJdkImports() { + String source = + """ + package demo; + + // List in a comment must not drive imports by itself. + import de.monticore.codeAdaption.utils.Adapt; + import Person; + import static java.util.Collections.emptyList; + import java.io.File; + + public class Sample { + private List names; + private Map> ids; + private ZonedDateTime created; + private File file; + private String literal = "Set"; + } + """; + + String cleaned = JavaSourcePostProcessor.process(source, "Sample.java"); + + assertFalse(cleaned.contains("import de.monticore.codeAdaption.utils.Adapt;")); + assertFalse(cleaned.contains("import Person;")); + assertTrue(cleaned.contains("import static java.util.Collections.emptyList;")); + assertTrue(cleaned.contains("import java.io.File;")); + assertFalse(cleaned.contains("import java.util.List;")); + assertFalse(cleaned.contains("import java.util.Map;")); + assertFalse(cleaned.contains("import java.util.Optional;")); + assertFalse(cleaned.contains("import java.util.UUID;")); + assertFalse(cleaned.contains("import java.time.ZonedDateTime;")); + assertFalse(cleaned.contains("import java.util.Set;")); + } + + @Test + void preservesExistingJavaUtilWildcardWithoutAddingExplicitImports() { + String source = + """ + package demo; + + import java.util.*; + + public class Sample { + private List names; + } + """; + + String cleaned = JavaSourcePostProcessor.process(source, "Sample.java"); + + assertTrue(cleaned.contains("import java.util.*;")); + assertFalse(cleaned.contains("import java.util.List;")); + } + + @Test + void doesNotInventImportsForUnresolvedSimpleNames() { + String source = + """ + package demo; + + class List {} + + public class Sample { + private List names; + private Optional value; + } + """; + + String cleaned = JavaSourcePostProcessor.process(source, "Sample.java"); + + assertFalse(cleaned.contains("import java.util.List;")); + assertFalse(cleaned.contains("import java.util.Optional;")); + } + + @Test + void processDirectoryDoesNotInventImportsForSamePackageTypes() throws IOException { + Path list = tempDir.resolve("List.java"); + Path sample = tempDir.resolve("Sample.java"); + Files.writeString( + list, + """ + package demo; + + public class List {} + """, + StandardCharsets.UTF_8); + Files.writeString( + sample, + """ + package demo; + + public class Sample { + private List names; + private Map values; + } + """, + StandardCharsets.UTF_8); + + JavaSourcePostProcessor.processDirectory(tempDir); + String cleaned = Files.readString(sample, StandardCharsets.UTF_8); + + assertFalse(cleaned.contains("import java.util.List;")); + assertFalse(cleaned.contains("import java.util.Map;")); + } + + @Test + void removesOnlyMalformedParameterizedImportLines() { + String source = + """ + package demo; + + import Optional; + import static java.util.Collections.emptyList; + + public class Sample { + private Optional value; + private List numbers; + } + """; + + String cleaned = JavaSourcePostProcessor.process(source, "Sample.java"); + + assertFalse(cleaned.contains("import Optional;")); + assertTrue(cleaned.contains("import static java.util.Collections.emptyList;")); + assertFalse(cleaned.contains("import java.util.Optional;")); + assertFalse(cleaned.contains("import java.util.List;")); + assertTrue(cleaned.contains("Optional value")); + assertTrue(cleaned.contains("List numbers")); + } + + @Test + void malformedImportRecoveryPreservesCommentsAndTextBlocks() { + String source = + String.join( + System.lineSeparator(), + "package demo;", + "", + "/*", + " import Commented;", + "*/", + "import Optional;", + "", + "public class Sample {", + " String text = \"\"\"", + " import TextBlock;", + " \"\"\";", + " String literal = \"import Literal;\";", + "}", + ""); + + String cleaned = JavaSourcePostProcessor.process(source, "Sample.java"); + + assertFalse(cleaned.contains("import Optional;")); + assertTrue(cleaned.contains("import Commented;")); + assertTrue(cleaned.contains("import TextBlock;")); + assertTrue(cleaned.contains("\"import Literal;\"")); + } + + @Test + void printAstPreservesPackageDirectoriesForSameSimpleNames() throws IOException { + Path inputDir = tempDir.resolve("input"); + Path outputDir = tempDir.resolve("output"); + Files.createDirectories(inputDir.resolve("a")); + Files.createDirectories(inputDir.resolve("b")); + Files.writeString( + inputDir.resolve("a").resolve("User.java"), + """ + package a; + + public class User {} + """, + StandardCharsets.UTF_8); + Files.writeString( + inputDir.resolve("b").resolve("User.java"), + """ + package b; + + public class User {} + """, + StandardCharsets.UTF_8); + + Set asts = JavaLoader.readJavaCode(inputDir); + JavaLoader.printAST(asts, outputDir); + + assertTrue(Files.exists(outputDir.resolve("a").resolve("User.java"))); + assertTrue(Files.exists(outputDir.resolve("b").resolve("User.java"))); + assertFalse(Files.exists(outputDir.resolve("User.java"))); + } + +} diff --git a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/validator/CodeValidatorTest.java b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/validator/CodeValidatorTest.java index c390be48..661cda1e 100644 --- a/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/validator/CodeValidatorTest.java +++ b/ref-code-adaptation/src/test/java/de/monticore/codeAdaption/validator/CodeValidatorTest.java @@ -4,20 +4,31 @@ import de.monticore.cdbasis._ast.ASTCDCompilationUnit; import de.monticore.codeAdaption.AdapterAbstractTest; +import de.monticore.codeAdaption.matcher.errorMatcher.ErrorTypeMatcher; import de.monticore.codeAdaption.utils.AdapterParam; import de.monticore.codeAdaption.utils.JavaLoader; +import de.monticore.codeAdaption.utils.visitors.JavaAstElemCollector; +import de.monticore.java.javadsl.JavaDSLMill; import de.monticore.java.javadsl._ast.ASTOrdinaryCompilationUnit; +import de.monticore.java.javadsl._visitor.JavaDSLTraverser; import de.se_rwth.commons.logging.Log; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Set; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; class CodeValidatorTest extends AdapterAbstractTest { + @TempDir Path tempDir; + protected String baseDir = "src/test/resources/de/monticore/codeAdaption/validator/"; protected CodeValidator validator; protected ASTCDCompilationUnit cd; @@ -41,31 +52,112 @@ public static Stream invalidFiles1() { Arguments.of("MissingTemplateArgument.java", "0xRC001")); } - public static Stream invalidFiles2() { - return Stream.of(Arguments.of("InvalidTemplate.java", "0xRC003")); - } - @ParameterizedTest @MethodSource("invalidFiles1") public void checkAnnotationPhase1(String filename, String errorCode) { String fileName = baseDir + "invalid/" + filename; ASTOrdinaryCompilationUnit ast = JavaLoader.loadJava(new File(fileName)); - validator.runCoCosPhase1(ast, cd); + validator.runAdapterCoCos(ast, cd); Assertions.assertEquals(1, Log.getErrorCount()); Assertions.assertTrue(Log.getFindings().get(0).getMsg().startsWith(errorCode)); } - @ParameterizedTest - @MethodSource("invalidFiles2") - public void checkAnnotationPhase2(String filename, String errorCode) { + @Test + void literalDollarDoesNotCountAsTemplatePlaceholder() throws IOException { + ASTOrdinaryCompilationUnit ast = + javaSource( + "LiteralDollar.java", + """ + import de.monticore.codeAdaption.utils.Adapt; + @Adapt(ref = "Entity", template = "Price$${}", genTemplate = "Generated$${}") + class LiteralDollar {} + """); - String fileName = baseDir + "invalid/" + filename; - ASTOrdinaryCompilationUnit ast = JavaLoader.loadJava(new File(fileName)); - validator.runCoCosPhase2(ast, cd); + validator.runAdapterCoCos(ast, cd); + + Assertions.assertEquals(0, Log.getErrorCount()); + } + + @Test + void generatedTemplateMustMatchReferenceArity() throws IOException { + ASTOrdinaryCompilationUnit ast = + javaSource( + "InvalidGeneratedTemplate.java", + """ + import de.monticore.codeAdaption.utils.Adapt; + @Adapt(ref = "Entity", template = "${}", genTemplate = "${}${}") + class InvalidGeneratedTemplate {} + """); + + validator.runAdapterCoCos(ast, cd); + + Assertions.assertEquals(1, Log.getErrorCount()); + Assertions.assertTrue(Log.getFindings().get(0).getMsg().startsWith("0xRC001")); + } - Assertions.assertEquals(5, Log.getErrorCount()); - Log.getFindings().forEach(f -> Assertions.assertTrue(f.getMsg().startsWith(errorCode))); + @Test + void validationInitializesSourceTypesBeforeCheckingSupertypes() throws IOException { + Path sources = Files.createDirectory(tempDir.resolve("source-local-supertype")); + Files.writeString(sources.resolve("Base.java"), "class Base {}"); + Files.writeString(sources.resolve("Child.java"), "class Child extends Base {}"); + CodeValidator strictMembers = + new CodeValidator(cd, Set.of(IGNORE_NON_MATCHED_TYPE, IGNORE_NON_MATCHED_VAR)); + + Assertions.assertTrue(strictMembers.isValid(cd, sources)); + } + + @Test + void validationRejectsUnmatchedExternalSupertype() throws IOException { + Path sources = Files.createDirectory(tempDir.resolve("external-supertype")); + Files.writeString(sources.resolve("Child.java"), "class Child extends ExternalBase {}"); + CodeValidator strictMembers = + new CodeValidator(cd, Set.of(IGNORE_NON_MATCHED_TYPE, IGNORE_NON_MATCHED_VAR)); + + Assertions.assertFalse(strictMembers.isValid(cd, sources)); + } + + @Test + void validationCanIgnoreUnmatchedExternalSupertype() throws IOException { + Path sources = Files.createDirectory(tempDir.resolve("ignored-supertype")); + Files.writeString(sources.resolve("Child.java"), "class Child extends ExternalBase {}"); + CodeValidator ignoredMembers = + new CodeValidator( + cd, + Set.of( + IGNORE_NON_MATCHED_TYPE, + IGNORE_NON_MATCHED_TYPE_MEMBER, + IGNORE_NON_MATCHED_VAR)); + + Assertions.assertTrue(ignoredMembers.isValid(cd, sources)); } + + @Test + void unrelatedSameNamedSourceTypeDoesNotSatisfyAnImportedExternalSupertype() + throws IOException { + Path sources = Files.createDirectory(tempDir.resolve("validator-owner")); + Path unrelated = Files.createDirectories(sources.resolve("unrelated")); + Path consumer = Files.createDirectories(sources.resolve("consumer")); + Files.writeString(unrelated.resolve("Base.java"), "package unrelated; class Base {}"); + Files.writeString( + consumer.resolve("Child.java"), + "package consumer; import external.Base; class Child extends Base {}"); + Path modelFile = tempDir.resolve("ExternalSupertype.cd"); + Files.writeString(modelFile, "classdiagram ExternalSupertype {}"); + ASTCDCompilationUnit model = JavaLoader.parseCD(modelFile.toString()); + CodeValidator strictMembers = + new CodeValidator(model, Set.of(IGNORE_NON_MATCHED_TYPE, IGNORE_NON_MATCHED_VAR)); + + Assertions.assertFalse( + strictMembers.isValid(model, sources), + "An unrelated source package must not satisfy the imported external supertype"); + } + + private ASTOrdinaryCompilationUnit javaSource(String fileName, String source) throws IOException { + Path file = tempDir.resolve(fileName); + Files.writeString(file, source); + return JavaLoader.loadJava(file.toFile()); + } + } diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/App.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/App.cd index 8ed23820..f568ddfb 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/App.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/App.cd @@ -1,4 +1,4 @@ -import java.lang.String ; +import java.lang.String; classdiagram App { class Application; diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/UniApp.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/UniApp.cd index 85f72c87..d827c3b0 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/UniApp.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/UniApp.cd @@ -1,4 +1,4 @@ -import java.lang.String ; +import java.lang.String; classdiagram UniApp { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/adapter/name/Entity.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/adapter/name/Entity.java index 4c712628..73ec020f 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/adapter/name/Entity.java +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/adapter/name/Entity.java @@ -1,7 +1,4 @@ - - - - -public class Entity { +public class Entity { String id; -} + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationConc.cd new file mode 100644 index 00000000..34d3c1fc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationConc.cd @@ -0,0 +1,19 @@ +classdiagram EvaluationConc { + class Project { + } + class Staff { + int staffID; + } + <> class Administration { + } + <> class Trainee { + int supervisorID; + } + class Employee { + <> int employeeID; + } + association staffEmployment Staff -- Employee; + association Staff -- Project; + <> association traineeProgram + Administration (hostAdm) <- (trainee) Trainee [0..*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationOut.cd new file mode 100644 index 00000000..4b6f1501 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationOut.cd @@ -0,0 +1,31 @@ +import java.lang.String; + +classdiagram EvaluationConc { + class Project; + + class Staff { + int staffID; + } + + <>class Administration { + String name; + } + + <>class Trainee extends Person { + int supervisorID; + } + + class Employee extends Person { + <>int employeeID; + } + + class Person { + String name; + int age; + } + + association staffEmployment Staff(partOf)<->(member)Employee [1..*]; + association Staff--Project; + <>association traineeProgram Administration(hostAdm)<-(trainee)Trainee[0..*]; + association administrationAssignment [1]Staff<->Administration [1]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationRef.cd new file mode 100644 index 00000000..d560d8e4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/EvaluationRef.cd @@ -0,0 +1,22 @@ +import java.lang.String; + +classdiagram EvaluationRef { + class Person { + String name; + int age; + } + class Department { + String name; + } + class Employee extends Person { + int id; + } + class Intern extends Person { + int supervisorID; + } + class Staff { + } + association staffEmployment Staff (partOf) <-> (member) Employee [1..*]; + association departmentAssignment [1] Staff <-> Department [1]; + association internshipProgram Department -- (intern) Intern; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0001/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0001/Account.java new file mode 100644 index 00000000..3bd36114 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0001/Account.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationinsupertype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Account"}, template = "${}") +public class Account { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0002/Person.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0002/Person.java new file mode 100644 index 00000000..d4a0b550 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0002/Person.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationinsupertype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Person"}, template = "${}") +public class Person { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0003/A.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0003/A.java new file mode 100644 index 00000000..a97cd3a3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0003/A.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"A"}, template = "${}") +public class A { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0004/B.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0004/B.java new file mode 100644 index 00000000..7ebe4e66 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0004/B.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"B"}, template = "${}") +public class B { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0005/Department.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0005/Department.java new file mode 100644 index 00000000..bdd648ef --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0005/Department.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Department"}, template = "${}") +public class Department { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0006/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0006/Employee.java new file mode 100644 index 00000000..c0aaaf78 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0006/Employee.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0007/Project.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0007/Project.java new file mode 100644 index 00000000..667dcef8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0007/Project.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocnameimplicitnameadapt; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Project"}, template = "${}") +public class Project { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0008/Task.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0008/Task.java new file mode 100644 index 00000000..d2bbddbf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0008/Task.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocnameimplicitnameadapt; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Task"}, template = "${}") +public class Task { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0009/A.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0009/A.java new file mode 100644 index 00000000..8ea8119d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0009/A.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.associations; + +public class A { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0010/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0010/Builder.java new file mode 100644 index 00000000..b9931302 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0010/Builder.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattribute; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}") +public class Builder { + @Adapt(ref = {"Builder.attribute"}, template = "${}") + private Object attribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0011/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0011/DataClass.java new file mode 100644 index 00000000..3eb76ee1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0011/DataClass.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattribute; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0012/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0012/Employee.java new file mode 100644 index 00000000..e4c1d7a2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0012/Employee.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.foreach; + +public class Employee { + private String firstName; + + private String lastName; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0013/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0013/Employee.java new file mode 100644 index 00000000..d7df51c5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0013/Employee.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.valid.adapter.attributeindeepsuperclass; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee { + @Adapt(ref = {"Employee.firstName"}, template = "${}") + private String firstName; + + @Adapt(ref = {"Employee.lastName"}, template = "${}") + private String lastName; + + @Adapt(ref = {"Employee.number"}, template = "${}") + private int number; + + @Adapt(ref = {"Employee.salary"}, template = "${}") + private int salary; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0014/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0014/Employee.java new file mode 100644 index 00000000..457081cf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0014/Employee.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.valid.adapter.attributesmissing; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee { + @Adapt(ref = {"Employee.number"}, template = "${}") + private int number; + + @Adapt(ref = {"Employee.salary"}, template = "${}") + private int salary; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0015/Bank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0015/Bank.java new file mode 100644 index 00000000..fbfc5314 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0015/Bank.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Bank"}, template = "${}") +public class Bank { + @Adapt(ref = {"Bank.id"}, template = "${}") + private String id; + + @Adapt(ref = {"Bank.overallBalance"}, template = "${}") + private double overallBalance; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0016/BankAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0016/BankAccount.java new file mode 100644 index 00000000..b038d876 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0016/BankAccount.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.singleinc.concrete.banking; + +public class BankAccount { + private String iban; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0017/SEPABank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0017/SEPABank.java new file mode 100644 index 00000000..af1e9437 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0017/SEPABank.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.singleinc.concrete.banking; + +public class SEPABank { + private String bic; + + private double totalMoney; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0018/Department.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0018/Department.java new file mode 100644 index 00000000..af99f822 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0018/Department.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.builder; + +public class Department { + private String managerName; + + private int depId; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0019/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0019/Employee.java new file mode 100644 index 00000000..c0d610ba --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0019/Employee.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.builder; + +public class Employee { + private String firstName; + + private String lastName; + + private int number; + + private int salary; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0020/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0020/DataClass.java new file mode 100644 index 00000000..87587bea --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0020/DataClass.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.getter_setter.adapter.datamodel; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + + @Adapt(ref = {"DataClass.getAttribute"}, template = "${}") + public Object getAttribute() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0021/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0021/DataClass.java new file mode 100644 index 00000000..5a1be7f0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0021/DataClass.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies.adapter.transitivedependencies; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0022/DataClassBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0022/DataClassBuilder.java new file mode 100644 index 00000000..0ba1da24 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0022/DataClassBuilder.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies.adapter.transitivedependencies; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClassBuilder"}, template = "${}") +public class DataClassBuilder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0023/Person.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0023/Person.java new file mode 100644 index 00000000..20e3ec2d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0023/Person.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.attributeinheritance; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Person"}, template = "${}") +public class Person { + @Adapt(ref = {"Person.number"}, template = "${}") + private int number; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0024/Teacher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0024/Teacher.java new file mode 100644 index 00000000..2287be97 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0024/Teacher.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.attributeinheritance; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Teacher"}, template = "${}") +public class Teacher extends Person { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0025/Placeholder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0025/Placeholder.java new file mode 100644 index 00000000..dc2605c6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0025/Placeholder.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.classmissing; + +public class Placeholder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0026/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0026/Builder.java new file mode 100644 index 00000000..0fea0315 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0026/Builder.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachtypesamereturntype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}") +public class Builder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0027/BusinessAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0027/BusinessAccount.java new file mode 100644 index 00000000..5c725151 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0027/BusinessAccount.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; + +public class BusinessAccount { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0028/InstantTransaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0028/InstantTransaction.java new file mode 100644 index 00000000..709bb53b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0028/InstantTransaction.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; + +public class InstantTransaction { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0029/NormalTransaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0029/NormalTransaction.java new file mode 100644 index 00000000..9dcb7b5b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0029/NormalTransaction.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; + +public class NormalTransaction { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0030/PersonalAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0030/PersonalAccount.java new file mode 100644 index 00000000..5502c822 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/java/group-0030/PersonalAccount.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; + +public class PersonalAccount { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/shared-fixtures.tsv b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/shared-fixtures.tsv new file mode 100644 index 00000000..f8208c23 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/_shared/shared-fixtures.tsv @@ -0,0 +1,113 @@ +# case role shared output package +associations/AssociationInSuperTypeConc.cd adapter _shared/java/group-0001/Account.java Account.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationinsupertype; +methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd adapter _shared/java/group-0001/Account.java Account.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; +methods/multiIncarnation/ParameterTypeMIConc.cd adapter _shared/java/group-0001/Account.java Account.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; +methods/multiIncarnation/ParameterTypeMIOneExistsConc.cd adapter _shared/java/group-0001/Account.java Account.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; +methods/multiIncarnation/ReturnTypeMIConc.cd adapter _shared/java/group-0001/Account.java Account.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; +methods/multiIncarnation/ReturnTypeMIOneExistsConc.cd adapter _shared/java/group-0001/Account.java Account.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; +associations/AssociationInSuperTypeConc.cd adapter _shared/java/group-0002/Person.java Person.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationinsupertype; +inheritance/MissingInheritanceConc.cd adapter _shared/java/group-0002/Person.java Person.java package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.missinginheritance; +associations/AssociationMissingCardinalityConc.cd adapter _shared/java/group-0003/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; +associations/AssociationMissingSimpleConc.cd adapter _shared/java/group-0003/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingsimple; +associations/AssociationReverseMatchConc.cd adapter _shared/java/group-0003/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationreversematch; +associations/AssociationSuperMatchingConc.cd adapter _shared/java/group-0003/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationsupermatching; +associations/TypeMIOneAssocExistsConc.cd adapter _shared/java/group-0003/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.typemioneassocexists; +helper/HelperConc.cd adapter _shared/java/group-0003/A.java A.java package de.monticore.codeAdaption.cdconcretization.helper.adapter.helper; +associations/AssociationMissingCardinalityConc.cd adapter _shared/java/group-0004/B.java B.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; +associations/AssociationMissingSimpleConc.cd adapter _shared/java/group-0004/B.java B.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingsimple; +associations/AssociationReverseMatchConc.cd adapter _shared/java/group-0004/B.java B.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationreversematch; +associations/AssociationSuperMatchingConc.cd adapter _shared/java/group-0004/B.java B.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationsupermatching; +associations/TypeMIOneAssocExistsConc.cd adapter _shared/java/group-0004/B.java B.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.typemioneassocexists; +helper/HelperConc.cd adapter _shared/java/group-0004/B.java B.java package de.monticore.codeAdaption.cdconcretization.helper.adapter.helper; +associations/AssociationMissingCardinalityConc.cd adapter _shared/java/group-0005/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; +associations/AssociationMissingFinalConc.cd adapter _shared/java/group-0005/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingfinal; +associations/AssociationMissingRolenameConc.cd adapter _shared/java/group-0005/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingrolename; +associations/AssociationMissingSimpleConc.cd adapter _shared/java/group-0005/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingsimple; +associations/AssociationMultipleTypeIncarnationConc.cd adapter _shared/java/group-0005/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmultipletypeincarnation; +multipleIncarnation/BothAssocSidesMIConc.cd adapter _shared/java/group-0005/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.bothassocsidesmi; +associations/AssociationMissingCardinalityConc.cd adapter _shared/java/group-0006/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingcardinality; +associations/AssociationMissingFinalConc.cd adapter _shared/java/group-0006/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingfinal; +associations/AssociationMissingRolenameConc.cd adapter _shared/java/group-0006/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingrolename; +associations/AssociationMissingSimpleConc.cd adapter _shared/java/group-0006/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmissingsimple; +associations/AssocNameImplicitNameAdaptConc.cd adapter _shared/java/group-0007/Project.java Project.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocnameimplicitnameadapt; +associations/AssocRoleImplicitNameAdaptConc.cd adapter _shared/java/group-0007/Project.java Project.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocroleimplicitnameadapt; +associations/AssocNameImplicitNameAdaptConc.cd adapter _shared/java/group-0008/Task.java Task.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocnameimplicitnameadapt; +associations/AssocRoleImplicitNameAdaptConc.cd adapter _shared/java/group-0008/Task.java Task.java package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocroleimplicitnameadapt; +associations/AssociationInSuperTypeConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssociationMissingCardinalityConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssociationMissingFinalConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssociationMissingRolenameConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssociationMissingSimpleConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssociationMultipleTypeIncarnationConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssociationReverseMatchConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssociationSuperMatchingConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssocNameImplicitNameAdaptConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/AssocRoleImplicitNameAdaptConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +associations/TypeMIOneAssocExistsConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.associations; +helper/HelperConc.cd concrete _shared/java/group-0009/A.java A.java package de.monticore.codeAdaption.cdconcretization.helper; +attributes/forEach/ForEachAttributeConc.cd adapter _shared/java/group-0010/Builder.java Builder.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattribute; +attributes/forEach/ForEachAttributeNoTargetIncConc.cd adapter _shared/java/group-0010/Builder.java Builder.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattributenotargetinc; +types/forEach/ForEachTypeForEachAttributeConc.cd adapter _shared/java/group-0010/Builder.java Builder.java package de.monticore.codeAdaption.cdconcretization.types.foreach.adapter.foreachtypeforeachattribute; +attributes/forEach/ForEachAttributeConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattribute; +attributes/forEach/ForEachAttributeDifferentNameClassMIConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattributedifferentnameclassmi; +attributes/forEach/ForEachAttributeDifferentTypeConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattributedifferenttype; +attributes/forEach/ForEachAttributeNoTargetIncConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattributenotargetinc; +attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.attributes.underspecified.adapter.attributetypeunderspecifieddifferentinctypes; +evaluation/builder/DataModelConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.evaluation.builder.adapter.datamodel; +methods/forEach/ForEachAttributeConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachattribute; +types/forEach/ForEachTypeForEachAttributeConc.cd adapter _shared/java/group-0011/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.types.foreach.adapter.foreachtypeforeachattribute; +attributes/forEach/ForEachAttributeConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach; +attributes/forEach/ForEachAttributeDifferentNameClassMIConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach; +attributes/forEach/ForEachAttributeDifferentNameConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach; +attributes/forEach/ForEachAttributeDifferentTypeConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach; +attributes/forEach/ForEachAttributeInheritanceConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach; +attributes/forEach/ForEachAttributeNoTargetIncConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.foreach; +attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.underspecified; +attributes/underspecified/AttributeTypeUnderspecifiedNoIncConc.cd concrete _shared/java/group-0012/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.underspecified; +attributes/valid/AttributeInDeepSuperClassConc.cd adapter _shared/java/group-0013/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.valid.adapter.attributeindeepsuperclass; +attributes/valid/AttributeInSuperClassConc.cd adapter _shared/java/group-0013/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.valid.adapter.attributeinsuperclass; +attributes/valid/AttributesMissingConc.cd adapter _shared/java/group-0014/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.valid.adapter.attributesmissing; +attributes/valid/TwoAttributesMissingConc.cd adapter _shared/java/group-0014/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.attributes.valid.adapter.twoattributesmissing; +evaluation/banking/multiInc/BankingConc.cd adapter _shared/java/group-0015/Bank.java Bank.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking; +evaluation/banking/singleInc/BankingConc.cd adapter _shared/java/group-0015/Bank.java Bank.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking; +evaluation/banking/usageByExtension/BankingConc.cd adapter _shared/java/group-0015/Bank.java Bank.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking; +evaluation/banking2/BankingConc.cd adapter _shared/java/group-0015/Bank.java Bank.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; +evaluation/banking/singleInc/BankingConc.cd concrete _shared/java/group-0016/BankAccount.java BankAccount.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking.singleinc.concrete.banking; +evaluation/banking/usageByExtension/BankingConc.cd concrete _shared/java/group-0016/BankAccount.java BankAccount.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking.usagebyextension.concrete.banking; +evaluation/banking2/BankingConc.cd concrete _shared/java/group-0016/BankAccount.java BankAccount.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking2; +evaluation/banking/singleInc/BankingConc.cd concrete _shared/java/group-0017/SEPABank.java SEPABank.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking.singleinc.concrete.banking; +evaluation/banking/usageByExtension/BankingConc.cd concrete _shared/java/group-0017/SEPABank.java SEPABank.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking.usagebyextension.concrete.banking; +evaluation/banking2/BankingConc.cd concrete _shared/java/group-0017/SEPABank.java SEPABank.java package de.monticore.codeAdaption.cdconcretization.evaluation.banking2; +evaluation/builder/DataModelConc.cd concrete _shared/java/group-0018/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.evaluation.builder; +evaluation/getter-setter/DataModelConc.cd concrete _shared/java/group-0018/Department.java Department.java package de.monticore.codeAdaption.cdconcretization.evaluation.getter_setter; +evaluation/builder/DataModelConc.cd concrete _shared/java/group-0019/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.evaluation.builder; +evaluation/getter-setter/DataModelConc.cd concrete _shared/java/group-0019/Employee.java Employee.java package de.monticore.codeAdaption.cdconcretization.evaluation.getter_setter; +evaluation/getter-setter/DataModelConc.cd adapter _shared/java/group-0020/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.evaluation.getter_setter.adapter.datamodel; +methods/forEach/ForEachAttributeSameReturnTypeClassMIConc.cd adapter _shared/java/group-0020/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachattributesamereturntypeclassmi; +evaluation/transitiveDependencies/TransitiveDependenciesConc.cd adapter _shared/java/group-0021/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies.adapter.transitivedependencies; +types/forEach/ForEachTypeConc.cd adapter _shared/java/group-0021/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.types.foreach.adapter.foreachtype; +types/forEach/ForEachTypeInfixReplaceConc.cd adapter _shared/java/group-0021/DataClass.java DataClass.java package de.monticore.codeAdaption.cdconcretization.types.foreach.adapter.foreachtypeinfixreplace; +evaluation/transitiveDependencies/TransitiveDependenciesConc.cd adapter _shared/java/group-0022/DataClassBuilder.java DataClassBuilder.java package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies.adapter.transitivedependencies; +types/forEach/ForEachTypeInfixReplaceConc.cd adapter _shared/java/group-0022/DataClassBuilder.java DataClassBuilder.java package de.monticore.codeAdaption.cdconcretization.types.foreach.adapter.foreachtypeinfixreplace; +inheritance/AttributeInheritanceConc.cd adapter _shared/java/group-0023/Person.java Person.java package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.attributeinheritance; +inheritance/AttributeTypeMismatchConc.cd adapter _shared/java/group-0023/Person.java Person.java package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.attributetypemismatch; +inheritance/AttributeInheritanceConc.cd adapter _shared/java/group-0024/Teacher.java Teacher.java package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.attributeinheritance; +inheritance/AttributeTypeMismatchConc.cd adapter _shared/java/group-0024/Teacher.java Teacher.java package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.attributetypemismatch; +inheritance/MissingInheritanceConc.cd adapter _shared/java/group-0024/Teacher.java Teacher.java package de.monticore.codeAdaption.cdconcretization.inheritance.adapter.missinginheritance; +methods/basic/valid/ClassMissingConc.cd concrete _shared/java/group-0025/Placeholder.java Placeholder.java package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.classmissing; +types/forEach/ForEachTypeConc.cd concrete _shared/java/group-0025/Placeholder.java Placeholder.java package de.monticore.codeAdaption.cdconcretization.types.foreach; +types/forEach/ForEachTypeForEachAttributeConc.cd concrete _shared/java/group-0025/Placeholder.java Placeholder.java package de.monticore.codeAdaption.cdconcretization.types.foreach; +types/forEach/ForEachTypeInfixReplaceConc.cd concrete _shared/java/group-0025/Placeholder.java Placeholder.java package de.monticore.codeAdaption.cdconcretization.types.foreach; +types/forEach/ForEachTypeNoTargetIncConc.cd concrete _shared/java/group-0025/Placeholder.java Placeholder.java package de.monticore.codeAdaption.cdconcretization.types.foreach; +methods/forEach/ForEachTypeSameReturnTypeConc.cd adapter _shared/java/group-0026/Builder.java Builder.java package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachtypesamereturntype; +types/forEach/ForEachTypeConc.cd adapter _shared/java/group-0026/Builder.java Builder.java package de.monticore.codeAdaption.cdconcretization.types.foreach.adapter.foreachtype; +methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd concrete _shared/java/group-0027/BusinessAccount.java BusinessAccount.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; +methods/multiIncarnation/ParameterTypeMIConc.cd concrete _shared/java/group-0027/BusinessAccount.java BusinessAccount.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parametertypemi; +methods/multiIncarnation/ParameterTypeMIOneExistsConc.cd concrete _shared/java/group-0027/BusinessAccount.java BusinessAccount.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parametertypemioneexists; +methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd concrete _shared/java/group-0028/InstantTransaction.java InstantTransaction.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; +methods/multiIncarnation/ReturnTypeMIConc.cd concrete _shared/java/group-0028/InstantTransaction.java InstantTransaction.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.returntypemi; +methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd concrete _shared/java/group-0029/NormalTransaction.java NormalTransaction.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; +methods/multiIncarnation/ReturnTypeMIConc.cd concrete _shared/java/group-0029/NormalTransaction.java NormalTransaction.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.returntypemi; +methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd concrete _shared/java/group-0030/PersonalAccount.java PersonalAccount.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parameterandreturntypemi; +methods/multiIncarnation/ParameterTypeMIConc.cd concrete _shared/java/group-0030/PersonalAccount.java PersonalAccount.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parametertypemi; +methods/multiIncarnation/ParameterTypeMIOneExistsConc.cd concrete _shared/java/group-0030/PersonalAccount.java PersonalAccount.java package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parametertypemioneexists; diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Department.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Department.java new file mode 100644 index 00000000..57757693 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Department.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.adapter.evaluation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Department"}, template = "${}") +public class Department { + @Adapt(ref = {"Department.name"}, template = "${}") + private String name; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Employee.java new file mode 100644 index 00000000..2928b3c8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Employee.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.adapter.evaluation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee extends Person { + @Adapt(ref = {"Employee.id"}, template = "${}") + private int id; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Intern.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Intern.java new file mode 100644 index 00000000..31e28482 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Intern.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.adapter.evaluation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Intern"}, template = "${}") +public class Intern extends Person { + @Adapt(ref = {"Intern.supervisorID"}, template = "${}") + private int supervisorID; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Person.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Person.java new file mode 100644 index 00000000..54e0a24c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Person.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.adapter.evaluation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Person"}, template = "${}") +public class Person { + @Adapt(ref = {"Person.name"}, template = "${}") + private String name; + + @Adapt(ref = {"Person.age"}, template = "${}") + private int age; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Staff.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Staff.java new file mode 100644 index 00000000..453806d9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/adapter/Evaluation/Staff.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.adapter.evaluation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Staff"}, template = "${}") +public class Staff { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptConc.cd new file mode 100644 index 00000000..95622df1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptConc.cd @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocNameImplicitNameAdaptConc { + <> class Ticket; + <> class Sprint; + // existing association without name — DefaultAssocCompleter copies and adapts it + association Sprint -> Ticket [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptOut.cd new file mode 100644 index 00000000..9d0f4311 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptOut.cd @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocNameImplicitNameAdaptConc { + <> class Ticket; + <> class Sprint; + // association name "taskAssignment" copied from reference and adapted: "task" -> "ticket" + association ticketAssignment Sprint -> Ticket [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptRef.cd new file mode 100644 index 00000000..808123b4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocNameImplicitNameAdaptRef.cd @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocNameImplicitNameAdaptRef { + class Task; + class Project; + // association name "taskAssignment" starts with uncapitalised "task" -> adapted to "ticketAssignment" + association taskAssignment Project -> Task [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptConc.cd new file mode 100644 index 00000000..22239ab2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptConc.cd @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocRoleImplicitNameAdaptConc { + <> class Ticket; + <> class Sprint; + // existing association without role name — DefaultAssocCompleter adds and adapts it + association Sprint -> Ticket [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptOut.cd new file mode 100644 index 00000000..e53a03b1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptOut.cd @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocRoleImplicitNameAdaptConc { + <> class Ticket; + <> class Sprint; + // role name "assignedTask" copied from reference and adapted: "Task" -> "Ticket" + association Sprint -> (assignedTicket) Ticket [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptRef.cd new file mode 100644 index 00000000..e2ae2a7a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocRoleImplicitNameAdaptRef.cd @@ -0,0 +1,6 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocRoleImplicitNameAdaptRef { + class Task; + class Project; + association Project -> (assignedTask) Task [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetConc.cd new file mode 100644 index 00000000..f3d2dd2e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetConc.cd @@ -0,0 +1,7 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocSubtypeTargetConc { + // concrete CD has the same types as the reference but no associations + class Container; + class Item; + class SpecialItem extends Item; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetOut.cd new file mode 100644 index 00000000..c31a798b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetOut.cd @@ -0,0 +1,10 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocSubtypeTargetConc { + class Container; + class Item; + class SpecialItem extends Item; + // both associations are added: SpecialItem extends Item, but Item [*] must not suppress + // adding a separate incarnation for the explicitly modelled SpecialItem association + association Container -> (items) Item [*]; + association Container -> (specialItems) SpecialItem [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetRef.cd new file mode 100644 index 00000000..f761b4b6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssocSubtypeTargetRef.cd @@ -0,0 +1,8 @@ +/* (c) https://github.com/MontiCore/monticore */ +classdiagram AssocSubtypeTargetRef { + class Container; + class Item; + class SpecialItem extends Item; + association Container -> (items) Item [*]; + association Container -> (specialItems) SpecialItem [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeConc.cd new file mode 100644 index 00000000..c4d659c1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeConc.cd @@ -0,0 +1,11 @@ +classdiagram AssociationInSuperTypeConc { + + abstract class AccountTop; + + class Account extends AccountTop; + + class Person ; + + association AccountTop -> Person[1]; + association Person -> Account[*]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeOut.cd new file mode 100644 index 00000000..b6365657 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeOut.cd @@ -0,0 +1,12 @@ +classdiagram AssociationInSuperTypeConc { + + abstract class AccountTop; + + class Account extends AccountTop; + + class Person ; + + association AccountTop -> Person[1]; + association Person -> Account[*]; + // there is no association missing to be conform to reference CD. So the tool should not add any association +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeRef.cd new file mode 100644 index 00000000..e0098405 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationInSuperTypeRef.cd @@ -0,0 +1,8 @@ +classdiagram AssociationInSuperTypeRef { + class Account; + + class Person ; + + association Account -> Person[1]; + association Person -> Account[*] ; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityConc.cd new file mode 100644 index 00000000..5ab2436b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityConc.cd @@ -0,0 +1,12 @@ +classdiagram AssociationMissingCardinalityConc { + class Employee { + } + class Department { + } + class A { + } + class B { + } + association Employee <-> Department; + association A <-> B ; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityOut.cd new file mode 100644 index 00000000..3615d936 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityOut.cd @@ -0,0 +1,12 @@ +classdiagram AssociationMissingCardinalityConc { + class Employee { + } + class Department { + } + class A { + } + class B { + } + association [1..*] Employee <-> Department [1]; + association A <-> B [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityRef.cd new file mode 100644 index 00000000..76a385d8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingCardinalityRef.cd @@ -0,0 +1,12 @@ +classdiagram AssociationMissingCardinalityRef { + class Employee { + } + class Department { + } + class A { + } + class B { + } + association A <-> B [1]; + association [1..*] Employee <-> Department [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalConc.cd new file mode 100644 index 00000000..1d973cc0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalConc.cd @@ -0,0 +1,7 @@ +classdiagram AssociationMissingFinalConc { + class Employee { + } + class Department { + } + association Employee (employed) -> Department [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalOut.cd new file mode 100644 index 00000000..a433f8c3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalOut.cd @@ -0,0 +1,6 @@ +classdiagram AssociationMissingFinalConc { + class Employee; + class Department; + + association [1..*] Employee (employed) <-> (workplace) Department [1]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalRef.cd new file mode 100644 index 00000000..c2948e32 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingFinalRef.cd @@ -0,0 +1,7 @@ +classdiagram AssociationMissingFinalRef { + class Employee { + } + class Department { + } + association [1] Department (workplace) -> Employee [1..*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameConc.cd new file mode 100644 index 00000000..30722477 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameConc.cd @@ -0,0 +1,7 @@ +classdiagram AssociationMissingRolenameConc { + class Employee { + } + class Department { + } + association Employee <-> Department; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameOut.cd new file mode 100644 index 00000000..b712a5c7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameOut.cd @@ -0,0 +1,7 @@ +classdiagram AssociationMissingRolenameConc { + class Employee { + } + class Department { + } + association Employee (employed) <-> Department; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameRef.cd new file mode 100644 index 00000000..6f9fb368 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingRolenameRef.cd @@ -0,0 +1,7 @@ +classdiagram AssociationMissingRolenameRef { + class Employee { + } + class Department { + } + association Employee (employed) <-> Department; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleConc.cd new file mode 100644 index 00000000..6de9993d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleConc.cd @@ -0,0 +1,12 @@ +classdiagram AssociationMissingSimpleConc { + class Employee { + } + class Department { + } + class A { + + } + class B { + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleOut.cd new file mode 100644 index 00000000..d61307d4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleOut.cd @@ -0,0 +1,14 @@ +classdiagram AssociationMissingSimpleConc { + class Employee { + } + class Department { + } + class A { + + } + class B { + + } + association Employee -- Department; + association A (partOf) -- B; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleRef.cd new file mode 100644 index 00000000..7dc266cc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMissingSimpleRef.cd @@ -0,0 +1,14 @@ +classdiagram AssociationMissingSimpleRef { + class Employee { + } + class Department { + } + class A { + + } + class B { + + } + association Employee -- Department; + association A (partOf) -- B; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationConc.cd new file mode 100644 index 00000000..d9dfd665 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationConc.cd @@ -0,0 +1,10 @@ +classdiagram AssocMultipleTypeIncarnationConc { + class Teacher { + } + <> class MathDept { + } + <> class ScienceDept { + } +} + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationOut.cd new file mode 100644 index 00000000..0b70c335 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationOut.cd @@ -0,0 +1,10 @@ +classdiagram AssocMultipleTypeIncarnationConc { + class Teacher; + + <>class MathDept; + <>class ScienceDept; + + association Teacher (hasTeacher) <-> (teachesIn_MathDept) MathDept; + association Teacher (hasTeacher) <-> (teachesIn_ScienceDept) ScienceDept; + +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationRef.cd new file mode 100644 index 00000000..ec101a21 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationMultipleTypeIncarnationRef.cd @@ -0,0 +1,7 @@ +classdiagram AssocMultipleTypeIncarnationRef { + class Teacher { + } + class Department { + } + association Teacher (hasTeacher) <-> (teachesIn) Department; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchConc.cd new file mode 100644 index 00000000..2f265d25 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchConc.cd @@ -0,0 +1,7 @@ +classdiagram AssociationReverseMatchConc { + class A { + } + class B { + } + association B <- A; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchOut.cd new file mode 100644 index 00000000..db263d89 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchOut.cd @@ -0,0 +1,6 @@ +classdiagram AssociationReverseMatchConc { + class A; + class B; + + association [*] B <- (partOf) A; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchRef.cd new file mode 100644 index 00000000..40b03ec4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationReverseMatchRef.cd @@ -0,0 +1,7 @@ +classdiagram AssociationReverseMatchRef { + class A { + } + class B { + } + association A (partOf) -> B [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingConc.cd new file mode 100644 index 00000000..bd61f4ea --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingConc.cd @@ -0,0 +1,22 @@ +classdiagram AssociationSuperMatchingConc { + + class ASuperSuper1; + class ASuperSuper2; + class ASuper1 extends ASuperSuper1; + class ASuper2 extends ASuperSuper2; + class ASuper3; + + <> class A1 extends ASuper1; + <> class A2 extends ASuper2; + <> class A3 extends ASuper3; // this one is missing associations + + abstract class BSuperSuper1; + abstract class BSuperSuper2; + abstract class BSuper1 extends BSuperSuper1, BSuperSuper2; + + <> class B1 extends BSuper1; + <> class B2 extends BSuperSuper2; // this one is missing associations + + association ASuperSuper1 -> BSuperSuper1 [*]; + association ASuperSuper2 -> BSuperSuper1 [*]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingOut.cd new file mode 100644 index 00000000..b3a27d92 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingOut.cd @@ -0,0 +1,27 @@ +classdiagram AssociationSuperMatchingConc { + + class ASuperSuper1; + class ASuperSuper2; + class ASuper1 extends ASuperSuper1; + class ASuper2 extends ASuperSuper2; + class ASuper3; + + <> class A1 extends ASuper1; + <> class A2 extends ASuper2; + <> class A3 extends ASuper3; // this one is missing associations + + abstract class BSuperSuper1; + abstract class BSuperSuper2; + abstract class BSuper1 extends BSuperSuper1, BSuperSuper2; + + <> class B1 extends BSuper1; + <> class B2 extends BSuperSuper2; // this one is missing associations + + association ASuperSuper1 -> BSuperSuper1 [*]; + association ASuperSuper2 -> BSuperSuper1 [*]; + + association A3 -> B1 [*]; + association A3 -> B2 [*]; + association A1 -> B2 [*]; + association A2 -> B2 [*]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingRef.cd new file mode 100644 index 00000000..dd183612 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/AssociationSuperMatchingRef.cd @@ -0,0 +1,6 @@ +classdiagram AssociationSuperMatchingRef { + class A; + class B; + + association A -> B [*]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsConc.cd new file mode 100644 index 00000000..3a0cfcc4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsConc.cd @@ -0,0 +1,8 @@ +classdiagram TypeMIOneAssocExistsConc { + + class A; + <> class B1; + <> class B2; + + association A -- (partOf) B1; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsOut.cd new file mode 100644 index 00000000..b5c7faff --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsOut.cd @@ -0,0 +1,9 @@ +classdiagram TypeMIOneAssocExistsConc { + + class A; + <> class B1; + <> class B2; + + association A -- (partOf) B1; + association A -- (partOf_B2) B2; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsRef.cd new file mode 100644 index 00000000..8fbbe01c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/TypeMIOneAssocExistsRef.cd @@ -0,0 +1,7 @@ +classdiagram TypeMIOneAssocExistsRef { + + class A; + class B; + + association A -- (partOf) B; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/Container.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/Container.java new file mode 100644 index 00000000..cc744da1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/Container.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocsubtypetarget; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Container"}, template = "${}") +public class Container { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/Item.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/Item.java new file mode 100644 index 00000000..6161a4ec --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/Item.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocsubtypetarget; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Item"}, template = "${}") +public class Item { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/SpecialItem.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/SpecialItem.java new file mode 100644 index 00000000..c1ef6e27 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssocSubtypeTarget/SpecialItem.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.assocsubtypetarget; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"SpecialItem"}, template = "${}") +public class SpecialItem extends Item { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssociationMultipleTypeIncarnation/Teacher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssociationMultipleTypeIncarnation/Teacher.java new file mode 100644 index 00000000..66554f71 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/adapter/AssociationMultipleTypeIncarnation/Teacher.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.associations.adapter.associationmultipletypeincarnation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Teacher"}, template = "${}") +public class Teacher { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/concrete/B1.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/concrete/B1.java new file mode 100644 index 00000000..8ff56b05 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/concrete/B1.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.associations; + +public class B1 { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/concrete/B2.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/concrete/B2.java new file mode 100644 index 00000000..b29889fb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/associations/concrete/B2.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.associations; + +public class B2 { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeConc.cd new file mode 100644 index 00000000..75a16f5b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram ForEachAttributeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameClassMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameClassMIConc.cd new file mode 100644 index 00000000..b668d5eb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameClassMIConc.cd @@ -0,0 +1,15 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentNameClassMIConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameClassMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameClassMIOut.cd new file mode 100644 index 00000000..c20574f2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameClassMIOut.cd @@ -0,0 +1,24 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentNameClassMIConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } + + class Builder { + <> String attrCopy_Employee_firstName; + <> String attrCopy_Employee_lastName; + <> int attrCopy_Employee_number; + <> int attrCopy_Employee_salary; + <> String attrCopy_Department_managerName; + <> int attrCopy_Department_depId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameConc.cd new file mode 100644 index 00000000..92debce5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentNameConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameOut.cd new file mode 100644 index 00000000..09eecf93 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameOut.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentNameConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + class Builder { + <> String attrCopy_firstName; + <> String attrCopy_lastName; + <> int attrCopy_number; + <> int attrCopy_salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameRef.cd new file mode 100644 index 00000000..db5ac590 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentNameRef.cd @@ -0,0 +1,12 @@ + +classdiagram ForEachAttributeDifferentNameRef { + class DataClass { + any attribute; + } + + class Builder { + // Incarnations will have the name "attrCopy_{DataClass.attribute incarnation}" because here + // target and source attribute have different names! + <> any attrCopy; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeConc.cd new file mode 100644 index 00000000..5b2c71e2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentTypeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeOut.cd new file mode 100644 index 00000000..c29d06ca --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeOut.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentTypeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + class Builder { + <> boolean hasModifiedFirstName; + <> boolean hasModifiedLastName; + <> boolean hasModifiedNumber; + <> boolean hasModifiedSalary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeRef.cd new file mode 100644 index 00000000..725c5ddd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeDifferentTypeRef.cd @@ -0,0 +1,12 @@ + +classdiagram ForEachAttributeDifferentTypeRef { + class DataClass { + any attribute; + } + + class Builder { + // Incarnations will all have the type 'boolean' as the referenced attribute does not have the + // same type as the annotated attribute! + <> boolean hasModifiedAttribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeInheritanceConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeInheritanceConc.cd new file mode 100644 index 00000000..f6c023f0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeInheritanceConc.cd @@ -0,0 +1,13 @@ +import java.lang.String; + +classdiagram ForEachAttributeInheritanceConc { + class Person { + <> String firstName; + <> String lastName; + } + + <> class Employee extends Person { + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeInheritanceOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeInheritanceOut.cd new file mode 100644 index 00000000..9546bcc6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeInheritanceOut.cd @@ -0,0 +1,20 @@ +import java.lang.String; + +classdiagram ForEachAttributeInheritanceConc { + class Person { + <> String firstName; + <> String lastName; + } + + <> class Employee extends Person { + <> int number; + <> int salary; + } + + class Builder { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncConc.cd new file mode 100644 index 00000000..8477a472 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncConc.cd @@ -0,0 +1,9 @@ +import java.lang.String; + +classdiagram ForEachAttributeNoTargetIncConc { + <> class Employee { + // NOTE: the attribute is not annotated with <>! + String firstName; + String lastName; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncOut.cd new file mode 100644 index 00000000..d0b2fdad --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncOut.cd @@ -0,0 +1,11 @@ +import java.lang.String; + +classdiagram ForEachAttributeNoTargetIncConc { + <> class Employee { + String firstName; + String lastName; + } + + class Builder { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncRef.cd new file mode 100644 index 00000000..d75c46ab --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeNoTargetIncRef.cd @@ -0,0 +1,12 @@ + +classdiagram ForEachAttributeNoTargetIncRef { + class DataClass { + // TODO This example only works once we have the 'matchStructure' stereotype implemented + // TODO alternative: implement a stereotype 'optional' that cna be used to mark elements that must not be incarnated + <> any attribute; + } + + class Builder { + <> any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeOut.cd new file mode 100644 index 00000000..549f2a01 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeOut.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram ForEachAttributeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + class Builder { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeRef.cd new file mode 100644 index 00000000..e771c2e4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/ForEachAttributeRef.cd @@ -0,0 +1,12 @@ + +classdiagram ForEachAttributeRef { + class DataClass { + any attribute; + } + + class Builder { + // Incarnations will have the same name as the referenced attribute incarnations by convention + // because the referenced attribute has the same name as the annotated attribute! + <> any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/Builder.java new file mode 100644 index 00000000..d051cbdb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/Builder.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.foreach; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}") +public class Builder { + @Adapt(ref = {"Builder.attribute"}, template = "${}") + private Object attribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/DataClass.java new file mode 100644 index 00000000..e419a410 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/DataClass.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.foreach; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/ForEachAttributeDifferentNameClassMI/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/ForEachAttributeDifferentNameClassMI/Builder.java new file mode 100644 index 00000000..738398f0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/ForEachAttributeDifferentNameClassMI/Builder.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattributedifferentnameclassmi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}") +public class Builder { + @Adapt(ref = {"Builder.attrCopy"}, template = "${}") + private Object attrCopy; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/ForEachAttributeDifferentType/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/ForEachAttributeDifferentType/Builder.java new file mode 100644 index 00000000..05ce0e72 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/forEach/adapter/ForEachAttributeDifferentType/Builder.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.foreach.adapter.foreachattributedifferenttype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}") +public class Builder { + @Adapt(ref = {"Builder.hasModifiedAttribute"}, template = "${}") + private boolean hasModifiedAttribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesConc.cd new file mode 100644 index 00000000..ae30f34b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesConc.cd @@ -0,0 +1,9 @@ +import java.lang.String; + +classdiagram AttributeTypeUnderspecifiedDifferentIncTypesConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int salery; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesOut.cd new file mode 100644 index 00000000..bcbd85fe --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedDifferentIncTypesOut.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram AttributeTypeUnderspecifiedDifferentIncTypesConc { + // nothing changed. we just make sure the tool can process the input and does not add anything + <> class Employee { + <> String firstName; + <> String lastName; + <> int salery; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedNoIncConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedNoIncConc.cd new file mode 100644 index 00000000..467da550 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedNoIncConc.cd @@ -0,0 +1,9 @@ +import java.lang.String; + +classdiagram AttributeTypeUnderspecifiedNoIncConc { + <> class Employee { + // NOTE: the attribute is not annotated with <>! + String firstName; + String lastName; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedRef.cd new file mode 100644 index 00000000..d5b8a57c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/AttributeTypeUnderspecifiedRef.cd @@ -0,0 +1,6 @@ + +classdiagram AttributeTypeUnderspecifiedRef { + class DataClass { + any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/adapter/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/adapter/DataClass.java new file mode 100644 index 00000000..0980d1c5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/underspecified/adapter/DataClass.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.underspecified; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassConc.cd new file mode 100644 index 00000000..8503d2c1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassConc.cd @@ -0,0 +1,16 @@ +import java.lang.String; + +classdiagram AttributeInDeepSuperClassConc { + class Person { + String firstName; + String lastName; + } + + class AbstractEmployee extends Person { + String someOtherProperty; + } + + class Employee extends AbstractEmployee { + int number; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassOut.cd new file mode 100644 index 00000000..7cb33549 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassOut.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram AttributeInDeepSuperClassConc { + class Person { + String firstName; + String lastName; + } + + class AbstractEmployee extends Person { + String someOtherProperty; + } + + class Employee extends AbstractEmployee { + int number; + int salary; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassRef.cd new file mode 100644 index 00000000..e716e6ad --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInDeepSuperClassRef.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram AttributeInDeepSuperClassRef { + class Employee { + String firstName; + String lastName; + int number; + int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassConc.cd new file mode 100644 index 00000000..ec00665a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassConc.cd @@ -0,0 +1,12 @@ +import java.lang.String; + +classdiagram AttributeInSuperClassConc { + class Person { + String firstName; + String lastName; + } + + class Employee extends Person { + int number; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassOut.cd new file mode 100644 index 00000000..bed54bad --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassOut.cd @@ -0,0 +1,13 @@ +import java.lang.String; + +classdiagram AttributeInSuperClassConc { + class Person { + String firstName; + String lastName; + } + + class Employee extends Person { + int number; + int salary; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassRef.cd new file mode 100644 index 00000000..bf3ea05a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributeInSuperClassRef.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram AttributeInSuperClassRef { + class Employee { + String firstName; + String lastName; + int number; + int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributesMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributesMissingConc.cd new file mode 100644 index 00000000..524b5160 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributesMissingConc.cd @@ -0,0 +1,5 @@ +classdiagram AttributesMissingConc { + class Employee { + int number; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributesMissingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributesMissingRef.cd new file mode 100644 index 00000000..48c5d49b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/AttributesMissingRef.cd @@ -0,0 +1,6 @@ +classdiagram AttributesMissingRef { + class Employee { + int number; + int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingConc.cd new file mode 100644 index 00000000..5da89302 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingConc.cd @@ -0,0 +1,4 @@ +classdiagram TwoAttributesMissingConc { + class Employee { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingOneMatchConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingOneMatchConc.cd new file mode 100644 index 00000000..37605e83 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingOneMatchConc.cd @@ -0,0 +1,5 @@ +classdiagram TwoAttributesMissingOneMatchConc { + class Employee { + int number; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingOneMatchRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingOneMatchRef.cd new file mode 100644 index 00000000..72c4527b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingOneMatchRef.cd @@ -0,0 +1,7 @@ +classdiagram TwoAttributesMissingOneMatchRef { + class Employee { + int number; + int anotherNumber; + int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingRef.cd new file mode 100644 index 00000000..fc38d3da --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/TwoAttributesMissingRef.cd @@ -0,0 +1,6 @@ +classdiagram TwoAttributesMissingRef { + class Employee { + int number; + int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/adapter/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/adapter/Employee.java new file mode 100644 index 00000000..9b5513c3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/adapter/Employee.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.valid; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee { + @Adapt(ref = {"Employee.firstName"}, template = "${}") + private String firstName; + + @Adapt(ref = {"Employee.lastName"}, template = "${}") + private String lastName; + + @Adapt(ref = {"Employee.number"}, template = "${}") + private int number; + + @Adapt(ref = {"Employee.salary"}, template = "${}") + private int salary; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/adapter/TwoAttributesMissingOneMatch/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/adapter/TwoAttributesMissingOneMatch/Employee.java new file mode 100644 index 00000000..bcf2d8dd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/adapter/TwoAttributesMissingOneMatch/Employee.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.valid.adapter.twoattributesmissingonematch; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee { + @Adapt(ref = {"Employee.number"}, template = "${}") + private int number; + + @Adapt(ref = {"Employee.anotherNumber"}, template = "${}") + private int anotherNumber; + + @Adapt(ref = {"Employee.salary"}, template = "${}") + private int salary; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/concrete/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/concrete/Employee.java new file mode 100644 index 00000000..8472bcf2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/attributes/valid/concrete/Employee.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.attributes.valid; + +public class Employee { + private int number; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Administration.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Administration.java new file mode 100644 index 00000000..cc3c5df0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Administration.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.concrete; + +public class Administration { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Employee.java new file mode 100644 index 00000000..bfaf32c2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Employee.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.concrete; + +public class Employee { + private int employeeID; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Project.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Project.java new file mode 100644 index 00000000..284f017c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Project.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.concrete; + +public class Project { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Staff.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Staff.java new file mode 100644 index 00000000..c3857556 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Staff.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.concrete; + +public class Staff { + private int staffID; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Trainee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Trainee.java new file mode 100644 index 00000000..411fbd50 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/concrete/Trainee.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.concrete; + +public class Trainee { + private int supervisorID; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/BankingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/BankingRef.cd new file mode 100644 index 00000000..dbf0f299 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/BankingRef.cd @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram Banking { + + class Bank { + String id; + double overallBalance; // money of all accounts together + } + + class Account { + String id; + double balance; // money in the account + } + + class Customer { + String name; + String address; + String phoneNumber; + } + + // sends money from sourceAccount to targetAccount + class Transaction { + Account sourceAccount; + Account targetAccount; + double amount; + } + + association Bank -> Account [*]; + association Bank -> Customer [*]; + association [*] Account <-> Customer [*]; + association Account -> Transaction [*] <>; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Account.java new file mode 100644 index 00000000..bf4cd2cf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Account.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Account"}, template = "${}") +public class Account { + @Adapt(ref = {"Account.id"}, template = "${}") + private String id; + + @Adapt(ref = {"Account.balance"}, template = "${}") + private double balance; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Customer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Customer.java new file mode 100644 index 00000000..f7060621 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Customer.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Customer"}, template = "${}") +public class Customer { + @Adapt(ref = {"Customer.name"}, template = "${}") + private String name; + + @Adapt(ref = {"Customer.address"}, template = "${}") + private String address; + + @Adapt(ref = {"Customer.phoneNumber"}, template = "${}") + private String phoneNumber; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Transaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Transaction.java new file mode 100644 index 00000000..a78f2099 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/adapter/Transaction.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Transaction"}, template = "${}") +public class Transaction { + @Adapt(ref = {"Transaction.sourceAccount"}, template = "${}") + private Account sourceAccount; + + @Adapt(ref = {"Transaction.targetAccount"}, template = "${}") + private Account targetAccount; + + @Adapt(ref = {"Transaction.amount"}, template = "${}") + private double amount; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/BankingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/BankingConc.cd new file mode 100644 index 00000000..0a04a540 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/BankingConc.cd @@ -0,0 +1,22 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram Banking { + + <> class SEPABank { + <> String bic; + <> double privateAccountsOverallBalance; + <> double businessAccountsOverallBalance; + } + + <> class PrivateAccount { + <> String iban; + } + + <> class BusinessAccount { + <> String iban; + } + + <> class PrivateCustomer; + <> class BusinessCustomer; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/BankingOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/BankingOut.cd new file mode 100644 index 00000000..7745ea0a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/BankingOut.cd @@ -0,0 +1,49 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram Banking { + + <> class SEPABank { + <> String bic; + <> double privateAccountsOverallBalance; + <> double businessAccountsOverallBalance; + } + + <> class PrivateAccount { + <> String iban; + double balance; // money in the account + } + + <> class BusinessAccount { + <> String iban; + double balance; // money in the account + } + + <> class PrivateCustomer { + String name; + String address; + String phoneNumber; + } + + <> class BusinessCustomer { + String name; + String address; + String phoneNumber; + } + + // sends money from sourceAccount to targetAccount + class Transaction { + PrivateAccount sourceAccount; + PrivateAccount targetAccount; + double amount; + } + + association SEPABank -> PrivateAccount [*]; + association SEPABank -> BusinessAccount [*]; + association SEPABank -> PrivateCustomer [*]; + association SEPABank -> BusinessCustomer [*]; + association [*] PrivateAccount <-> PrivateCustomer [*]; + association [*] BusinessAccount <-> BusinessCustomer [*]; + association PrivateAccount -> Transaction [*] <>; + association BusinessAccount -> Transaction [*] <>; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/BusinessAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/BusinessAccount.java new file mode 100644 index 00000000..5be30eea --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/BusinessAccount.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.multiinc.concrete.banking; + +public class BusinessAccount { + private String iban; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/BusinessCustomer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/BusinessCustomer.java new file mode 100644 index 00000000..2cac03e8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/BusinessCustomer.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.multiinc.concrete.banking; + +public class BusinessCustomer { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/PrivateAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/PrivateAccount.java new file mode 100644 index 00000000..9c7c1080 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/PrivateAccount.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.multiinc.concrete.banking; + +public class PrivateAccount { + private String iban; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/PrivateCustomer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/PrivateCustomer.java new file mode 100644 index 00000000..19e7cce4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/PrivateCustomer.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.multiinc.concrete.banking; + +public class PrivateCustomer { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/SEPABank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/SEPABank.java new file mode 100644 index 00000000..ec08a330 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/multiInc/concrete/Banking/SEPABank.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.multiinc.concrete.banking; + +public class SEPABank { + private String bic; + + private double privateAccountsOverallBalance; + + private double businessAccountsOverallBalance; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/singleInc/BankingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/singleInc/BankingConc.cd new file mode 100644 index 00000000..e5d04992 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/singleInc/BankingConc.cd @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram Banking { + + <> class SEPABank { + <> String bic; + <> double totalMoney; // money of all accounts together + } + + <> class BankAccount { + <> String iban; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/singleInc/BankingOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/singleInc/BankingOut.cd new file mode 100644 index 00000000..01b50e83 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/singleInc/BankingOut.cd @@ -0,0 +1,33 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram Banking { + + <> class SEPABank { + <> String bic; + <> double totalMoney; // money of all accounts together + } + + <> class BankAccount { + <> String iban; + double balance; // money in the account + } + + class Customer { + String name; + String address; + String phoneNumber; + } + + // sends money from sourceAccount to targetAccount + class Transaction { + BankAccount sourceBankAccount; + BankAccount targetBankAccount; + double amount; + } + + association SEPABank -> BankAccount [*]; + association SEPABank -> Customer [*]; + association [*] BankAccount <-> Customer [*]; + association BankAccount -> Transaction [*] <>; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/BankingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/BankingConc.cd new file mode 100644 index 00000000..5aabbc85 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/BankingConc.cd @@ -0,0 +1,17 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram Banking { + + <> class SEPABank { + <> String bic; + <> double totalMoney; // money of all accounts together + } + + <> class BankAccount { + <> String iban; + } + + class PrivateAccount extends BankAccount; + class BusinessAccount extends BankAccount; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/BankingOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/BankingOut.cd new file mode 100644 index 00000000..5da2d0fb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/BankingOut.cd @@ -0,0 +1,36 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram Banking { + + <> class SEPABank { + <> String bic; + <> double totalMoney; // money of all accounts together + } + + <> class BankAccount { + <> String iban; + double balance; // money in the account + } + + class PrivateAccount extends BankAccount; + class BusinessAccount extends BankAccount; + + class Customer { + String name; + String address; + String phoneNumber; + } + + // sends money from sourceAccount to targetAccount + class Transaction { + BankAccount sourceBankAccount; + BankAccount targetBankAccount; + double amount; + } + + association SEPABank -> BankAccount [*]; + association SEPABank -> Customer [*]; + association [*] BankAccount <-> Customer [*]; + association BankAccount -> Transaction [*] <>; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/concrete/Banking/BusinessAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/concrete/Banking/BusinessAccount.java new file mode 100644 index 00000000..0b35c4d8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/concrete/Banking/BusinessAccount.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.usagebyextension.concrete.banking; + +public class BusinessAccount extends BankAccount { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/concrete/Banking/PrivateAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/concrete/Banking/PrivateAccount.java new file mode 100644 index 00000000..609ff3be --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking/usageByExtension/concrete/Banking/PrivateAccount.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking.usagebyextension.concrete.banking; + +public class PrivateAccount extends BankAccount { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingConc.cd new file mode 100644 index 00000000..946f8399 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingConc.cd @@ -0,0 +1,14 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram BankingConc { + + <> class SEPABank { + <> String bic; + <> double totalMoney; // money of all accounts together + } + + <> class BankAccount { + <> String iban; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingOut.cd new file mode 100644 index 00000000..e8657333 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingOut.cd @@ -0,0 +1,43 @@ +/* (c) https://github.com/MontiCore/monticore */ +import java.lang.String; + +classdiagram BankingConc { + + <> class SEPABank implements Auditable { + <> String bic; + <> double totalMoney; // money of all accounts together + } + + <> abstract class BankAccount implements Auditable { + <> String iban; + double balance; + AccountStatus status; + void withdraw(double amount); + void deposit(double amount); + void transfer(BankAccount targetBankAccount, double amount); + } + + interface Auditable { + String generateReport(); + } + + class SavingsAccount extends BankAccount; + class CheckingAccount extends BankAccount; + + enum AccountStatus { + PENDING, ACTIVE, CLOSED; + } + + class Customer; + + class Transaction { + double amount; + } + + association SEPABank -> BankAccount [*]; + association allCustomers SEPABank -> Customer [*]; + association [*] BankAccount <-> Customer [*]; + association BankAccount -> Transaction [*] {ordered}; + association Transaction -> (source) BankAccount [1]; + association Transaction -> (target) BankAccount [1]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingRef.cd new file mode 100644 index 00000000..0008ca32 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/BankingRef.cd @@ -0,0 +1,42 @@ +import java.lang.String; + +classdiagram BankingRef { + + interface Auditable { + String generateReport(); + } + + class Bank implements Auditable { + String id; + double overallBalance; + } + + abstract class Account implements Auditable { + String id; + double balance; + AccountStatus status; + void withdraw(double amount); + void deposit(double amount); + void transfer(Account targetAccount, double amount); + } + + class SavingsAccount extends Account; + class CheckingAccount extends Account; + + enum AccountStatus { + PENDING, ACTIVE, CLOSED; + } + + class Customer; + + class Transaction { + double amount; + } + + association Bank -> Account [*]; + association allCustomers Bank -> Customer [*]; + association [*] Account <-> Customer [*]; + association Account -> Transaction [*] {ordered}; + association Transaction -> (source) Account [1]; + association Transaction -> (target) Account [1]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Bank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Bank.java new file mode 100644 index 00000000..a5869fc0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Bank.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Bank"}, template = "${}") +public class Bank { + @Adapt(ref = {"Bank.id"}, template = "${}") + private String id; + + @Adapt(ref = {"Bank.overallBalance"}, template = "${}") + private double overallBalance; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Account.java new file mode 100644 index 00000000..cd528bc3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Account.java @@ -0,0 +1,28 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Account"}, template = "${}") +public class Account { + @Adapt(ref = {"Account.id"}, template = "${}") + private String id; + + @Adapt(ref = {"Account.balance"}, template = "${}") + private double balance; + + @Adapt(ref = {"Account.status"}, template = "${}") + private AccountStatus status; + + @Adapt(ref = {"Account.withdraw"}, template = "${}") + public void withdraw(double amount) { + } + + @Adapt(ref = {"Account.deposit"}, template = "${}") + public void deposit(double amount) { + } + + @Adapt(ref = {"Account.transfer"}, template = "${}") + public void transfer(Account targetAccount, double amount) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/AccountStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/AccountStatus.java new file mode 100644 index 00000000..42455197 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/AccountStatus.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; + +public enum AccountStatus { + PENDING, + ACTIVE, + CLOSED; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Auditable.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Auditable.java new file mode 100644 index 00000000..58a536c0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Auditable.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Auditable"}, template = "${}") +public interface Auditable { + @Adapt(ref = {"Auditable.generateReport"}, template = "${}") + String generateReport(); + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/CheckingAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/CheckingAccount.java new file mode 100644 index 00000000..9472122d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/CheckingAccount.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"CheckingAccount"}, template = "${}") +public class CheckingAccount extends Account { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Customer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Customer.java new file mode 100644 index 00000000..9728d941 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Customer.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Customer"}, template = "${}") +public class Customer { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/SavingsAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/SavingsAccount.java new file mode 100644 index 00000000..002f415e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/SavingsAccount.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"SavingsAccount"}, template = "${}") +public class SavingsAccount extends Account { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Transaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Transaction.java new file mode 100644 index 00000000..c713e110 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/banking2/adapter/Banking/Transaction.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.banking2.adapter.banking; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Transaction"}, template = "${}") +public class Transaction { + @Adapt(ref = {"Transaction.amount"}, template = "${}") + private double amount; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/BuilderAndMillOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/BuilderAndMillOut.cd new file mode 100644 index 00000000..2dfeeed3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/BuilderAndMillOut.cd @@ -0,0 +1,42 @@ +import java.lang.String; + +classdiagram DataModelConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } + + <> + class EmployeeBuilder { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + <> EmployeeBuilder firstName(String firstName); + <> EmployeeBuilder lastName(String lastName); + <> EmployeeBuilder number(int number); + <> EmployeeBuilder salary(int salary); + Employee build(); + } + + <> + class DepartmentBuilder { + <> String managerName; + <> int depId; + <> DepartmentBuilder managerName(String managerName); + <> DepartmentBuilder depId(int depId); + Department build(); + } + + class Mill { + <> DepartmentBuilder departmentBuilder(); + <> EmployeeBuilder employeeBuilder(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/BuilderAndMillRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/BuilderAndMillRef.cd new file mode 100644 index 00000000..016e3d5e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/BuilderAndMillRef.cd @@ -0,0 +1,20 @@ +import java.lang.String; + +classdiagram BuilderAndMillRef { + class DataClass { + // TODO use <> here once it is implemented + any attribute; + } + + <> class DataClassBuilder { + // Incarnations will have the same name as the referenced attribute incarnations by convention + // because the referenced attribute has the same name as the annotated attribute! + <> any attribute; + <> DataClassBuilder attribute(any attribute); + DataClass build(); + } + + class Mill { + <> DataClassBuilder dataClassBuilder(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/DataModelConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/DataModelConc.cd new file mode 100644 index 00000000..6b9f3357 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/DataModelConc.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram DataModelConc { + <> class Employee { + // TODO remove attribute mappings once <> is implemented + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + // TODO remove attribute mappings once <> is implemented + <> String managerName; + <> int depId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataClass.java new file mode 100644 index 00000000..2dfd0aa9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataClass.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.builder; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataModel/DataClassBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataModel/DataClassBuilder.java new file mode 100644 index 00000000..f9db617f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataModel/DataClassBuilder.java @@ -0,0 +1,20 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.builder.adapter.datamodel; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClassBuilder"}, template = "${}") +public class DataClassBuilder { + @Adapt(ref = {"DataClassBuilder.attribute"}, template = "${}") + private Object attribute; + + @Adapt(ref = {"DataClassBuilder.attribute"}, template = "${}") + public DataClassBuilder attribute(Object attribute) { + return null; + } + + @Adapt(ref = {"DataClassBuilder.build"}, template = "${}") + public DataClass build() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataModel/Mill.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataModel/Mill.java new file mode 100644 index 00000000..4c19fa67 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/builder/adapter/DataModel/Mill.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.builder.adapter.datamodel; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Mill"}, template = "${}") +public class Mill { + @Adapt(ref = {"Mill.dataClassBuilder"}, template = "${}") + public DataClassBuilder dataClassBuilder() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceConc.cd new file mode 100644 index 00000000..aeb2eee3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceConc.cd @@ -0,0 +1,6 @@ +classdiagram MicroserviceConc { + <> class UserService; + <> class OrderService; + <> class ProductService; + <> class InvoiceService; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceOut.cd new file mode 100644 index 00000000..a6f1b4b6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceOut.cd @@ -0,0 +1,31 @@ +import java.lang.String; + +classdiagram MicroserviceConc { + <> class UserService { + <> void sendToUserService(String message); + <> void sendToOrderService(String message); + <> void sendToProductService(String message); + <> void sendToInvoiceService(String message); + } + + <> class OrderService { + <> void sendToUserService(String message); + <> void sendToOrderService(String message); + <> void sendToProductService(String message); + <> void sendToInvoiceService(String message); + } + + <> class ProductService { + <> void sendToUserService(String message); + <> void sendToOrderService(String message); + <> void sendToProductService(String message); + <> void sendToInvoiceService(String message); + } + + <> class InvoiceService { + <> void sendToUserService(String message); + <> void sendToOrderService(String message); + <> void sendToProductService(String message); + <> void sendToInvoiceService(String message); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceRef.cd new file mode 100644 index 00000000..b8f04acb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/MicroserviceRef.cd @@ -0,0 +1,7 @@ +import java.lang.String; + +classdiagram MicroserviceRef { + class Microservice { + <> void sendToMicroservice(String message); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/adapter/Microservice/Microservice.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/adapter/Microservice/Microservice.java new file mode 100644 index 00000000..dcf36796 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/adapter/Microservice/Microservice.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.cross_references.adapter.microservice; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Microservice"}, template = "${}") +public class Microservice { + @Adapt(ref = {"Microservice.sendToMicroservice"}, template = "${}") + public void sendToMicroservice(String message) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/InvoiceService.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/InvoiceService.java new file mode 100644 index 00000000..2aefa7c8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/InvoiceService.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.cross_references; + +public class InvoiceService { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/OrderService.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/OrderService.java new file mode 100644 index 00000000..51a1017b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/OrderService.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.cross_references; + +public class OrderService { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/ProductService.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/ProductService.java new file mode 100644 index 00000000..0e43103b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/ProductService.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.cross_references; + +public class ProductService { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/UserService.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/UserService.java new file mode 100644 index 00000000..2ccb999d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/cross-references/concrete/UserService.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.cross_references; + +public class UserService { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/CRUDBackendOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/CRUDBackendOut.cd new file mode 100644 index 00000000..f436916f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/CRUDBackendOut.cd @@ -0,0 +1,80 @@ +import java.lang.String; +import java.util.List; +import java.util.Optional; + +classdiagram DomainModel { + + <> class Employee { + <> String id; + <> String taxId; + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } + + class HttpResponse; + + <> class DepartmentRepository { + <> Optional findByDepId(int depId); + List findAll(); + Department save(Department department); + <> void removeByDepId(int depId); + + } + + <> class EmployeeRepository { + <> Optional findById(String id); + <> Optional findByTaxId(String taxId); + List findAll(); + Employee save(Employee employee); + <> void removeById(String id); + <> void removeByTaxId(String taxId); + } + + <> class EmployeeController { + <> HttpResponse getById(String id); + <> HttpResponse getByTaxId(String taxId); + HttpResponse getAll(); + HttpResponse create(CreateEmployeeData data); + <> HttpResponse updateById(String id, UpdateEmployeeData data); + <> HttpResponse updateByTaxId(String taxId, UpdateEmployeeData data); + <> HttpResponse removeById(String id); + <> HttpResponse removeByTaxId(String taxId); + } + + <> class DepartmentController { + <> HttpResponse getByDepId(int depId); + HttpResponse getAll(); + HttpResponse create(CreateDepartmentData data); + <> HttpResponse updateByDepId(int depId, UpdateDepartmentData data); + <> HttpResponse removeByDepId(int depId); + } + + <> class CreateEmployeeData { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class UpdateEmployeeData { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class CreateDepartmentData { + <> String managerName; + } + + <> class UpdateDepartmentData { + <> String managerName; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/CRUDBackendRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/CRUDBackendRef.cd new file mode 100644 index 00000000..c31aa29a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/CRUDBackendRef.cd @@ -0,0 +1,35 @@ +import java.util.List; +import java.util.Optional; + +classdiagram CRUDBackendRef { + + class Entity { + any id; + any attribute; + } + + <> class EntityRepository { + <> Optional findById(any id); + List findAll(); + Entity save(Entity entity); + <> void removeById(any id); + } + + <> class EntityController { + <> HttpResponse getById(any id); + HttpResponse getAll(); + HttpResponse create(CreateEntityData data); + <> HttpResponse updateById(any id, UpdateEntityData data); + <> HttpResponse removeById(any id); + } + + <> class CreateEntityData { + <> any attribute; + } + + <> class UpdateEntityData { + <> any attribute; + } + + class HttpResponse; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/DomainModel.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/DomainModel.cd new file mode 100644 index 00000000..d6006970 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/crud-backend/DomainModel.cd @@ -0,0 +1,17 @@ +import java.lang.String; + + classdiagram DomainModel { + <> class Employee { + <> String id; + <> String taxId; + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } + } diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/DataModelConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/DataModelConc.cd new file mode 100644 index 00000000..6b9f3357 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/DataModelConc.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram DataModelConc { + <> class Employee { + // TODO remove attribute mappings once <> is implemented + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + // TODO remove attribute mappings once <> is implemented + <> String managerName; + <> int depId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/GetterOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/GetterOut.cd new file mode 100644 index 00000000..cc000b80 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/GetterOut.cd @@ -0,0 +1,23 @@ +import java.lang.String; + +classdiagram DataModelConc { + <> class Employee { + // TODO remove attribute mappings once <> is implemented + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + <> String getFirstName(); + <> String getLastName(); + <> int getNumber(); + <> int getSalary(); + } + + <> class Department { + // TODO remove attribute mappings once <> is implemented + <> String managerName; + <> int depId; + <> String getManagerName(); + <> int getDepId(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/GetterRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/GetterRef.cd new file mode 100644 index 00000000..0ef9d9b5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/GetterRef.cd @@ -0,0 +1,8 @@ + +classdiagram GetterRef { + class DataClass { + // TODO use <> here once it is implemented + any attribute; + <> any getAttribute(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/SetterOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/SetterOut.cd new file mode 100644 index 00000000..2fd430e7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/SetterOut.cd @@ -0,0 +1,23 @@ +import java.lang.String; + +classdiagram DataModelConc { + <> class Employee { + // TODO remove attribute mappings once <> is implemented + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + <> void setFirstName(String firstName); + <> void setLastName(String lastName); + <> void setNumber(int number); + <> void setSalary(int salary); + } + + <> class Department { + // TODO remove attribute mappings once <> is implemented + <> String managerName; + <> int depId; + <> void setManagerName(String managerName); + <> void setDepId(int depId); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/SetterRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/SetterRef.cd new file mode 100644 index 00000000..2848a165 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/SetterRef.cd @@ -0,0 +1,8 @@ + +classdiagram SetterRef { + class DataClass { + // TODO use <> here once it is implemented + any attribute; + <> void setAttribute(any attribute); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/adapter/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/adapter/DataClass.java new file mode 100644 index 00000000..c4fdf660 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/getter-setter/adapter/DataClass.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.getter-setter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + + @Adapt(ref = {"DataClass.getAttribute"}, template = "${}") + public Object getAttribute() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/EmptyConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/EmptyConc.cd new file mode 100644 index 00000000..0b713592 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/EmptyConc.cd @@ -0,0 +1,3 @@ +classdiagram EmptyConc { + +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/EmptyConcOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/EmptyConcOut.cd new file mode 100644 index 00000000..947d2def --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/EmptyConcOut.cd @@ -0,0 +1,1455 @@ +import java.lang.String; +import java.lang.Long; +import java.lang.Integer; +import java.lang.Boolean; +import java.util.List; +import java.util.Optional; +import java.time.ZonedDateTime; +/* (c) https://github.com/MontiCore/monticore */ +/* adapted and customized by (c) MaCoCo, ein RWTH Aachen projekt */ + +// No packages supported yet in CD completion! +//package de.macoco.be.domain; + + + +classdiagram EmptyConc { + + //---------------------------------------------- + //--------------- PERSONAL --------------------- + //---------------------------------------------- + + /** + * Person ist die Zentrale Klasse des Personal-Komplexes. + **/ + class Person { + String vorname; + Optional vorsatzwort; + String nachname; + String kuerzel; // Abgeleitet analog wie bei SAP: 3 Vor- 3 Nachnamenbuchstaben + Optional personalnummer; + Optional gebDatum; + Optional beschBeginn; + Optional beschEnde; + List kommentar; + boolean istAktiv; + boolean istStundenzettelpflichtig = false; + Optional stundenzettelpflichtigVon; + Optional stundenzettelpflichtigBis; // TODO: brauchen wir nicht mehr + Optional ueberStundenUebertrag; + boolean darfWochenendarbeiten; + // Start WZL: + Optional hoechstbeschaeftigungBis; + List staatsangehoerigkeiten; + Optional telefonnummer; + Optional arbeitserlaubnisBis; + boolean extern; + Optional titel; + Optional rufname; + Optional adresse; + Optional email; + Optional geburtsname; + Optional geschlecht; + Optional lBVNummer; + Optional entfristung; + <> + Optional qualifikationstitel; + boolean speicherungGewuenscht = false; + <> + Optional urlaubskommentar; + Optional aenderungsdatum; + } + + association [1] Person -> (anstellungsarten) Anstellungsart [*]; + association [*] Person -> (vorgesetzte) Person [*]; + association [1] Person -> (notiz) Freitext [0..1]; + + //Für Studentenzettel: + association [1] Person -> (jahresurlaubstage) Jahresurlaub [*]; + association [1] Person -> (zusatzurlaubstage) Sonderurlaub [*]; + association [1] Person -> (stundenzettel) Stundenzettel [*]; + association [1] Person -> (abwesenheiten) Abwesenheit [*]; + + <> + association [1] Person -> (user) MacocoUser [0..1]; + + + //---------------------------------------------- + // Person -> *Anstellungsart + + class Anstellungsart { + BeschaeftigungsArt beschArt; + } + + association [1] Anstellungsart -> (vertraege) Vertrag [*]; + association [1] Anstellungsart -> (anstellungsformen) Anstellungsform [*]; + + enum BeschaeftigungsArt { + WiMi("Wissenschaftliche/r Mitarbeiter/in (WiMi)"), + HiWi("Hilfskraft (HiWi)"), + BTV("Beschäftigte/r in Technik und Verwaltung (BTV)"), + BEAMTE("Beamte/r"), + AZUBI("Azubi/ne"), + PLAN; + } + + //---------------------------------------------- + // Person -> *Anstellungsart -> *Vertrag + // eine direkte Verbindung zwischen Person und Vertrag existiert nicht. + + class Vertrag { // spiegelt den Vertrag der Hochschule wieder + ZonedDateTime vertragsBeginn; + ZonedDateTime vertragsEnde; + boolean aenderungsvertrag; + /String vertragsStatus; // Aktiv, Planung, Abgelaufen // TODO: change String to enum + List kommentar; + ZahlenWert planUmfang; + Optional arbeitsstundenProWoche; //Stundenzettel + boolean kuendigungsschutz; // WZL + Optional vertragsgrundlage; // WZL / TODO: change to enum + Optional aktion; // WZL / TODO: change to enum + Optional aktionsDatum; // WZL / TODO: change to enum + } + + association [1] Vertrag -> (kostenstellen) Kostenstelle [*]; + + //---------------------------------------------- + // Person -> *Anstellungsart -> Anstellungsform + + /** + * Beschreibt alle wesentlichen Merkmale einer Anstellung. + * Entgeltgruppe, Stufe, etc. + **/ + class Anstellungsform { + boolean erstanstellung; + Optional entgeltGruppe; // { TVL-13} Für Hiwis: {SHK (default), WHB, WHK} + Optional erfahrungsStufe; // 1- 6 ; sowie Spezialfälle (TVL Ü+); + boolean unbefristet; + ZonedDateTime anstellungVon; + Optional anstellungBis; + long gehaltCent; // evtl. Schätzgehalt in Zukunft + boolean istEigenerReferenzwert; + List kommentar; + Optional berufsgruppe; // WZL / TODO: change to enum + Optional aktion; // WZL / TODO: change to enum + Optional aktionsDatum; // WZL / TODO: change to enum + } + + //---------------------------------------------- + // Person -> *Anstellungsart -> *Vertrag -> *Kostenstelle + + class Kostenstelle { + ZonedDateTime verbuchungsBeginn; + ZonedDateTime verbuchungsEnde; + KostenstelleBezeichnung bezeichnung; + List kommentar; + ZahlenWert beschaeftigungsUmfang; + boolean buchungenNeuErzeugen; + boolean buchungenLoeschen; + boolean gesperrt; + } + + enum KostenstelleBezeichnung { + NONE(""), + AN_INSTITUT("An-Institut"), + GMBH("GmbH"), + ANDERER_LEHRSTUHL, + ANDERES_INSTITUT, + VORUEBERGEHEND_ABWESEND("Vorübergehend Abwesend"), + ANDERE_FINANZIERUNG; + } + + enum Zuweisungsart { + KONTO("Kontenzuweisung"), + STELLENZUWEISUNG, + PLANSTELLE, + SONSTIGES; + } + + association [*] Kostenstelle -> (personalBudget) Budget [0..1]; + association Kostenstelle -> (stellenzuweisung) Stellenzuweisung [0..1]; + association Kostenstelle -> (planstelle) Planstelle [0..1]; + <> + association [0..1] Kostenstelle -> (personalBuchungen) Buchung [*]; + + // Person -> *Anstellungsart -> *Vertrag -> *Kostenstelle -> ?Planstelle + + class Planstelle { + String bezeichnung; + Optional minEntgeltgruppe; + Optional minEntgeltstufe; + Optional maxEntgeltgruppe; + Optional maxEntgeltstufe; + Optional verfuegbarVon; + Optional verfuegbarBis; + Optional kommentar; + ZahlenWert planUmfang; + boolean aktiv; + } + + association Planstelle -> (konto) Konto [0..1]; + + //---------------------------------------------- + + /** + * Repräsentiert verschiedene Arten von monetären Haben + * @attribute wert ist jeweils unterschiedlich zu interpretieren: + * EURO heisst, der wert ist in Cent angegeben + * STUNDE heisst: er ist in Stunden angegeben + * (und das obwohl jedes Monat, Jahr unterschiedliche Stundenzahlen hat) + * PROZENT heisst er ist zwischen 0 und 100 angegeben (100%) + * NONE + **/ + class ZahlenWert { + ZahlenTyp zahlenTyp; + long wert; + } + + enum ZahlenTyp {EURO, STUNDE, PROZENT, NONE; } + + //---------------------------------------------- + //------------- Alerts ------------------------- + //---------------------------------------------- + + // Konto -> *MailAlert + + class MailAlert { + String name; + <> + String alertCondition; + <> + String mailBody; + int repetitions; + ZonedDateTime firstAlert; + ZonedDateTime lastAlert; + + Optional receipients; + Optional ccs; + Optional bccs; + + // extra Attribute zur Speicherung einiger Werte (vom System unbenutzt) + long a1; + long a2; + boolean flag1; + boolean flag2; + String s1; + String s2; + } + + //---------------------------------------------- + // ============ Finanz-Datenstruktur =========== + //---------------------------------------------- + + /** + * Hauptklasse; das Konto, von der alle anderen abhängen. + * Hat mehrere Varianten (Subklassen) + */ + abstract class Konto { + String name; + Optional pspElement; + Optional kontotyp; // unterschiedlich je nach Kontoart + Optional sapDatum; + Optional internesAktenzeichen; + boolean istPlanKonto; + boolean istVerbuchungsKonto; + boolean istAktiv; + Optional vergabeVerordnung; + Optional farbe; + Optional aenderungsdatum; + /Optional optStartDatum; + /Optional optEndDatum; + /Geschaeftsvorgang gueltigerGeschaeftsvorgang; + } + + <> + association [1] Konto (konto) -> (gesamtBudget) Budget [0..1]; + association [1] Konto -> (kommentare) Freitext [*]; + association [1] Konto -> (notiz) Freitext [0..1]; + association [1] Konto <-> MailAlert [*]; + association [1] Konto -> (abgleichsKonto) ExternKonto [0..1]; + + abstract class ErweitertesKonto extends Konto { + Optional startDatum; + Optional endDatum; + Optional bewilligungsDatum; // Bewilligungsdatum ist im Industrieprojekt nur mandatory, wenn kein Sammelkonto + Optional verlaengertBisDatum; + Optional aufstockungsDatum; + Optional foerderquote; + // human names: "Aktenzeichen (RWTH)" + // Drittmittel: "Förderkennzeichen", + Optional aktenzeichen; + Optional finanzierteKomplettsumme; + // human names: + // Drittmittel: "Aktenzeichen (Fördergeber)", + // Industrieprojekt: "Bestellnummer", + // Sonstiges: "Bestellnummer/Aktenzeichen (Fördergeber)" + Optional referenzSponsor; + boolean hatProgrammpauschale; + } + + + class Drittmittelprojekt extends ErweitertesKonto { + } + + association [*] Drittmittelprojekt -> (fachlicherVerantwortlicher) Person [*]; + + class Haushaltskonto extends Konto { + Optional startDatum; + Optional endDatum; + } + + class Industrieprojekt extends ErweitertesKonto { + Optional auftraggeber; + boolean istSammelkonto; + Optional mehrwertsteuer; + } + + association [*] Industrieprojekt -> (fachlicherVerantwortlicher) Person [*]; + + class Sonstiges extends ErweitertesKonto { + Optional auftraggeber; + Geschaeftsvorgang geschaeftsvorgang; + Optional mehrwertsteuer; + boolean istSammelkonto; + } + + + enum AbrechnungsInterval { + GESAMTE_PROJEKTLAUFZEIT("Gesamte Projektlaufzeit"), + JAEHRLICH("Jährlich"), + HALBJAEHRLICH("Halbjährlich"), + QUARTALSWEISE("Quartalsweise"), + MONATLICH("Monatlich"); + } + + association [1] Konto -> OrganigrammKonto [*]; + + class OrganigrammKonto { + Optional von; + Optional bis; + Optional umfang; + } + + association [*] OrganigrammKonto -> Organigramm [1]; + + class ZeitlicheVerbuchungskontoZuordnung { + Optional programmPauschale; + Optional gemeinkostensatz; + Optional startDate; + Optional endDate; + boolean istHauptVerbuchungskonto; + } + + association [1] ErweitertesKonto -> (verbuchungskontoZuordnung) ZeitlicheVerbuchungskontoZuordnung [*]; + association [*] ZeitlicheVerbuchungskontoZuordnung -> (verbuchungskonto) Konto [0..1]; + + //---------------------------------------------- + + // ------ Budget ------ + // Konto -> 1Budget + // Konto -> 1Budget -> *Budget + // Konto -> 1Budget -> *Budget -> *Budget + + /** + * Ein Budget erlaubt es ein Konto nach verschiedenen + * Strukturierungskriterien zu zerlegen. Budgets sind nur auf drei Ebenen + * erlaubt: 1 Hauptbudget, * OberBudgets, ** Unterbudgets. + * Die Budget-Struktur stellt einen Baum der maximalen Tiefe 3 dar. + */ + + class Budget { + String typ; // name + Optional startDatum; + Optional endDatum; + Optional kommentar; + Optional budgetRahmenCent; // wird im Gesamtbudget als Bewilligungssumme interpretiert + Optional eigenAnteilCent; + List jahresBudgets; + boolean proportionaleVerteilung; + boolean forOverheads; // das erste Budget (gesamtBudget.getUnterbudgets[0]) + Optional budgetKategorie; + /int budgetDepth; + } + + association [*] Budget -> Konto [1]; + association [0..1] Budget (elternBudget) <-> (unterBudget) Budget [*]; + association [1] Budget (budget) <-> (buchungseintrag) Buchungseintrag [*]; + association [1] Budget (budget) <-> (stellenzuweisung) Stellenzuweisung [*]; + <> + association [0..1] Budget -> Organigramm [0..1]; + + //---------------------------------------------- + + // ------ Buchungseinträge ------ + // Konto -> 1Budget (->*Budget)* -> *Buchung + + /** + * Buchungen aller Arten: gemeinsame Oberklasse + */ + abstract class Buchungseintrag { + ZonedDateTime datum; + Optional buchungseintragText; // human name: Buchung: "Buchungstext", Rechnung: "Rechnungsgrund", Mittelabruf: "Anmerkung", Mittelzuweisung: "Grund" + long betragCent; // human name: "Betrag", Rechnung: "Rechnungsbetrag", Konten mit Programmpauschale: "Rechnungsbetrag (inkl Pauschale)" + boolean istAktiv; + Optional aenderungsdatum; + /Geschaeftsvorgang gueltigerGeschaeftsvorgang; + /String lfdeNummer; + } + + class Buchung extends Buchungseintrag { + ZonedDateTime belegdatum; + String zahlungsgrund; //Belegpositionstext + Optional kreditorDebitor; + List sachkonto; + Optional buchungsdatum; + BuchungsStatus status; + List belegnummern; + List auftragsnummer; + Optional bereich; + Optional projekt; + Optional geschaeftsjahr; + Optional steuer; + Optional skonto; + Optional gez_Skonto; + Optional erfasser; + Optional belegart; + Optional referenz; + Optional stornonummer; + Optional stkz; + Optional erfassungsdatum; + Optional ausgleichsdatum; + long betragCentOriginal; // Bei Abweichungen von BetragCent kann nun eine Absicht ueberprueft werden + boolean budgetChangedManually; // s.o. + Optional kostenarten; + /Optional bezugsdatumJahr; + } + + class Rechnungsstellung extends Buchungseintrag { + Optional rechnungsdatum; + Optional rechnungsnummer; + RechnungsstellungStatus status; + List belegnummern; + Optional gemeinkosten; + Optional honorierung; + Optional projekt; + Optional debitor; + Optional mehrwertsteuer; + Optional zahlungsziel; + } + + class Mittelzuweisung extends Buchungseintrag { + Optional verfallDatum; + Optional kennung; + MittelzuweisungStatus status; + } + + class Mittelabruf extends Buchungseintrag { + ZonedDateTime abrufdatum; + MittelabrufStatus status; + String zeitraum; + Optional pauschaleManuell; // Präsent, falls die Pauschale manuell geändert wurde + } + + // ------ Stellenzuweisung ------ + + // Konto -> 1Budget (->*Budget)* -> *Stellenzuweisung + + class Stellenzuweisung { + ZonedDateTime erstellDatum; // aktuelles Datum + Optional startDatum; // Datum, zu welchem die Stelle beginnt + Optional endDatum; // Datum, zu welchem die Stelle endet + long wert; + String kennung; + StellenzuweisungStatus status; + ZahlenWert stellenumfang; + boolean istAktiv; + Optional aenderungsdatum; + } + + enum Geschaeftsvorgang { + NONE, + MITTELABRUF, + ZUWEISUNG, + RECHNUNG; + } + + enum BuchungsStatus { + EINGEREICHT, + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG, + FEHLERHAFT; + } + + enum RechnungsstellungStatus { + OFFENE_RECHNUNG, + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG; + } + + enum MittelzuweisungStatus { + BESCHEID_ERHALTEN ("Bescheid erhalten"), + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG, + FEHLERHAFT; + } + + enum MittelabrufStatus { + ABGERUFEN, + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG, + EINGEREICHT; + } + + enum StellenzuweisungStatus { + BESETZT, + TEILBESETZT, + UNBESETZT, + PLANUNG; + } + + enum VertragStatus { + AKTIV, + PLANUNG, + ABGELAUFEN; + } + + //---------------------------------------------- + // =============== SAPii =============== + //---------------------------------------------- + + class SAPVerbindung { + SAPverbindungsStatus status; + } + + class Anfrage { + String ikz; + String bezeichner; + boolean letzteAnfrageErfolgreich; + } + + enum SAPverbindungsStatus { + VERBUNDEN, + GETRENNT, + PROBLEM; + } + + enum SAPimportMode { + DEFAULT, + F1KONTO; + } + + association [1] SAPVerbindung -> (authorizedBy) MacocoUser [*]; + association [1] MacocoUser -> (anfragen) Anfrage [*]; + association [1] Anfrage -> (filteredKonten) PSPImportFilter [*]; + + //---------------------------------------------- + + class AbgleichsBuchung extends Buchung { + AbgleichsStatus abgleichsStatus; + Optional abgleichKommentar; + String positionsNummer; + boolean deactivated; + } + + enum AbgleichsStatus { + ABGESCHLOSSEN, // Der Abgleich dieser Buchung ist Abgeschlossen + TEILWEISE_ABGESCHLOSSEN, // Der Abgleich wurde schon Bearbeitet es fehlen jedoch Summen + ABWEICHEND, // Der Abgleich ist Vollständig mit Abweichenden Summen + OFFEN, // Der Abgleich wurde noch nicht gesetzt + VORGESCHLAGEN; // Der Abgleich wurde vom System vorgeschlagen + } + + class AbgleichsGruppe { + String bezeichnung; + } + <> + association AbgleichsGruppe -> (buchungen) Buchung [*]; + <> + association AbgleichsGruppe -> (abgleichsBuchungen) AbgleichsBuchung [*]; + <> + association AbgleichsGruppe -> (konto) Konto [1]; + + // ------ Freitext ------ + + class Freitext { + Optional erstellDatum; + Optional bearbeitetDatum; + <> + Optional text; + } + + class Legende { + LegendeTyp type; + String expression; + <> + String description; + } + + enum LegendeTyp { + KONTO_FARBE; + } + + //---------------------------------------------- + // =============== Projekte =============== + //---------------------------------------------- + + abstract class Projekt { + String name; + String kuerzel; + Optional minPM; + Optional maxPM; + Optional finanzPM; + Optional laufzeitVon; + Optional laufzeitBis; + Optional kommentar; + Optional foerdergeberAZ; + ProjektStatus status; + boolean istAktiv; + boolean lockedForStundenzettel; // Das gesamte Projekt ist gesperrt + List lockedMonths; // Stellen die Ausnahme aus der Lock-Regelung für das gesamte Projekt dar + Optional aenderungsdatum; + } + + association [1] Projekt -> Aufwand [*]; + association [1] Projekt -> Arbeitspaket [*]; + association [1] Projekt -> Anstellung [*]; + association [*] Projekt -> Konto [0..1]; + association [*] Projekt -> (hauptverantwortlich) Person [*]; + association [1] Projekt -> (notiz) Freitext [0..1]; + + class Auftragsprojekt extends Projekt { + Optional nummer; + ProjektTyp typ; + Optional regelung; + } + + class Organisation extends Projekt { + } + + class Lehre extends Projekt { + Optional stunden; + } + + class Arbeitspaket { + String nummer; // könnte auch 1.1 sein + String name; + Optional beschreibung; + Optional beginnDatum; + Optional endeDatum; + Optional pMs; + Optional stunden; + Optional aenderungsdatum; + } + + class Aufwand { + /long pM; + ZahlenWert umfang; + ZonedDateTime laufzeitVon; + ZonedDateTime laufzeitBis; + Optional aenderungsdatum; + } + + association [*] Aufwand -> Person [0..1]; + + class Anstellung { + Optional bezeichnung; + ZahlenWert umfang; + ZonedDateTime von; + ZonedDateTime bis; + boolean verfaellt; + Optional min; + Optional max; + /long pM; + BeschaeftigungsArt beschaeftigungsArt; + } + + association [*] Anstellung -> Person [0..1]; + + class Stellentyp { + Optional entgeltgruppe; + Optional entgeltstufe; + } + + enum ProjektStatus { + IN_DEFINITION, + BEANTRAGT, + GENEHMIGT, + ABGELEHNT, + LAUFEND, + FACHLICH_ABGESCHLOSSEN("Fachlich abgeschlossen"), + ENDBERICHT_EINGEREICHT("Endbericht eingereicht"), + INPRUEFUNG("In Prüfung"), + GEPRUEFT("Geprüft"), + ABGERECHNET, + ABGESCHLOSSEN, + ARCHIVIERT; + } + + enum ProjektArt { + AUFTRAG, + ORGANISATION, + LEHRE, + NONE; + } + + enum ProjektTyp { + HOHEITLICH, + INDUSTRIE, + UNIVERSITAET("Universität"), + SONSTIGE; + } + + enum Vergabeverordnung { + VOL_A ("VOL/A"), + VOB_A ("VOB/A"), + UVGO_NRW ("UVgO NRW"), + UVGO_BUND ("UVgO Bund"), + NONE (" "); + } + + //---------------------------------------------- + // =============== Stundenzettel =============== + //---------------------------------------------- + + class Stundenzettel { + StundenzettelStatus status; + ZonedDateTime zeit; + Optional abgegebenVonUserId; + Optional abgabeDatum; + } + + association [1] Stundenzettel -> (eintraege) StundenzettelEintrag [*]; + + class StundenzettelEintrag { + ZonedDateTime uhrzeitVon; + Optional uhrzeitBis; + Optional beschreibung; + /long stunden; + StundenzettelProjekt pauseOderSonstiges; + } + + association [*] StundenzettelEintrag -> Projekt [0..1]; + association [*] StundenzettelEintrag -> Arbeitspaket [0..1]; + + class StundenzettelHistorieEintrag { + ZonedDateTime bearbeitungsdatum; + Optional editorId; + Optional editedBy; + String monat; + int jahr; + String changeOperation; + } + + enum StundenzettelChangeOperation { + INVALID, + ABGEGEBEN("Abgegeben"), + INTERN_GEPRUEFT("Intern Geprüft"), + INTERN_ABGESCHLOSSEN("Intern Abgeschlossen"); + } + + association [1] Person -> StundenzettelHistorieEintrag [*]; + + class Abwesenheit { + Abwesenheitsgrund grund; + ZonedDateTime datumVon; + ZonedDateTime datumBis; + Optional kommentar; + Optional uhrzeitVon; + Optional uhrzeitBis; + Optional minutenProTag; + /long tageGesamt; + } + + class Dienstreise extends Abwesenheit { + Optional reisenummer; + Optional dienstreisegrund; + Optional reiseziel; + Optional gesamtkosten; + Optional status; + Optional statusText; + } + + association [*] Abwesenheit -> Konto [0..1]; + + association [1] Abwesenheit -> UrlaubsGenehmigung [0..1]; + + association [*] Dienstreise -> Budget [0..1]; + + class Arbeitstage { + ZonedDateTime guiltigAb; + ZonedDateTime guiltigBis; + List wochenTage; // 0..4 (Montag-Freitag) + } + + association [1] Person -> Arbeitstage [*]; + + class Jahresurlaub { + int jahr; + long tageAnzahl; + Optional stundenAnzahl; + boolean istManuellerEintrag; + } + + class UrlaubsGenehmigung { + Optional beantragt; + Optional genehmigt; + Optional geprueft; + Optional storniert; + Urlaubssstatus status; + Stornierungsstatus stornierungsStatus; + } + + class Sonderurlaub { // Für WZL: Zusatzurlaub umbenennen in Sonderurlaub und neues Feld anzahl + ZonedDateTime datumVon; + ZonedDateTime datumBis; + long anzahl; + } + + class UrlaubEinstellungen { + boolean urlaubsanspruchInStunden; + } + + enum Abwesenheitsgrund { + U_URLAUB, + D_DIENSTREISE, + S_SONSTIGE; + /* + Z_ZUSATZURLAUB, + A_ARBEITSUNFALL, + AB_ABGELTUNG, + E_ELTERNZEIT, + F_FREISTELLUNG, + K_KRANKHEIT, + KU_KUR, + S_SONSTIGES;*/ + } + + enum Urlaubssstatus { + NONE, + BEANTRAGT, + GEPRUEFT("Geprüft"), + PRUEFUNG_ABGELEHNT("Prüfung abgelehnt"), + GENEHMIGT, + GENEHMIGUNG_ABGELEHNT; + } + + enum Stornierungsstatus { + NONE, + VERBOTEN, + STORNIERUNGSANTRAG, + STORNIERT; + } + + enum Abrechnungsstatus { + OFFEN, + ABZURECHNEN, + ABGERECHNET, + STORNIERT; + } + + enum StundenzettelStatus { + IN_ERFASSUNG, + INTERN_INPRUEFUNG("Intern in Prüfung"), + INTERN_ABGESCHLOSSEN, + DRITTMITTELABTEILUNG_INPRUEFUNG("Drittmittelabteilung in Prüfung"), + DRITTMITTELABTEILUNG_ABGESCHLOSSEN, + ENDBERICHT_EINGEREICHT, + GEPRUEFT("Geprüft"), + FOERDERGEBER_ABGESCHLOSSEN("Fördergeber abgeschlossen"), + ABGESCHLOSSEN; + } + + enum StundenzettelProjekt { + PAUSE, + SONSTIGE, + NONE; + } + + association [*] F1Konto -> (institut) Institut [0..1]; + + class ExternKonto extends Konto { + Optional startDatum; + Optional endDatum; + Optional importVermerk; + AbgleichType abgleichVerhalten; + } + + enum AbgleichType { + IGNORE, + MANUEL, + AUTO_COPY; + } + + association [*] ExternKonto -> (institut) Institut [0..1]; + + class PSPImportFilter{ + boolean importThisPSPelement; + boolean importBookings; + String name; + String pspElement; + Optional startDate; + } + + association [*] PSPImportFilter -> (targetPSP) Konto [0..1]; + + //---------------------------------------------- + // Reports + + class EventReport { + ZonedDateTime eventStart; + Optional eventEnd; + /EventStatus status; + String message; // BuchungImport: Nutzer 'netz' hat die Datei '.....' importiert + } + + class EventReportEntry { + EventType type; + Optional targetId; // click -> navigate to id + Optional count; + <> + String message; // Zeile 4 Fehler ...... Zeile 5 ..... + EventStatus status; + } + association [1] EventReport -> (entries) EventReportEntry [*]; + + class ImportReport extends EventReport { + } + + class ImportReportEntry extends EventReportEntry { + } + + + class PersonalExportReport extends EventReport { + Long dataId; + + ZonedDateTime datenStart; + ZonedDateTime datenEnd; + Long countData; + /Long countError; + EventStatus exportStatus; + } + + class PersonalExportReportEntry extends EventReportEntry { + Optional vorsatzwort; + String vorname; + String nachname; + BeschaeftigungsArt beschaeftigungsArt; + ZonedDateTime monat; + } + + enum EventStatus { + // Import + SUCCESS("erfolgreich"), + ERROR("fehlerhaft"), + INFO("erfolgreich"), // success + special + CREATED("erstellt"), + UPDATED("upgedated"), + IGNORED("ignoriert"), + IN_PROGRESS("in bearbeitung"), + + // Personal Export + SENT("verschickt"), + CORRECT("sachlich korrekt"); + } + + enum EventType { + IMPORT_KONTO("Konten"), + IMPORT_BUCHUNG("Buchungs"), + IMPORT_PERSON("Personen"), + IMPORT_INSTITUT("Instituts"), + IMPORT_NUTZER("Nutzer"), + IMPORT_VERTRAG("Vertrags"), + EXPORT_FINANZEN_KONTO("Konten"), + EXPORT_FINANZEN_PLANSTELLE("Planstellen"), + EXPORT_FINANZEN_VERTRAG("Vertragabdeckungs"); + } + + + //---------------------------------------------- + // =============== Fakultät 1 DS =============== + //---------------------------------------------- + + // Institut + <> + class Institut { + String institutsKennZiffer; + String institutsName; + String professorName; + boolean lokalInstitut; + } + + // Fachgruppe + enum Fachgruppe { + MATHEMATIK, + INFORMATIK, + PHYSIK, + CHEMIE, + BIOLOGIE; + } + + //SAP-Abschöpfung von Geschäftsjahr + class GeschaeftsjahrSAPAbschoepfung { + String fachgruppe; + String institutsKennZiffer; + String institutsName; + String pspElement; + long gesamtSAPAbschoepfung; + } + + class SAPAbschoepfung { + Long betragCent; + ZonedDateTime year; + } + + association [1] GeschaeftsjahrSAPAbschoepfung -> (sapAbschoepfung) SAPAbschoepfung [*]; + + + // Konto + class F1Konto extends Haushaltskonto { + long originalBudgetCent; + long sonstigeZuweisungenCent; + long resteCent; + long aktuellerKontostandCent; + long kontoRahmenCent; + KommunikationsStatus kommunikationsStatus; + StrafsteuerStatus strafsteuerStatus; + long strafsteuerBasisCent; + long strafsteuerSAPCent; + long strafsteuerCent; + long strafsteuerBerechnungsGrundlageCent; + Optional kontoStandBeginnGeschaeftsjahr; + } + + // Konto -> 1Budget (->*Budget)* -> *Buchung -> ?Begruendung + + // Buchungen werden hier noch erweitert: + association [1] Buchung -> (begruendung) Begruendung [0..1]; + + class Begruendung { + String text; + ZonedDateTime erstellung; + Optional letzteBearbeitung; + Optional geschaeftsjahr; + } + + association [1] Begruendung -> (akzeptanz) Akzeptanz [0..1]; + //association [1] Begruendung -> (geschaeftsjahr) StrafsteuerGeschaeftsjahr [0..1]; + + //---------------------------------------------- + + // Konto -> 1Budget (->*Budget)* -> *Buchung -> ?Begruendung -> ?Akzeptanz + + class Akzeptanz { + Akzeptanzstatus akzeptanzstatus; + Optional teilbetragCent; + Optional bearbeitung; + boolean istAktiv; + Optional kommentar; + } + + //---------------------------------------------- + + enum KommunikationsStatus { + KOM_STAT_NO_ACTION_NEEDED, // Kein Handlungsbedarf + KOM_STAT_BEGR_FEHLT, // Begründung fehlt + KOM_STAT_ANTW_FEHLT, // Antwort fehlt + KOM_STAT_ANTW_CHANGED, // Buchung oder Begründung wurden geändert + KOM_STAT_BEGR_CHANGED, // Antwort wurde geändert + KOM_STAT_ANTW_BEGR_CHANGED, // Antwort und Begründung wurde geändert + KOM_STAT_ANTW_FEHLT_CHANGED; // Antwort Fehlt, andere Antwort wurde geändert + } + + enum StrafsteuerStatus { + NONE, + SOME, + ALL; + } + + enum Akzeptanzstatus { + OFFEN, + OK, + TEILOK, + NOTOK; + } + + class StrafsteuerGeschaeftsjahr { + ZonedDateTime jahr; + boolean current; + } + + class StrafsteuerSperrDatum { + ZonedDateTime sperrDatum; + } + + //---------------------------------------------- + // =============== MacocoUser =============== + //---------------------------------------------- + + class MacocoUser { + <> + String username; + Optional encodedPassword; + String passwordSaltBase64; + ZonedDateTime registrationDate; + Optional initials; + MacocoUserActivationStatus activated; + boolean enabled; + <> + String email; + boolean authentifiziert; + Optional timID; + Optional sapAccessToken; + Optional sapRefreshToKen; + + } + + association [*] MacocoUser -> (institute) Institut [*]; + + enum MacocoUserActivationStatus { + AKTIVIERT, + MAIL_NICHT_GESENDET("Mail nicht gesendet"), + MAIL_FEHLERHAFT("Mail fehlerhaft"), + MAIL_GESENDET("Mail gesendet"); + } + + // Favoriten + class Favorite { + String title; + String url; + } + + association [1] MacocoUser -> (favorite) Favorite [*]; + + class RoleAssignment { + } + + association [*] RoleAssignment -> (user) MacocoUser [1]; + association [*] RoleAssignment -> (accessPolicy) AccessPolicy [1]; + + class AccessPolicy { + String roleName; + Optional objId; + } + + class GroupedAccessPolicy { + String name; + } + + association [*] GroupedAccessPolicy -> (user) MacocoUser [*]; + association [1] GroupedAccessPolicy -> (policy) AccessPolicy [*]; + + //---------------------------------------------- + // =============== Fristen =============== + //---------------------------------------------- + + class Frist { + String beschreibung; + Friststatus status; + Fristobjekt fristobjekt; + ZonedDateTime faelligkeitsdatum; + Optional notiz; + Boolean manuellerEintrag; + Long bezugsobjektId; + String bezugsobjektName; + Optional aenderungsdatum; + boolean istAktiv; + } + + association [*] Frist -> (zuweisung) Person [0..1]; + + association [1] Person -> Frist [*]; + association [1] Konto -> Frist [*]; + association [1] Projekt -> Frist [*]; + association [1] Mittelabruf -> Frist [0..1]; + association [1] Rechnungsstellung -> Frist [0..1]; + association [1] Vertrag -> Frist [0..1]; + association [1] Zulage -> Frist [0..1]; + + enum Friststatus { + PLANUNG, + IN_BEARBEITUNG, + ERLEDIGT; + } + + enum Fristobjekt { + PERSON, + KONTO, + VERTRAGSVERLAENGERUNG("Vertragsverlängerung"), + ZULAGE, + RECHNUNG, + MITTELABRUF, + PROJEKT; + } + + class DefaultFristen { + ZonedDateTime finanzenStartDatum; + Long finanzenFristinWeeks; + ZonedDateTime personalStartDatum; + Long personalFristinWeeks; + } + + //---------------------------------------------- + // ============= Einstellungen ================= + //---------------------------------------------- + + class Setting { + String identifier; + } + + class InstanzSetting extends Setting { + String value; + } + + class UISetting extends Setting { + String seite; + } + + association [0..1] MacocoUser -> (setting) UISetting [*]; + + class FilterSetting extends UISetting { + List filterValues; + } + + class TableSetting extends UISetting { + TableIdentifierTyp typ; + boolean zeigeInaktive; + int zeilenLimit; + Optional gruppiereNach; + Optional sortiereNach; + Optional sortiereDir; + boolean isShowColor; + } + + association [1] TableSetting -> (spalten) TableSettingSpalten [*]; + + enum TableIdentifierTyp { + DEFAULT, + USER, + INSTITUT; + } + + class TableSettingSpalten { + String name; + boolean istAktiv; + long breite; + } + + class ErweiterbareListen{ + String bereich; + String feldbezeichnung; + List inhalte; + } + + //---------------------------------------------- + // =============== Card Settings =============== + //---------------------------------------------- + + class CardSetting { + String page; + String identifier; + <> + String configuration; + } + + association [*] CardSetting -> (benutzer) MacocoUser [0..1]; + + //---------------------------------------------- + // =============== WZL =============== + //---------------------------------------------- + + class Elternzeit { // WZL + Optional von; + Optional bis; + ZahlenWert umfang; + } + + class Nebentaetigkeit { // WZL + Optional von; + Optional bis; + ZahlenWert umfang; + Optional arbeitgeber; + Optional taetigkeitsbereich; + } + + class Zulage { // WZL + Optional pspelement; + Optional bezeichnung; + ZonedDateTime von; + ZonedDateTime bis; + Long arbeitgeberbrutto; + Long arbeitnehmerbrutto; + } + + // WZL + enum Geschlecht { NONE(""), WEIBLICH("weiblich"), MAENNLICH("männlich"), DIVERS("divers"); } + + // WZL + // -> DataSource + enum Titel { NONE(""), E1("Apl. Prof. Dr.") , E2("B.Sc.") , E3("Dipl.-Inform.") , E4("Dipl.-Ing."), E5("Dipl.-Ing. (FH)"), + E6("Dipl.-Ing. (RUS)"), E7("Dipl.-Kff.") , E8("Dipl.-Math."), E9("Dipl.-Phys.") , E10("Dipl.-Wirt. Ing.") , E11("Dr.") , E12("Dr. phil."), + E13("Dr. rer. nat."), E14("Dr. rer. pol."), E15("Dr.-Ing."), E16("M.A.") , E17("M.Eng."), E18("M.Sc."), E19("PhD"), E20("Prof. Dr.-Ing."); } + + // WZL + // -> DataSource + enum Berufsgruppe { NONE(""), E1("Akademischer Direktor"), E2("Angestellter in der DV"), E3("Auszubildende/r"), E4("Bibl.-Beschäftiger"), + E5("Elektriker"), E6("Elektroniker"), E7("Elektrotechniker"), E8("Fachinformatiker"), E9("Fachinformatiker"), E10("Industriemechaniker"), + E11("Lagerarbeiter"), E12("Mechaniker"), E13("Meister"), E14("Praktikant, SV-frei"), E15("Programmierer"), E16("Stud. Hilfskraft"), + E17("Techn. Beschäftigter"), E18("Techn. Zeichner"), E19("Techniker"), E20("Universitätsprofessor"), E21("Verw. Beschäftigter"), E22("Werkstoffprüfer"), + E23("Wiss. Beschäftigter"), E24("Wiss. Hilfskraft Bachelor"), E25("Wiss. Hilfskraft Master"); } + + // WZL + enum Aktion { NONE(""), ANFRAGE_VERSENDET("Anfrage versendet"), ERINNERUNG_VERSENDET("Erinnerung versendet"), + ZUR_UNTERSCHRIFT_BEIM_VORGESETZTEN("Zur Unterschrift beim Vorgesetzten"), + BEI_ZHV_VORGELEGT("Bei ZHV vorgelegt"), VON_ZHV_BESTAETIGT("Von ZHV bestätigt"); } + + // WZL + // -> DataSource + enum Vertragsgrundlage { NONE(""), E1("sonst. Befristungen (gleicht Auszubildenden)"), E2("unbefristet"), E3("unbefr. (Beamter aL)"), E4("§14 Abs. 2 TzBfG"), + E5("Elternzeitvertretung"), E6("Befristung SHK"), E7("Mehrbedarf sonstiger"), E8("Mehrb. Forschungspr."), E9("§2(5)Nr.3WZVG MuS-EZ"), + E10("§2 WZVG DM (NP)"), E11("§2 WZVG KiBetr NP"), E12("WZVG-Postdoc-Quali."), E13("WZVG-N-Gr._Leiter"), E14("WZVG-Wiss.-Quali NP"), + E15("§2 WZVG DM (VP)"), E16("§2 WZVG KiBetr VP"), E17("WZVG-Promotion"), E18("WZVG-sonst.Wiss-Qual"), E19("Befristung WHB"), E20("Befristung WHK"), + E21("§2 (1) S.2 WZVG nPr"), E22("§14(1) Nr.2 TzBfG-Üb"), E23("befr. Arbeitszeitänderung"), E24("Übernahme nach Ausbildung"); } + + association [1] Person -> (elternzeiten) Elternzeit [*]; // WZL + association [1] Person -> (nebentaetigkeiten) Nebentaetigkeit[*]; // WZL + association [1] Person -> (zulagen) Zulage [*]; // WZL + + // WZL Personal Export + class PersonalExportData { + } + association [1] PersonalExportData -> (entries) PersonalExportDataEntry [*]; + + class PersonalExportDataEntry { + String personalnummer; + Optional vorsatzwort; + String nachname; + String vorname; + ZonedDateTime von; + ZonedDateTime bis; + String pspElement; + Long prozentsatz; + } + + class PersonalExportEinstellungen { + String pspElement; + List bezeichnungen; + ZonedDateTime startmonat; + ZonedDateTime endmonat; + } + + class Organigramm { + boolean istStabstelle; + boolean istAktiv; + String bezeichnung; + } + + <> + association [0..1] Organigramm (parent) <-> (child) Organigramm [*]; + + association [1] Organigramm -> (leiter) OrganigrammPersonInfo [*]; + association [1] Organigramm -> (stellvertreter) OrganigrammPersonInfo [*]; + association [1] Organigramm -> (mitarbeiter) OrganigrammPersonInfo [*]; + + class OrganigrammPersonInfo { + Optional von; + Optional bis; + Optional umfang; + } + + association [*] OrganigrammPersonInfo -> Person [1]; + + //---------------------------------------------- + // =============== WZL END =============== + //---------------------------------------------- + + + // Druckeinstellungen + class DruckEinstellungenProjekt { + Long projektId; + Integer jahr; + Integer monat; + Optional mittelabrufnummer; + Optional belegnummer; + } + + association [1] DruckEinstellungenProjekt -> (druckEinstellungenPersonen) DruckEinstellungenPerson [*]; + + class DruckEinstellungenPerson { + Long personId; + Optional stundensatz; + } + + class BlacklistedToken { + <> // have more than 255 characters + String token; + ZonedDateTime addedAt; + ZonedDateTime expiresAt; + } + + class RefreshToken { + long userId; + String token; + } + + // NotificationCenter + class UserNotification { + UserNotificationType notificationType; + String title; + + <> + String message; + ZonedDateTime timeStamp; + + // clickable + Optional link; // fuer Router + + boolean seen; + boolean pinned; + } + + enum UserNotificationType { SUCCESS, INFO, WARNING, DANGER; } + + association [1] MacocoUser -> (notification) UserNotification [*]; + + //---------------------------------------------- + // =============== Command Logs ================ + //---------------------------------------------- + class AccessIdentifier { + } + + class CommandLog { + ZonedDateTime timestamp; + List diff; + String affectedArea; + String affectedObject; + } + + association [1] CommandLog -> (executor) MacocoUser [0..1]; // TODO: In den Migrationsskript für 2.9.0 -> 2.10.0 : früher war association [1] CommandLog -> (executor) MacocoUser [1]; + association [1] CommandLog -> (permission) AccessIdentifier [*]; + + //---------------------------------------------- + // =============== Dokumente =================== + //---------------------------------------------- + + // Bei Änderung PermissionCheckHelper.doesUserHavePermissionForDokument updaten + enum DokumentGroup { + PERSONAL("personal"), + KONTO("konto"); + } + + class Dokument { + DokumentGroup groupName; + Long associationId; // ID des Objektes an dem das Dokument hängt + ZonedDateTime datum; // Upload-Datum + String bezeichnung; + String dateiFormat; + Long fileSize; + Optional anmerkung; + boolean istAktiv; + } + +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/MaCoCoRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/MaCoCoRef.cd new file mode 100644 index 00000000..ea32f1f3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/MaCoCoRef.cd @@ -0,0 +1,1456 @@ +/* (c) https://github.com/MontiCore/monticore */ +/* adapted and customized by (c) MaCoCo, ein RWTH Aachen projekt */ + +// No packages supported yet in CD completion! +//package de.macoco.be.domain; + +import java.lang.String; +import java.lang.Long; +import java.lang.Integer; +import java.lang.Boolean; +import java.util.List; +import java.util.Optional; +import java.time.ZonedDateTime; + +classdiagram MaCoCo { + + //---------------------------------------------- + //--------------- PERSONAL --------------------- + //---------------------------------------------- + + /** + * Person ist die Zentrale Klasse des Personal-Komplexes. + **/ + class Person { + String vorname; + Optional vorsatzwort; + String nachname; + String kuerzel; // Abgeleitet analog wie bei SAP: 3 Vor- 3 Nachnamenbuchstaben + Optional personalnummer; + Optional gebDatum; + Optional beschBeginn; + Optional beschEnde; + List kommentar; + boolean istAktiv; + boolean istStundenzettelpflichtig = false; + Optional stundenzettelpflichtigVon; + Optional stundenzettelpflichtigBis; // TODO: brauchen wir nicht mehr + Optional ueberStundenUebertrag; + boolean darfWochenendarbeiten; + // Start WZL: + Optional hoechstbeschaeftigungBis; + List staatsangehoerigkeiten; + Optional telefonnummer; + Optional arbeitserlaubnisBis; + boolean extern; + Optional titel; + Optional rufname; + Optional adresse; + Optional email; + Optional geburtsname; + Optional geschlecht; + Optional lBVNummer; + Optional entfristung; + <> + Optional qualifikationstitel; + boolean speicherungGewuenscht = false; + <> + Optional urlaubskommentar; + Optional aenderungsdatum; + } + + association [1] Person -> (anstellungsarten) Anstellungsart [*]; + association [*] Person -> (vorgesetzte) Person [*]; + association [1] Person -> (notiz) Freitext [0..1]; + + //Für Studentenzettel: + association [1] Person -> (jahresurlaubstage) Jahresurlaub [*]; + association [1] Person -> (zusatzurlaubstage) Sonderurlaub [*]; + association [1] Person -> (stundenzettel) Stundenzettel [*]; + association [1] Person -> (abwesenheiten) Abwesenheit [*]; + + <> + association [1] Person -> (user) MacocoUser [0..1]; + + + //---------------------------------------------- + // Person -> *Anstellungsart + + class Anstellungsart { + BeschaeftigungsArt beschArt; + } + + association [1] Anstellungsart -> (vertraege) Vertrag [*]; + association [1] Anstellungsart -> (anstellungsformen) Anstellungsform [*]; + + enum BeschaeftigungsArt { + WiMi("Wissenschaftliche/r Mitarbeiter/in (WiMi)"), + HiWi("Hilfskraft (HiWi)"), + BTV("Beschäftigte/r in Technik und Verwaltung (BTV)"), + BEAMTE("Beamte/r"), + AZUBI("Azubi/ne"), + PLAN; + } + + //---------------------------------------------- + // Person -> *Anstellungsart -> *Vertrag + // eine direkte Verbindung zwischen Person und Vertrag existiert nicht. + + class Vertrag { // spiegelt den Vertrag der Hochschule wieder + ZonedDateTime vertragsBeginn; + ZonedDateTime vertragsEnde; + boolean aenderungsvertrag; + /String vertragsStatus; // Aktiv, Planung, Abgelaufen // TODO: change String to enum + List kommentar; + ZahlenWert planUmfang; + Optional arbeitsstundenProWoche; //Stundenzettel + boolean kuendigungsschutz; // WZL + Optional vertragsgrundlage; // WZL / TODO: change to enum + Optional aktion; // WZL / TODO: change to enum + Optional aktionsDatum; // WZL / TODO: change to enum + } + + association [1] Vertrag -> (kostenstellen) Kostenstelle [*]; + + //---------------------------------------------- + // Person -> *Anstellungsart -> Anstellungsform + + /** + * Beschreibt alle wesentlichen Merkmale einer Anstellung. + * Entgeltgruppe, Stufe, etc. + **/ + class Anstellungsform { + boolean erstanstellung; + Optional entgeltGruppe; // { TVL-13} Für Hiwis: {SHK (default), WHB, WHK} + Optional erfahrungsStufe; // 1- 6 ; sowie Spezialfälle (TVL Ü+); + boolean unbefristet; + ZonedDateTime anstellungVon; + Optional anstellungBis; + long gehaltCent; // evtl. Schätzgehalt in Zukunft + boolean istEigenerReferenzwert; + List kommentar; + Optional berufsgruppe; // WZL / TODO: change to enum + Optional aktion; // WZL / TODO: change to enum + Optional aktionsDatum; // WZL / TODO: change to enum + } + + //---------------------------------------------- + // Person -> *Anstellungsart -> *Vertrag -> *Kostenstelle + + class Kostenstelle { + ZonedDateTime verbuchungsBeginn; + ZonedDateTime verbuchungsEnde; + KostenstelleBezeichnung bezeichnung; + List kommentar; + ZahlenWert beschaeftigungsUmfang; + boolean buchungenNeuErzeugen; + boolean buchungenLoeschen; + boolean gesperrt; + } + + enum KostenstelleBezeichnung { + NONE(""), + AN_INSTITUT("An-Institut"), + GMBH("GmbH"), + ANDERER_LEHRSTUHL, + ANDERES_INSTITUT, + VORUEBERGEHEND_ABWESEND("Vorübergehend Abwesend"), + ANDERE_FINANZIERUNG; + } + + enum Zuweisungsart { + KONTO("Kontenzuweisung"), + STELLENZUWEISUNG, + PLANSTELLE, + SONSTIGES; + } + + association [*] Kostenstelle -> (personalBudget) Budget [0..1]; + association Kostenstelle -> (stellenzuweisung) Stellenzuweisung [0..1]; + association Kostenstelle -> (planstelle) Planstelle [0..1]; + <> + association [0..1] Kostenstelle -> (personalBuchungen) Buchung [*]; + + // Person -> *Anstellungsart -> *Vertrag -> *Kostenstelle -> ?Planstelle + + class Planstelle { + String bezeichnung; + Optional minEntgeltgruppe; + Optional minEntgeltstufe; + Optional maxEntgeltgruppe; + Optional maxEntgeltstufe; + Optional verfuegbarVon; + Optional verfuegbarBis; + Optional kommentar; + ZahlenWert planUmfang; + boolean aktiv; + } + + association Planstelle -> (konto) Konto [0..1]; + + //---------------------------------------------- + + /** + * Repräsentiert verschiedene Arten von monetären Haben + * @attribute wert ist jeweils unterschiedlich zu interpretieren: + * EURO heisst, der wert ist in Cent angegeben + * STUNDE heisst: er ist in Stunden angegeben + * (und das obwohl jedes Monat, Jahr unterschiedliche Stundenzahlen hat) + * PROZENT heisst er ist zwischen 0 und 100 angegeben (100%) + * NONE + **/ + class ZahlenWert { + ZahlenTyp zahlenTyp; + long wert; + } + + enum ZahlenTyp {EURO, STUNDE, PROZENT, NONE; } + + //---------------------------------------------- + //------------- Alerts ------------------------- + //---------------------------------------------- + + // Konto -> *MailAlert + + class MailAlert { + String name; + <> + String alertCondition; + <> + String mailBody; + int repetitions; + ZonedDateTime firstAlert; + ZonedDateTime lastAlert; + + Optional receipients; + Optional ccs; + Optional bccs; + + // extra Attribute zur Speicherung einiger Werte (vom System unbenutzt) + long a1; + long a2; + boolean flag1; + boolean flag2; + String s1; + String s2; + } + + //---------------------------------------------- + // ============ Finanz-Datenstruktur =========== + //---------------------------------------------- + + /** + * Hauptklasse; das Konto, von der alle anderen abhängen. + * Hat mehrere Varianten (Subklassen) + */ + abstract class Konto { + String name; + Optional pspElement; + Optional kontotyp; // unterschiedlich je nach Kontoart + Optional sapDatum; + Optional internesAktenzeichen; + boolean istPlanKonto; + boolean istVerbuchungsKonto; + boolean istAktiv; + Optional vergabeVerordnung; + Optional farbe; + Optional aenderungsdatum; + /Optional optStartDatum; + /Optional optEndDatum; + /Geschaeftsvorgang gueltigerGeschaeftsvorgang; + } + + <> + association [1] Konto (konto) -> (gesamtBudget) Budget [0..1]; + association [1] Konto -> (kommentare) Freitext [*]; + association [1] Konto -> (notiz) Freitext [0..1]; + association [1] Konto <-> MailAlert [*]; + association [1] Konto -> (abgleichsKonto) ExternKonto [0..1]; + + abstract class ErweitertesKonto extends Konto { + Optional startDatum; + Optional endDatum; + Optional bewilligungsDatum; // Bewilligungsdatum ist im Industrieprojekt nur mandatory, wenn kein Sammelkonto + Optional verlaengertBisDatum; + Optional aufstockungsDatum; + Optional foerderquote; + // human names: "Aktenzeichen (RWTH)" + // Drittmittel: "Förderkennzeichen", + Optional aktenzeichen; + Optional finanzierteKomplettsumme; + // human names: + // Drittmittel: "Aktenzeichen (Fördergeber)", + // Industrieprojekt: "Bestellnummer", + // Sonstiges: "Bestellnummer/Aktenzeichen (Fördergeber)" + Optional referenzSponsor; + boolean hatProgrammpauschale; + } + + + class Drittmittelprojekt extends ErweitertesKonto { + } + + association [*] Drittmittelprojekt -> (fachlicherVerantwortlicher) Person [*]; + + class Haushaltskonto extends Konto { + Optional startDatum; + Optional endDatum; + } + + class Industrieprojekt extends ErweitertesKonto { + Optional auftraggeber; + boolean istSammelkonto; + Optional mehrwertsteuer; + } + + association [*] Industrieprojekt -> (fachlicherVerantwortlicher) Person [*]; + + class Sonstiges extends ErweitertesKonto { + Optional auftraggeber; + Geschaeftsvorgang geschaeftsvorgang; + Optional mehrwertsteuer; + boolean istSammelkonto; + } + + + enum AbrechnungsInterval { + GESAMTE_PROJEKTLAUFZEIT("Gesamte Projektlaufzeit"), + JAEHRLICH("Jährlich"), + HALBJAEHRLICH("Halbjährlich"), + QUARTALSWEISE("Quartalsweise"), + MONATLICH("Monatlich"); + } + + association [1] Konto -> OrganigrammKonto [*]; + + class OrganigrammKonto { + Optional von; + Optional bis; + Optional umfang; + } + + association [*] OrganigrammKonto -> Organigramm [1]; + + class ZeitlicheVerbuchungskontoZuordnung { + Optional programmPauschale; + Optional gemeinkostensatz; + Optional startDate; + Optional endDate; + boolean istHauptVerbuchungskonto; + } + + association [1] ErweitertesKonto -> (verbuchungskontoZuordnung) ZeitlicheVerbuchungskontoZuordnung [*]; + association [*] ZeitlicheVerbuchungskontoZuordnung -> (verbuchungskonto) Konto [0..1]; + + //---------------------------------------------- + + // ------ Budget ------ + // Konto -> 1Budget + // Konto -> 1Budget -> *Budget + // Konto -> 1Budget -> *Budget -> *Budget + + /** + * Ein Budget erlaubt es ein Konto nach verschiedenen + * Strukturierungskriterien zu zerlegen. Budgets sind nur auf drei Ebenen + * erlaubt: 1 Hauptbudget, * OberBudgets, ** Unterbudgets. + * Die Budget-Struktur stellt einen Baum der maximalen Tiefe 3 dar. + */ + + class Budget { + String typ; // name + Optional startDatum; + Optional endDatum; + Optional kommentar; + Optional budgetRahmenCent; // wird im Gesamtbudget als Bewilligungssumme interpretiert + Optional eigenAnteilCent; + List jahresBudgets; + boolean proportionaleVerteilung; + boolean forOverheads; // das erste Budget (gesamtBudget.getUnterbudgets[0]) + Optional budgetKategorie; + /int budgetDepth; + } + + association [*] Budget -> Konto [1]; + association [0..1] Budget (elternBudget) <-> (unterBudget) Budget [*]; + association [1] Budget (budget) <-> (buchungseintrag) Buchungseintrag [*]; + association [1] Budget (budget) <-> (stellenzuweisung) Stellenzuweisung [*]; + <> + association [0..1] Budget -> Organigramm [0..1]; + + //---------------------------------------------- + + // ------ Buchungseinträge ------ + // Konto -> 1Budget (->*Budget)* -> *Buchung + + /** + * Buchungen aller Arten: gemeinsame Oberklasse + */ + abstract class Buchungseintrag { + ZonedDateTime datum; + Optional buchungseintragText; // human name: Buchung: "Buchungstext", Rechnung: "Rechnungsgrund", Mittelabruf: "Anmerkung", Mittelzuweisung: "Grund" + long betragCent; // human name: "Betrag", Rechnung: "Rechnungsbetrag", Konten mit Programmpauschale: "Rechnungsbetrag (inkl Pauschale)" + boolean istAktiv; + Optional aenderungsdatum; + /Geschaeftsvorgang gueltigerGeschaeftsvorgang; + /String lfdeNummer; + } + + class Buchung extends Buchungseintrag { + ZonedDateTime belegdatum; + String zahlungsgrund; //Belegpositionstext + Optional kreditorDebitor; + List sachkonto; + Optional buchungsdatum; + BuchungsStatus status; + List belegnummern; + List auftragsnummer; + Optional bereich; + Optional projekt; + Optional geschaeftsjahr; + Optional steuer; + Optional skonto; + Optional gez_Skonto; + Optional erfasser; + Optional belegart; + Optional referenz; + Optional stornonummer; + Optional stkz; + Optional erfassungsdatum; + Optional ausgleichsdatum; + long betragCentOriginal; // Bei Abweichungen von BetragCent kann nun eine Absicht ueberprueft werden + boolean budgetChangedManually; // s.o. + Optional kostenarten; + /Optional bezugsdatumJahr; + } + + class Rechnungsstellung extends Buchungseintrag { + Optional rechnungsdatum; + Optional rechnungsnummer; + RechnungsstellungStatus status; + List belegnummern; + Optional gemeinkosten; + Optional honorierung; + Optional projekt; + Optional debitor; + Optional mehrwertsteuer; + Optional zahlungsziel; + } + + class Mittelzuweisung extends Buchungseintrag { + Optional verfallDatum; + Optional kennung; + MittelzuweisungStatus status; + } + + class Mittelabruf extends Buchungseintrag { + ZonedDateTime abrufdatum; + MittelabrufStatus status; + String zeitraum; + Optional pauschaleManuell; // Präsent, falls die Pauschale manuell geändert wurde + } + + // ------ Stellenzuweisung ------ + + // Konto -> 1Budget (->*Budget)* -> *Stellenzuweisung + + class Stellenzuweisung { + ZonedDateTime erstellDatum; // aktuelles Datum + Optional startDatum; // Datum, zu welchem die Stelle beginnt + Optional endDatum; // Datum, zu welchem die Stelle endet + long wert; + String kennung; + StellenzuweisungStatus status; + ZahlenWert stellenumfang; + boolean istAktiv; + Optional aenderungsdatum; + } + + enum Geschaeftsvorgang { + NONE, + MITTELABRUF, + ZUWEISUNG, + RECHNUNG; + } + + enum BuchungsStatus { + EINGEREICHT, + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG, + FEHLERHAFT; + } + + enum RechnungsstellungStatus { + OFFENE_RECHNUNG, + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG; + } + + enum MittelzuweisungStatus { + BESCHEID_ERHALTEN ("Bescheid erhalten"), + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG, + FEHLERHAFT; + } + + enum MittelabrufStatus { + ABGERUFEN, + SAP("SAP"), + SAP_STORNIERT("SAP(Storniert)"), + PLANUNG, + EINGEREICHT; + } + + enum StellenzuweisungStatus { + BESETZT, + TEILBESETZT, + UNBESETZT, + PLANUNG; + } + + enum VertragStatus { + AKTIV, + PLANUNG, + ABGELAUFEN; + } + + //---------------------------------------------- + // =============== SAPii =============== + //---------------------------------------------- + + class SAPVerbindung { + SAPverbindungsStatus status; + } + + class Anfrage { + String ikz; + String bezeichner; + boolean letzteAnfrageErfolgreich; + } + + enum SAPverbindungsStatus { + VERBUNDEN, + GETRENNT, + PROBLEM; + } + + enum SAPimportMode { + DEFAULT, + F1KONTO; + } + + association [1] SAPVerbindung -> (authorizedBy) MacocoUser [*]; + association [1] MacocoUser -> (anfragen) Anfrage [*]; + association [1] Anfrage -> (filteredKonten) PSPImportFilter [*]; + + //---------------------------------------------- + + class AbgleichsBuchung extends Buchung { + AbgleichsStatus abgleichsStatus; + Optional abgleichKommentar; + String positionsNummer; + boolean deactivated; + } + + enum AbgleichsStatus { + ABGESCHLOSSEN, // Der Abgleich dieser Buchung ist Abgeschlossen + TEILWEISE_ABGESCHLOSSEN, // Der Abgleich wurde schon Bearbeitet es fehlen jedoch Summen + ABWEICHEND, // Der Abgleich ist Vollständig mit Abweichenden Summen + OFFEN, // Der Abgleich wurde noch nicht gesetzt + VORGESCHLAGEN; // Der Abgleich wurde vom System vorgeschlagen + } + + class AbgleichsGruppe { + String bezeichnung; + } + <> + association AbgleichsGruppe -> (buchungen) Buchung [*]; + <> + association AbgleichsGruppe -> (abgleichsBuchungen) AbgleichsBuchung [*]; + <> + association AbgleichsGruppe -> (konto) Konto [1]; + + // ------ Freitext ------ + + class Freitext { + Optional erstellDatum; + Optional bearbeitetDatum; + <> + Optional text; + } + + class Legende { + LegendeTyp type; + String expression; + <> + String description; + } + + enum LegendeTyp { + KONTO_FARBE; + } + + //---------------------------------------------- + // =============== Projekte =============== + //---------------------------------------------- + + abstract class Projekt { + String name; + String kuerzel; + Optional minPM; + Optional maxPM; + Optional finanzPM; + Optional laufzeitVon; + Optional laufzeitBis; + Optional kommentar; + Optional foerdergeberAZ; + ProjektStatus status; + boolean istAktiv; + boolean lockedForStundenzettel; // Das gesamte Projekt ist gesperrt + List lockedMonths; // Stellen die Ausnahme aus der Lock-Regelung für das gesamte Projekt dar + Optional aenderungsdatum; + } + + association [1] Projekt -> Aufwand [*]; + association [1] Projekt -> Arbeitspaket [*]; + association [1] Projekt -> Anstellung [*]; + association [*] Projekt -> Konto [0..1]; + association [*] Projekt -> (hauptverantwortlich) Person [*]; + association [1] Projekt -> (notiz) Freitext [0..1]; + + class Auftragsprojekt extends Projekt { + Optional nummer; + ProjektTyp typ; + Optional regelung; + } + + class Organisation extends Projekt { + } + + class Lehre extends Projekt { + Optional stunden; + } + + class Arbeitspaket { + String nummer; // könnte auch 1.1 sein + String name; + Optional beschreibung; + Optional beginnDatum; + Optional endeDatum; + Optional pMs; + Optional stunden; + Optional aenderungsdatum; + } + + class Aufwand { + /long pM; + ZahlenWert umfang; + ZonedDateTime laufzeitVon; + ZonedDateTime laufzeitBis; + Optional aenderungsdatum; + } + + association [*] Aufwand -> Person [0..1]; + + class Anstellung { + Optional bezeichnung; + ZahlenWert umfang; + ZonedDateTime von; + ZonedDateTime bis; + boolean verfaellt; + Optional min; + Optional max; + /long pM; + BeschaeftigungsArt beschaeftigungsArt; + } + + association [*] Anstellung -> Person [0..1]; + + class Stellentyp { + Optional entgeltgruppe; + Optional entgeltstufe; + } + + enum ProjektStatus { + IN_DEFINITION, + BEANTRAGT, + GENEHMIGT, + ABGELEHNT, + LAUFEND, + FACHLICH_ABGESCHLOSSEN("Fachlich abgeschlossen"), + ENDBERICHT_EINGEREICHT("Endbericht eingereicht"), + INPRUEFUNG("In Prüfung"), + GEPRUEFT("Geprüft"), + ABGERECHNET, + ABGESCHLOSSEN, + ARCHIVIERT; + } + + enum ProjektArt { + AUFTRAG, + ORGANISATION, + LEHRE, + NONE; + } + + enum ProjektTyp { + HOHEITLICH, + INDUSTRIE, + UNIVERSITAET("Universität"), + SONSTIGE; + } + + enum Vergabeverordnung { + VOL_A ("VOL/A"), + VOB_A ("VOB/A"), + UVGO_NRW ("UVgO NRW"), + UVGO_BUND ("UVgO Bund"), + NONE (" "); + } + + //---------------------------------------------- + // =============== Stundenzettel =============== + //---------------------------------------------- + + class Stundenzettel { + StundenzettelStatus status; + ZonedDateTime zeit; + Optional abgegebenVonUserId; + Optional abgabeDatum; + } + + association [1] Stundenzettel -> (eintraege) StundenzettelEintrag [*]; + + class StundenzettelEintrag { + ZonedDateTime uhrzeitVon; + Optional uhrzeitBis; + Optional beschreibung; + /long stunden; + StundenzettelProjekt pauseOderSonstiges; + } + + association [*] StundenzettelEintrag -> Projekt [0..1]; + association [*] StundenzettelEintrag -> Arbeitspaket [0..1]; + + class StundenzettelHistorieEintrag { + ZonedDateTime bearbeitungsdatum; + Optional editorId; + Optional editedBy; + String monat; + int jahr; + String changeOperation; + } + + enum StundenzettelChangeOperation { + INVALID, + ABGEGEBEN("Abgegeben"), + INTERN_GEPRUEFT("Intern Geprüft"), + INTERN_ABGESCHLOSSEN("Intern Abgeschlossen"); + } + + association [1] Person -> StundenzettelHistorieEintrag [*]; + + class Abwesenheit { + Abwesenheitsgrund grund; + ZonedDateTime datumVon; + ZonedDateTime datumBis; + Optional kommentar; + Optional uhrzeitVon; + Optional uhrzeitBis; + Optional minutenProTag; + /long tageGesamt; + } + + class Dienstreise extends Abwesenheit { + Optional reisenummer; + Optional dienstreisegrund; + Optional reiseziel; + Optional gesamtkosten; + Optional status; + Optional statusText; + } + + association [*] Abwesenheit -> Konto [0..1]; + + association [1] Abwesenheit -> UrlaubsGenehmigung [0..1]; + + association [*] Dienstreise -> Budget [0..1]; + + class Arbeitstage { + ZonedDateTime guiltigAb; + ZonedDateTime guiltigBis; + List wochenTage; // 0..4 (Montag-Freitag) + } + + association [1] Person -> Arbeitstage [*]; + + class Jahresurlaub { + int jahr; + long tageAnzahl; + Optional stundenAnzahl; + boolean istManuellerEintrag; + } + + class UrlaubsGenehmigung { + Optional beantragt; + Optional genehmigt; + Optional geprueft; + Optional storniert; + Urlaubssstatus status; + Stornierungsstatus stornierungsStatus; + } + + class Sonderurlaub { // Für WZL: Zusatzurlaub umbenennen in Sonderurlaub und neues Feld anzahl + ZonedDateTime datumVon; + ZonedDateTime datumBis; + long anzahl; + } + + class UrlaubEinstellungen { + boolean urlaubsanspruchInStunden; + } + + enum Abwesenheitsgrund { + U_URLAUB, + D_DIENSTREISE, + S_SONSTIGE; + /* + Z_ZUSATZURLAUB, + A_ARBEITSUNFALL, + AB_ABGELTUNG, + E_ELTERNZEIT, + F_FREISTELLUNG, + K_KRANKHEIT, + KU_KUR, + S_SONSTIGES;*/ + } + + enum Urlaubssstatus { + NONE, + BEANTRAGT, + GEPRUEFT("Geprüft"), + PRUEFUNG_ABGELEHNT("Prüfung abgelehnt"), + GENEHMIGT, + GENEHMIGUNG_ABGELEHNT; + } + + enum Stornierungsstatus { + NONE, + VERBOTEN, + STORNIERUNGSANTRAG, + STORNIERT; + } + + enum Abrechnungsstatus { + OFFEN, + ABZURECHNEN, + ABGERECHNET, + STORNIERT; + } + + enum StundenzettelStatus { + IN_ERFASSUNG, + INTERN_INPRUEFUNG("Intern in Prüfung"), + INTERN_ABGESCHLOSSEN, + DRITTMITTELABTEILUNG_INPRUEFUNG("Drittmittelabteilung in Prüfung"), + DRITTMITTELABTEILUNG_ABGESCHLOSSEN, + ENDBERICHT_EINGEREICHT, + GEPRUEFT("Geprüft"), + FOERDERGEBER_ABGESCHLOSSEN("Fördergeber abgeschlossen"), + ABGESCHLOSSEN; + } + + enum StundenzettelProjekt { + PAUSE, + SONSTIGE, + NONE; + } + + association [*] F1Konto -> (institut) Institut [0..1]; + + class ExternKonto extends Konto { + Optional startDatum; + Optional endDatum; + Optional importVermerk; + AbgleichType abgleichVerhalten; + } + + enum AbgleichType { + IGNORE, + MANUEL, + AUTO_COPY; + } + + association [*] ExternKonto -> (institut) Institut [0..1]; + + class PSPImportFilter{ + boolean importThisPSPelement; + boolean importBookings; + String name; + String pspElement; + Optional startDate; + } + + association [*] PSPImportFilter -> (targetPSP) Konto [0..1]; + + //---------------------------------------------- + // Reports + + class EventReport { + ZonedDateTime eventStart; + Optional eventEnd; + /EventStatus status; + String message; // BuchungImport: Nutzer 'netz' hat die Datei '.....' importiert + } + + class EventReportEntry { + EventType type; + Optional targetId; // click -> navigate to id + Optional count; + <> + String message; // Zeile 4 Fehler ...... Zeile 5 ..... + EventStatus status; + } + association [1] EventReport -> (entries) EventReportEntry [*]; + + class ImportReport extends EventReport { + } + + class ImportReportEntry extends EventReportEntry { + } + + + class PersonalExportReport extends EventReport { + Long dataId; + + ZonedDateTime datenStart; + ZonedDateTime datenEnd; + Long countData; + /Long countError; + EventStatus exportStatus; + } + + class PersonalExportReportEntry extends EventReportEntry { + Optional vorsatzwort; + String vorname; + String nachname; + BeschaeftigungsArt beschaeftigungsArt; + ZonedDateTime monat; + } + + enum EventStatus { + // Import + SUCCESS("erfolgreich"), + ERROR("fehlerhaft"), + INFO("erfolgreich"), // success + special + CREATED("erstellt"), + UPDATED("upgedated"), + IGNORED("ignoriert"), + IN_PROGRESS("in bearbeitung"), + + // Personal Export + SENT("verschickt"), + CORRECT("sachlich korrekt"); + } + + enum EventType { + IMPORT_KONTO("Konten"), + IMPORT_BUCHUNG("Buchungs"), + IMPORT_PERSON("Personen"), + IMPORT_INSTITUT("Instituts"), + IMPORT_NUTZER("Nutzer"), + IMPORT_VERTRAG("Vertrags"), + EXPORT_FINANZEN_KONTO("Konten"), + EXPORT_FINANZEN_PLANSTELLE("Planstellen"), + EXPORT_FINANZEN_VERTRAG("Vertragabdeckungs"); + } + + + //---------------------------------------------- + // =============== Fakultät 1 DS =============== + //---------------------------------------------- + + // Institut + <> + class Institut { + String institutsKennZiffer; + String institutsName; + String professorName; + boolean lokalInstitut; + } + + // Fachgruppe + enum Fachgruppe { + MATHEMATIK, + INFORMATIK, + PHYSIK, + CHEMIE, + BIOLOGIE; + } + + //SAP-Abschöpfung von Geschäftsjahr + class GeschaeftsjahrSAPAbschoepfung { + String fachgruppe; + String institutsKennZiffer; + String institutsName; + String pspElement; + long gesamtSAPAbschoepfung; + } + + class SAPAbschoepfung { + Long betragCent; + ZonedDateTime year; + } + + association [1] GeschaeftsjahrSAPAbschoepfung -> (sapAbschoepfung) SAPAbschoepfung [*]; + + + // Konto + class F1Konto extends Haushaltskonto { + Optional startDatum; + Optional endDatum; + long originalBudgetCent; + long sonstigeZuweisungenCent; + long resteCent; + long aktuellerKontostandCent; + long kontoRahmenCent; + KommunikationsStatus kommunikationsStatus; + StrafsteuerStatus strafsteuerStatus; + long strafsteuerBasisCent; + long strafsteuerSAPCent; + long strafsteuerCent; + long strafsteuerBerechnungsGrundlageCent; + Optional kontoStandBeginnGeschaeftsjahr; + } + + // Konto -> 1Budget (->*Budget)* -> *Buchung -> ?Begruendung + + // Buchungen werden hier noch erweitert: + association [1] Buchung -> (begruendung) Begruendung [0..1]; + + class Begruendung { + String text; + ZonedDateTime erstellung; + Optional letzteBearbeitung; + Optional geschaeftsjahr; + } + + association [1] Begruendung -> (akzeptanz) Akzeptanz [0..1]; + //association [1] Begruendung -> (geschaeftsjahr) StrafsteuerGeschaeftsjahr [0..1]; + + //---------------------------------------------- + + // Konto -> 1Budget (->*Budget)* -> *Buchung -> ?Begruendung -> ?Akzeptanz + + class Akzeptanz { + Akzeptanzstatus akzeptanzstatus; + Optional teilbetragCent; + Optional bearbeitung; + boolean istAktiv; + Optional kommentar; + } + + //---------------------------------------------- + + enum KommunikationsStatus { + KOM_STAT_NO_ACTION_NEEDED, // Kein Handlungsbedarf + KOM_STAT_BEGR_FEHLT, // Begründung fehlt + KOM_STAT_ANTW_FEHLT, // Antwort fehlt + KOM_STAT_ANTW_CHANGED, // Buchung oder Begründung wurden geändert + KOM_STAT_BEGR_CHANGED, // Antwort wurde geändert + KOM_STAT_ANTW_BEGR_CHANGED, // Antwort und Begründung wurde geändert + KOM_STAT_ANTW_FEHLT_CHANGED; // Antwort Fehlt, andere Antwort wurde geändert + } + + enum StrafsteuerStatus { + NONE, + SOME, + ALL; + } + + enum Akzeptanzstatus { + OFFEN, + OK, + TEILOK, + NOTOK; + } + + class StrafsteuerGeschaeftsjahr { + ZonedDateTime jahr; + boolean current; + } + + class StrafsteuerSperrDatum { + ZonedDateTime sperrDatum; + } + + //---------------------------------------------- + // =============== MacocoUser =============== + //---------------------------------------------- + + class MacocoUser { + <> + String username; + Optional encodedPassword; + String passwordSaltBase64; + ZonedDateTime registrationDate; + Optional initials; + MacocoUserActivationStatus activated; + boolean enabled; + <> + String email; + boolean authentifiziert; + Optional timID; + Optional sapAccessToken; + Optional sapRefreshToKen; + + } + + association [*] MacocoUser -> (institute) Institut [*]; + + enum MacocoUserActivationStatus { + AKTIVIERT, + MAIL_NICHT_GESENDET("Mail nicht gesendet"), + MAIL_FEHLERHAFT("Mail fehlerhaft"), + MAIL_GESENDET("Mail gesendet"); + } + + // Favoriten + class Favorite { + String title; + String url; + } + + association [1] MacocoUser -> (favorite) Favorite [*]; + + class RoleAssignment { + } + + association [*] RoleAssignment -> (user) MacocoUser [1]; + association [*] RoleAssignment -> (accessPolicy) AccessPolicy [1]; + + class AccessPolicy { + String roleName; + Optional objId; + } + + class GroupedAccessPolicy { + String name; + } + + association [*] GroupedAccessPolicy -> (user) MacocoUser [*]; + association [1] GroupedAccessPolicy -> (policy) AccessPolicy [*]; + + //---------------------------------------------- + // =============== Fristen =============== + //---------------------------------------------- + + class Frist { + String beschreibung; + Friststatus status; + Fristobjekt fristobjekt; + ZonedDateTime faelligkeitsdatum; + Optional notiz; + Boolean manuellerEintrag; + Long bezugsobjektId; + String bezugsobjektName; + Optional aenderungsdatum; + boolean istAktiv; + } + + association [*] Frist -> (zuweisung) Person [0..1]; + + association [1] Person -> Frist [*]; + association [1] Konto -> Frist [*]; + association [1] Projekt -> Frist [*]; + association [1] Mittelabruf -> Frist [0..1]; + association [1] Rechnungsstellung -> Frist [0..1]; + association [1] Vertrag -> Frist [0..1]; + association [1] Zulage -> Frist [0..1]; + + enum Friststatus { + PLANUNG, + IN_BEARBEITUNG, + ERLEDIGT; + } + + enum Fristobjekt { + PERSON, + KONTO, + VERTRAGSVERLAENGERUNG("Vertragsverlängerung"), + ZULAGE, + RECHNUNG, + MITTELABRUF, + PROJEKT; + } + + class DefaultFristen { + ZonedDateTime finanzenStartDatum; + Long finanzenFristinWeeks; + ZonedDateTime personalStartDatum; + Long personalFristinWeeks; + } + + //---------------------------------------------- + // ============= Einstellungen ================= + //---------------------------------------------- + + class Setting { + String identifier; + } + + class InstanzSetting extends Setting { + String value; + } + + class UISetting extends Setting { + String seite; + } + + association [0..1] MacocoUser -> (setting) UISetting [*]; + + class FilterSetting extends UISetting { + List filterValues; + } + + class TableSetting extends UISetting { + TableIdentifierTyp typ; + boolean zeigeInaktive; + int zeilenLimit; + Optional gruppiereNach; + Optional sortiereNach; + Optional sortiereDir; + boolean isShowColor; + } + + association [1] TableSetting -> (spalten) TableSettingSpalten [*]; + + enum TableIdentifierTyp { + DEFAULT, + USER, + INSTITUT; + } + + class TableSettingSpalten { + String name; + boolean istAktiv; + long breite; + } + + class ErweiterbareListen{ + String bereich; + String feldbezeichnung; + List inhalte; + } + + //---------------------------------------------- + // =============== Card Settings =============== + //---------------------------------------------- + + class CardSetting { + String page; + String identifier; + <> + String configuration; + } + + association [*] CardSetting -> (benutzer) MacocoUser [0..1]; + + //---------------------------------------------- + // =============== WZL =============== + //---------------------------------------------- + + class Elternzeit { // WZL + Optional von; + Optional bis; + ZahlenWert umfang; + } + + class Nebentaetigkeit { // WZL + Optional von; + Optional bis; + ZahlenWert umfang; + Optional arbeitgeber; + Optional taetigkeitsbereich; + } + + class Zulage { // WZL + Optional pspelement; + Optional bezeichnung; + ZonedDateTime von; + ZonedDateTime bis; + Long arbeitgeberbrutto; + Long arbeitnehmerbrutto; + } + + // WZL + enum Geschlecht { NONE(""), WEIBLICH("weiblich"), MAENNLICH("männlich"), DIVERS("divers"); } + + // WZL + // -> DataSource + enum Titel { NONE(""), E1("Apl. Prof. Dr.") , E2("B.Sc.") , E3("Dipl.-Inform.") , E4("Dipl.-Ing."), E5("Dipl.-Ing. (FH)"), + E6("Dipl.-Ing. (RUS)"), E7("Dipl.-Kff.") , E8("Dipl.-Math."), E9("Dipl.-Phys.") , E10("Dipl.-Wirt. Ing.") , E11("Dr.") , E12("Dr. phil."), + E13("Dr. rer. nat."), E14("Dr. rer. pol."), E15("Dr.-Ing."), E16("M.A.") , E17("M.Eng."), E18("M.Sc."), E19("PhD"), E20("Prof. Dr.-Ing."); } + + // WZL + // -> DataSource + enum Berufsgruppe { NONE(""), E1("Akademischer Direktor"), E2("Angestellter in der DV"), E3("Auszubildende/r"), E4("Bibl.-Beschäftiger"), + E5("Elektriker"), E6("Elektroniker"), E7("Elektrotechniker"), E8("Fachinformatiker"), E9("Fachinformatiker"), E10("Industriemechaniker"), + E11("Lagerarbeiter"), E12("Mechaniker"), E13("Meister"), E14("Praktikant, SV-frei"), E15("Programmierer"), E16("Stud. Hilfskraft"), + E17("Techn. Beschäftigter"), E18("Techn. Zeichner"), E19("Techniker"), E20("Universitätsprofessor"), E21("Verw. Beschäftigter"), E22("Werkstoffprüfer"), + E23("Wiss. Beschäftigter"), E24("Wiss. Hilfskraft Bachelor"), E25("Wiss. Hilfskraft Master"); } + + // WZL + enum Aktion { NONE(""), ANFRAGE_VERSENDET("Anfrage versendet"), ERINNERUNG_VERSENDET("Erinnerung versendet"), + ZUR_UNTERSCHRIFT_BEIM_VORGESETZTEN("Zur Unterschrift beim Vorgesetzten"), + BEI_ZHV_VORGELEGT("Bei ZHV vorgelegt"), VON_ZHV_BESTAETIGT("Von ZHV bestätigt"); } + + // WZL + // -> DataSource + enum Vertragsgrundlage { NONE(""), E1("sonst. Befristungen (gleicht Auszubildenden)"), E2("unbefristet"), E3("unbefr. (Beamter aL)"), E4("§14 Abs. 2 TzBfG"), + E5("Elternzeitvertretung"), E6("Befristung SHK"), E7("Mehrbedarf sonstiger"), E8("Mehrb. Forschungspr."), E9("§2(5)Nr.3WZVG MuS-EZ"), + E10("§2 WZVG DM (NP)"), E11("§2 WZVG KiBetr NP"), E12("WZVG-Postdoc-Quali."), E13("WZVG-N-Gr._Leiter"), E14("WZVG-Wiss.-Quali NP"), + E15("§2 WZVG DM (VP)"), E16("§2 WZVG KiBetr VP"), E17("WZVG-Promotion"), E18("WZVG-sonst.Wiss-Qual"), E19("Befristung WHB"), E20("Befristung WHK"), + E21("§2 (1) S.2 WZVG nPr"), E22("§14(1) Nr.2 TzBfG-Üb"), E23("befr. Arbeitszeitänderung"), E24("Übernahme nach Ausbildung"); } + + association [1] Person -> (elternzeiten) Elternzeit [*]; // WZL + association [1] Person -> (nebentaetigkeiten) Nebentaetigkeit[*]; // WZL + association [1] Person -> (zulagen) Zulage [*]; // WZL + + // WZL Personal Export + class PersonalExportData { + } + association [1] PersonalExportData -> (entries) PersonalExportDataEntry [*]; + + class PersonalExportDataEntry { + String personalnummer; + Optional vorsatzwort; + String nachname; + String vorname; + ZonedDateTime von; + ZonedDateTime bis; + String pspElement; + Long prozentsatz; + } + + class PersonalExportEinstellungen { + String pspElement; + List bezeichnungen; + ZonedDateTime startmonat; + ZonedDateTime endmonat; + } + + class Organigramm { + boolean istStabstelle; + boolean istAktiv; + String bezeichnung; + } + + <> + association [0..1] Organigramm (parent) <-> (child) Organigramm [*]; + + association [1] Organigramm -> (leiter) OrganigrammPersonInfo [*]; + association [1] Organigramm -> (stellvertreter) OrganigrammPersonInfo [*]; + association [1] Organigramm -> (mitarbeiter) OrganigrammPersonInfo [*]; + + class OrganigrammPersonInfo { + Optional von; + Optional bis; + Optional umfang; + } + + association [*] OrganigrammPersonInfo -> Person [1]; + + //---------------------------------------------- + // =============== WZL END =============== + //---------------------------------------------- + + + // Druckeinstellungen + class DruckEinstellungenProjekt { + Long projektId; + Integer jahr; + Integer monat; + Optional mittelabrufnummer; + Optional belegnummer; + } + + association [1] DruckEinstellungenProjekt -> (druckEinstellungenPersonen) DruckEinstellungenPerson [*]; + + class DruckEinstellungenPerson { + Long personId; + Optional stundensatz; + } + + class BlacklistedToken { + <> // have more than 255 characters + String token; + ZonedDateTime addedAt; + ZonedDateTime expiresAt; + } + + class RefreshToken { + long userId; + String token; + } + + // NotificationCenter + class UserNotification { + UserNotificationType notificationType; + String title; + + <> + String message; + ZonedDateTime timeStamp; + + // clickable + Optional link; // fuer Router + + boolean seen; + boolean pinned; + } + + enum UserNotificationType { SUCCESS, INFO, WARNING, DANGER; } + + association [1] MacocoUser -> (notification) UserNotification [*]; + + //---------------------------------------------- + // =============== Command Logs ================ + //---------------------------------------------- + class AccessIdentifier { + } + + class CommandLog { + ZonedDateTime timestamp; + List diff; + String affectedArea; + String affectedObject; + } + + association [1] CommandLog -> (executor) MacocoUser [0..1]; // TODO: In den Migrationsskript für 2.9.0 -> 2.10.0 : früher war association [1] CommandLog -> (executor) MacocoUser [1]; + association [1] CommandLog -> (permission) AccessIdentifier [*]; + + //---------------------------------------------- + // =============== Dokumente =================== + //---------------------------------------------- + + // Bei Änderung PermissionCheckHelper.doesUserHavePermissionForDokument updaten + enum DokumentGroup { + PERSONAL("personal"), + KONTO("konto"); + } + + class Dokument { + DokumentGroup groupName; + Long associationId; // ID des Objektes an dem das Dokument hängt + ZonedDateTime datum; // Upload-Datum + String bezeichnung; + String dateiFormat; + Long fileSize; + Optional anmerkung; + boolean istAktiv; + } + +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichType.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichType.java new file mode 100644 index 00000000..7d4d28ec --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichType.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum AbgleichType { + IGNORE, + MANUEL, + AUTO_COPY; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsBuchung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsBuchung.java new file mode 100644 index 00000000..4d0310c9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsBuchung.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"AbgleichsBuchung"}, template = "${}") +public class AbgleichsBuchung extends Buchung { + @Adapt(ref = {"AbgleichsBuchung.abgleichsStatus"}, template = "${}") + private AbgleichsStatus abgleichsStatus; + + @Adapt(ref = {"AbgleichsBuchung.abgleichKommentar"}, template = "${}") + private Optional abgleichKommentar; + + @Adapt(ref = {"AbgleichsBuchung.positionsNummer"}, template = "${}") + private String positionsNummer; + + @Adapt(ref = {"AbgleichsBuchung.deactivated"}, template = "${}") + private boolean deactivated; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsGruppe.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsGruppe.java new file mode 100644 index 00000000..f48d5ed5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsGruppe.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"AbgleichsGruppe"}, template = "${}") +public class AbgleichsGruppe { + @Adapt(ref = {"AbgleichsGruppe.bezeichnung"}, template = "${}") + private String bezeichnung; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsStatus.java new file mode 100644 index 00000000..e35d006a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbgleichsStatus.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum AbgleichsStatus { + ABGESCHLOSSEN, + TEILWEISE_ABGESCHLOSSEN, + ABWEICHEND, + OFFEN, + VORGESCHLAGEN; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbrechnungsInterval.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbrechnungsInterval.java new file mode 100644 index 00000000..e62d5d91 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AbrechnungsInterval.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum AbrechnungsInterval { + GESAMTE_PROJEKTLAUFZEIT, + JAEHRLICH, + HALBJAEHRLICH, + QUARTALSWEISE, + MONATLICH; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abrechnungsstatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abrechnungsstatus.java new file mode 100644 index 00000000..f747976e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abrechnungsstatus.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Abrechnungsstatus { + OFFEN, + ABZURECHNEN, + ABGERECHNET, + STORNIERT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abwesenheit.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abwesenheit.java new file mode 100644 index 00000000..73aa2ba6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abwesenheit.java @@ -0,0 +1,31 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Abwesenheit"}, template = "${}") +public class Abwesenheit { + @Adapt(ref = {"Abwesenheit.grund"}, template = "${}") + private Abwesenheitsgrund grund; + + @Adapt(ref = {"Abwesenheit.datumVon"}, template = "${}") + private ZonedDateTime datumVon; + + @Adapt(ref = {"Abwesenheit.datumBis"}, template = "${}") + private ZonedDateTime datumBis; + + @Adapt(ref = {"Abwesenheit.kommentar"}, template = "${}") + private Optional kommentar; + + @Adapt(ref = {"Abwesenheit.uhrzeitVon"}, template = "${}") + private Optional uhrzeitVon; + + @Adapt(ref = {"Abwesenheit.uhrzeitBis"}, template = "${}") + private Optional uhrzeitBis; + + @Adapt(ref = {"Abwesenheit.minutenProTag"}, template = "${}") + private Optional minutenProTag; + + @Adapt(ref = {"Abwesenheit.tageGesamt"}, template = "${}") + private long tageGesamt; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abwesenheitsgrund.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abwesenheitsgrund.java new file mode 100644 index 00000000..de5e5612 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Abwesenheitsgrund.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Abwesenheitsgrund { + U_URLAUB, + D_DIENSTREISE, + S_SONSTIGE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AccessIdentifier.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AccessIdentifier.java new file mode 100644 index 00000000..c5aa459c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AccessIdentifier.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"AccessIdentifier"}, template = "${}") +public class AccessIdentifier { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AccessPolicy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AccessPolicy.java new file mode 100644 index 00000000..904815ef --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/AccessPolicy.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"AccessPolicy"}, template = "${}") +public class AccessPolicy { + @Adapt(ref = {"AccessPolicy.roleName"}, template = "${}") + private String roleName; + + @Adapt(ref = {"AccessPolicy.objId"}, template = "${}") + private Optional objId; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Aktion.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Aktion.java new file mode 100644 index 00000000..05001219 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Aktion.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Aktion { + NONE, + ANFRAGE_VERSENDET, + ERINNERUNG_VERSENDET, + ZUR_UNTERSCHRIFT_BEIM_VORGESETZTEN, + BEI_ZHV_VORGELEGT, + VON_ZHV_BESTAETIGT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Akzeptanz.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Akzeptanz.java new file mode 100644 index 00000000..c4bdb0c5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Akzeptanz.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Akzeptanz"}, template = "${}") +public class Akzeptanz { + @Adapt(ref = {"Akzeptanz.akzeptanzstatus"}, template = "${}") + private Akzeptanzstatus akzeptanzstatus; + + @Adapt(ref = {"Akzeptanz.teilbetragCent"}, template = "${}") + private Optional teilbetragCent; + + @Adapt(ref = {"Akzeptanz.bearbeitung"}, template = "${}") + private Optional bearbeitung; + + @Adapt(ref = {"Akzeptanz.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"Akzeptanz.kommentar"}, template = "${}") + private Optional kommentar; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Akzeptanzstatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Akzeptanzstatus.java new file mode 100644 index 00000000..39ad726b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Akzeptanzstatus.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Akzeptanzstatus { + OFFEN, + OK, + TEILOK, + NOTOK; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anfrage.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anfrage.java new file mode 100644 index 00000000..0d358bbc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anfrage.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Anfrage"}, template = "${}") +public class Anfrage { + @Adapt(ref = {"Anfrage.ikz"}, template = "${}") + private String ikz; + + @Adapt(ref = {"Anfrage.bezeichner"}, template = "${}") + private String bezeichner; + + @Adapt(ref = {"Anfrage.letzteAnfrageErfolgreich"}, template = "${}") + private boolean letzteAnfrageErfolgreich; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellung.java new file mode 100644 index 00000000..e002d2a0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellung.java @@ -0,0 +1,34 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Anstellung"}, template = "${}") +public class Anstellung { + @Adapt(ref = {"Anstellung.bezeichnung"}, template = "${}") + private Optional bezeichnung; + + @Adapt(ref = {"Anstellung.umfang"}, template = "${}") + private ZahlenWert umfang; + + @Adapt(ref = {"Anstellung.von"}, template = "${}") + private ZonedDateTime von; + + @Adapt(ref = {"Anstellung.bis"}, template = "${}") + private ZonedDateTime bis; + + @Adapt(ref = {"Anstellung.verfaellt"}, template = "${}") + private boolean verfaellt; + + @Adapt(ref = {"Anstellung.min"}, template = "${}") + private Optional min; + + @Adapt(ref = {"Anstellung.max"}, template = "${}") + private Optional max; + + @Adapt(ref = {"Anstellung.pM"}, template = "${}") + private long pM; + + @Adapt(ref = {"Anstellung.beschaeftigungsArt"}, template = "${}") + private BeschaeftigungsArt beschaeftigungsArt; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellungsart.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellungsart.java new file mode 100644 index 00000000..222a1d90 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellungsart.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Anstellungsart"}, template = "${}") +public class Anstellungsart { + @Adapt(ref = {"Anstellungsart.beschArt"}, template = "${}") + private BeschaeftigungsArt beschArt; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellungsform.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellungsform.java new file mode 100644 index 00000000..258b01fd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Anstellungsform.java @@ -0,0 +1,43 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Anstellungsform"}, template = "${}") +public class Anstellungsform { + @Adapt(ref = {"Anstellungsform.erstanstellung"}, template = "${}") + private boolean erstanstellung; + + @Adapt(ref = {"Anstellungsform.entgeltGruppe"}, template = "${}") + private Optional entgeltGruppe; + + @Adapt(ref = {"Anstellungsform.erfahrungsStufe"}, template = "${}") + private Optional erfahrungsStufe; + + @Adapt(ref = {"Anstellungsform.unbefristet"}, template = "${}") + private boolean unbefristet; + + @Adapt(ref = {"Anstellungsform.anstellungVon"}, template = "${}") + private ZonedDateTime anstellungVon; + + @Adapt(ref = {"Anstellungsform.anstellungBis"}, template = "${}") + private Optional anstellungBis; + + @Adapt(ref = {"Anstellungsform.gehaltCent"}, template = "${}") + private long gehaltCent; + + @Adapt(ref = {"Anstellungsform.istEigenerReferenzwert"}, template = "${}") + private boolean istEigenerReferenzwert; + + @Adapt(ref = {"Anstellungsform.kommentar"}, template = "${}") + private List kommentar; + + @Adapt(ref = {"Anstellungsform.berufsgruppe"}, template = "${}") + private Optional berufsgruppe; + + @Adapt(ref = {"Anstellungsform.aktion"}, template = "${}") + private Optional aktion; + + @Adapt(ref = {"Anstellungsform.aktionsDatum"}, template = "${}") + private Optional aktionsDatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Arbeitspaket.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Arbeitspaket.java new file mode 100644 index 00000000..4b173e47 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Arbeitspaket.java @@ -0,0 +1,31 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Arbeitspaket"}, template = "${}") +public class Arbeitspaket { + @Adapt(ref = {"Arbeitspaket.nummer"}, template = "${}") + private String nummer; + + @Adapt(ref = {"Arbeitspaket.name"}, template = "${}") + private String name; + + @Adapt(ref = {"Arbeitspaket.beschreibung"}, template = "${}") + private Optional beschreibung; + + @Adapt(ref = {"Arbeitspaket.beginnDatum"}, template = "${}") + private Optional beginnDatum; + + @Adapt(ref = {"Arbeitspaket.endeDatum"}, template = "${}") + private Optional endeDatum; + + @Adapt(ref = {"Arbeitspaket.pMs"}, template = "${}") + private Optional pMs; + + @Adapt(ref = {"Arbeitspaket.stunden"}, template = "${}") + private Optional stunden; + + @Adapt(ref = {"Arbeitspaket.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Arbeitstage.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Arbeitstage.java new file mode 100644 index 00000000..2e5b37ec --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Arbeitstage.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Arbeitstage"}, template = "${}") +public class Arbeitstage { + @Adapt(ref = {"Arbeitstage.guiltigAb"}, template = "${}") + private ZonedDateTime guiltigAb; + + @Adapt(ref = {"Arbeitstage.guiltigBis"}, template = "${}") + private ZonedDateTime guiltigBis; + + @Adapt(ref = {"Arbeitstage.wochenTage"}, template = "${}") + private List wochenTage; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Auftragsprojekt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Auftragsprojekt.java new file mode 100644 index 00000000..8b1e1687 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Auftragsprojekt.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Auftragsprojekt"}, template = "${}") +public class Auftragsprojekt extends Projekt { + @Adapt(ref = {"Auftragsprojekt.nummer"}, template = "${}") + private Optional nummer; + + @Adapt(ref = {"Auftragsprojekt.typ"}, template = "${}") + private ProjektTyp typ; + + @Adapt(ref = {"Auftragsprojekt.regelung"}, template = "${}") + private Optional regelung; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Aufwand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Aufwand.java new file mode 100644 index 00000000..a831ad52 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Aufwand.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Aufwand"}, template = "${}") +public class Aufwand { + @Adapt(ref = {"Aufwand.pM"}, template = "${}") + private long pM; + + @Adapt(ref = {"Aufwand.umfang"}, template = "${}") + private ZahlenWert umfang; + + @Adapt(ref = {"Aufwand.laufzeitVon"}, template = "${}") + private ZonedDateTime laufzeitVon; + + @Adapt(ref = {"Aufwand.laufzeitBis"}, template = "${}") + private ZonedDateTime laufzeitBis; + + @Adapt(ref = {"Aufwand.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Begruendung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Begruendung.java new file mode 100644 index 00000000..af718165 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Begruendung.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Begruendung"}, template = "${}") +public class Begruendung { + @Adapt(ref = {"Begruendung.text"}, template = "${}") + private String text; + + @Adapt(ref = {"Begruendung.erstellung"}, template = "${}") + private ZonedDateTime erstellung; + + @Adapt(ref = {"Begruendung.letzteBearbeitung"}, template = "${}") + private Optional letzteBearbeitung; + + @Adapt(ref = {"Begruendung.geschaeftsjahr"}, template = "${}") + private Optional geschaeftsjahr; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Berufsgruppe.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Berufsgruppe.java new file mode 100644 index 00000000..bd90b979 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Berufsgruppe.java @@ -0,0 +1,30 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Berufsgruppe { + NONE, + E1, + E2, + E3, + E4, + E5, + E6, + E7, + E8, + E9, + E10, + E11, + E12, + E13, + E14, + E15, + E16, + E17, + E18, + E19, + E20, + E21, + E22, + E23, + E24, + E25; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BeschaeftigungsArt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BeschaeftigungsArt.java new file mode 100644 index 00000000..f48d222d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BeschaeftigungsArt.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum BeschaeftigungsArt { + WiMi, + HiWi, + BTV, + BEAMTE, + AZUBI, + PLAN; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BlacklistedToken.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BlacklistedToken.java new file mode 100644 index 00000000..2322d91d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BlacklistedToken.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"BlacklistedToken"}, template = "${}") +public class BlacklistedToken { + @Adapt(ref = {"BlacklistedToken.token"}, template = "${}") + private String token; + + @Adapt(ref = {"BlacklistedToken.addedAt"}, template = "${}") + private ZonedDateTime addedAt; + + @Adapt(ref = {"BlacklistedToken.expiresAt"}, template = "${}") + private ZonedDateTime expiresAt; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Buchung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Buchung.java new file mode 100644 index 00000000..9a4b53bd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Buchung.java @@ -0,0 +1,82 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Buchung"}, template = "${}") +public class Buchung extends Buchungseintrag { + @Adapt(ref = {"Buchung.belegdatum"}, template = "${}") + private ZonedDateTime belegdatum; + + @Adapt(ref = {"Buchung.zahlungsgrund"}, template = "${}") + private String zahlungsgrund; + + @Adapt(ref = {"Buchung.kreditorDebitor"}, template = "${}") + private Optional kreditorDebitor; + + @Adapt(ref = {"Buchung.sachkonto"}, template = "${}") + private List sachkonto; + + @Adapt(ref = {"Buchung.buchungsdatum"}, template = "${}") + private Optional buchungsdatum; + + @Adapt(ref = {"Buchung.status"}, template = "${}") + private BuchungsStatus status; + + @Adapt(ref = {"Buchung.belegnummern"}, template = "${}") + private List belegnummern; + + @Adapt(ref = {"Buchung.auftragsnummer"}, template = "${}") + private List auftragsnummer; + + @Adapt(ref = {"Buchung.bereich"}, template = "${}") + private Optional bereich; + + @Adapt(ref = {"Buchung.projekt"}, template = "${}") + private Optional projekt; + + @Adapt(ref = {"Buchung.geschaeftsjahr"}, template = "${}") + private Optional geschaeftsjahr; + + @Adapt(ref = {"Buchung.steuer"}, template = "${}") + private Optional steuer; + + @Adapt(ref = {"Buchung.skonto"}, template = "${}") + private Optional skonto; + + @Adapt(ref = {"Buchung.gez_Skonto"}, template = "${}") + private Optional gez_Skonto; + + @Adapt(ref = {"Buchung.erfasser"}, template = "${}") + private Optional erfasser; + + @Adapt(ref = {"Buchung.belegart"}, template = "${}") + private Optional belegart; + + @Adapt(ref = {"Buchung.referenz"}, template = "${}") + private Optional referenz; + + @Adapt(ref = {"Buchung.stornonummer"}, template = "${}") + private Optional stornonummer; + + @Adapt(ref = {"Buchung.stkz"}, template = "${}") + private Optional stkz; + + @Adapt(ref = {"Buchung.erfassungsdatum"}, template = "${}") + private Optional erfassungsdatum; + + @Adapt(ref = {"Buchung.ausgleichsdatum"}, template = "${}") + private Optional ausgleichsdatum; + + @Adapt(ref = {"Buchung.betragCentOriginal"}, template = "${}") + private long betragCentOriginal; + + @Adapt(ref = {"Buchung.budgetChangedManually"}, template = "${}") + private boolean budgetChangedManually; + + @Adapt(ref = {"Buchung.kostenarten"}, template = "${}") + private Optional kostenarten; + + @Adapt(ref = {"Buchung.bezugsdatumJahr"}, template = "${}") + private Optional bezugsdatumJahr; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BuchungsStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BuchungsStatus.java new file mode 100644 index 00000000..36651a4c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/BuchungsStatus.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum BuchungsStatus { + EINGEREICHT, + SAP, + SAP_STORNIERT, + PLANUNG, + FEHLERHAFT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Buchungseintrag.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Buchungseintrag.java new file mode 100644 index 00000000..2bcc40c6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Buchungseintrag.java @@ -0,0 +1,28 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Buchungseintrag"}, template = "${}") +public class Buchungseintrag { + @Adapt(ref = {"Buchungseintrag.datum"}, template = "${}") + private ZonedDateTime datum; + + @Adapt(ref = {"Buchungseintrag.buchungseintragText"}, template = "${}") + private Optional buchungseintragText; + + @Adapt(ref = {"Buchungseintrag.betragCent"}, template = "${}") + private long betragCent; + + @Adapt(ref = {"Buchungseintrag.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"Buchungseintrag.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + + @Adapt(ref = {"Buchungseintrag.gueltigerGeschaeftsvorgang"}, template = "${}") + private Geschaeftsvorgang gueltigerGeschaeftsvorgang; + + @Adapt(ref = {"Buchungseintrag.lfdeNummer"}, template = "${}") + private String lfdeNummer; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Budget.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Budget.java new file mode 100644 index 00000000..5bfa9c60 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Budget.java @@ -0,0 +1,40 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Budget"}, template = "${}") +public class Budget { + @Adapt(ref = {"Budget.typ"}, template = "${}") + private String typ; + + @Adapt(ref = {"Budget.startDatum"}, template = "${}") + private Optional startDatum; + + @Adapt(ref = {"Budget.endDatum"}, template = "${}") + private Optional endDatum; + + @Adapt(ref = {"Budget.kommentar"}, template = "${}") + private Optional kommentar; + + @Adapt(ref = {"Budget.budgetRahmenCent"}, template = "${}") + private Optional budgetRahmenCent; + + @Adapt(ref = {"Budget.eigenAnteilCent"}, template = "${}") + private Optional eigenAnteilCent; + + @Adapt(ref = {"Budget.jahresBudgets"}, template = "${}") + private List jahresBudgets; + + @Adapt(ref = {"Budget.proportionaleVerteilung"}, template = "${}") + private boolean proportionaleVerteilung; + + @Adapt(ref = {"Budget.forOverheads"}, template = "${}") + private boolean forOverheads; + + @Adapt(ref = {"Budget.budgetKategorie"}, template = "${}") + private Optional budgetKategorie; + + @Adapt(ref = {"Budget.budgetDepth"}, template = "${}") + private int budgetDepth; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/CardSetting.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/CardSetting.java new file mode 100644 index 00000000..ef495975 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/CardSetting.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"CardSetting"}, template = "${}") +public class CardSetting { + @Adapt(ref = {"CardSetting.page"}, template = "${}") + private String page; + + @Adapt(ref = {"CardSetting.identifier"}, template = "${}") + private String identifier; + + @Adapt(ref = {"CardSetting.configuration"}, template = "${}") + private String configuration; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/CommandLog.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/CommandLog.java new file mode 100644 index 00000000..0683ded8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/CommandLog.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"CommandLog"}, template = "${}") +public class CommandLog { + @Adapt(ref = {"CommandLog.timestamp"}, template = "${}") + private ZonedDateTime timestamp; + + @Adapt(ref = {"CommandLog.diff"}, template = "${}") + private List diff; + + @Adapt(ref = {"CommandLog.affectedArea"}, template = "${}") + private String affectedArea; + + @Adapt(ref = {"CommandLog.affectedObject"}, template = "${}") + private String affectedObject; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DefaultFristen.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DefaultFristen.java new file mode 100644 index 00000000..f9bd97b5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DefaultFristen.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DefaultFristen"}, template = "${}") +public class DefaultFristen { + @Adapt(ref = {"DefaultFristen.finanzenStartDatum"}, template = "${}") + private ZonedDateTime finanzenStartDatum; + + @Adapt(ref = {"DefaultFristen.finanzenFristinWeeks"}, template = "${}") + private Long finanzenFristinWeeks; + + @Adapt(ref = {"DefaultFristen.personalStartDatum"}, template = "${}") + private ZonedDateTime personalStartDatum; + + @Adapt(ref = {"DefaultFristen.personalFristinWeeks"}, template = "${}") + private Long personalFristinWeeks; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Dienstreise.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Dienstreise.java new file mode 100644 index 00000000..c781a874 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Dienstreise.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Dienstreise"}, template = "${}") +public class Dienstreise extends Abwesenheit { + @Adapt(ref = {"Dienstreise.reisenummer"}, template = "${}") + private Optional reisenummer; + + @Adapt(ref = {"Dienstreise.dienstreisegrund"}, template = "${}") + private Optional dienstreisegrund; + + @Adapt(ref = {"Dienstreise.reiseziel"}, template = "${}") + private Optional reiseziel; + + @Adapt(ref = {"Dienstreise.gesamtkosten"}, template = "${}") + private Optional gesamtkosten; + + @Adapt(ref = {"Dienstreise.status"}, template = "${}") + private Optional status; + + @Adapt(ref = {"Dienstreise.statusText"}, template = "${}") + private Optional statusText; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Dokument.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Dokument.java new file mode 100644 index 00000000..f8c0aeba --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Dokument.java @@ -0,0 +1,31 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Dokument"}, template = "${}") +public class Dokument { + @Adapt(ref = {"Dokument.groupName"}, template = "${}") + private DokumentGroup groupName; + + @Adapt(ref = {"Dokument.associationId"}, template = "${}") + private Long associationId; + + @Adapt(ref = {"Dokument.datum"}, template = "${}") + private ZonedDateTime datum; + + @Adapt(ref = {"Dokument.bezeichnung"}, template = "${}") + private String bezeichnung; + + @Adapt(ref = {"Dokument.dateiFormat"}, template = "${}") + private String dateiFormat; + + @Adapt(ref = {"Dokument.fileSize"}, template = "${}") + private Long fileSize; + + @Adapt(ref = {"Dokument.anmerkung"}, template = "${}") + private Optional anmerkung; + + @Adapt(ref = {"Dokument.istAktiv"}, template = "${}") + private boolean istAktiv; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DokumentGroup.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DokumentGroup.java new file mode 100644 index 00000000..7c3ccf34 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DokumentGroup.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum DokumentGroup { + PERSONAL, + KONTO; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Drittmittelprojekt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Drittmittelprojekt.java new file mode 100644 index 00000000..ff3e5b84 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Drittmittelprojekt.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Drittmittelprojekt"}, template = "${}") +public class Drittmittelprojekt extends ErweitertesKonto { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DruckEinstellungenPerson.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DruckEinstellungenPerson.java new file mode 100644 index 00000000..6cddbab3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DruckEinstellungenPerson.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DruckEinstellungenPerson"}, template = "${}") +public class DruckEinstellungenPerson { + @Adapt(ref = {"DruckEinstellungenPerson.personId"}, template = "${}") + private Long personId; + + @Adapt(ref = {"DruckEinstellungenPerson.stundensatz"}, template = "${}") + private Optional stundensatz; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DruckEinstellungenProjekt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DruckEinstellungenProjekt.java new file mode 100644 index 00000000..827ed751 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/DruckEinstellungenProjekt.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DruckEinstellungenProjekt"}, template = "${}") +public class DruckEinstellungenProjekt { + @Adapt(ref = {"DruckEinstellungenProjekt.projektId"}, template = "${}") + private Long projektId; + + @Adapt(ref = {"DruckEinstellungenProjekt.jahr"}, template = "${}") + private Integer jahr; + + @Adapt(ref = {"DruckEinstellungenProjekt.monat"}, template = "${}") + private Integer monat; + + @Adapt(ref = {"DruckEinstellungenProjekt.mittelabrufnummer"}, template = "${}") + private Optional mittelabrufnummer; + + @Adapt(ref = {"DruckEinstellungenProjekt.belegnummer"}, template = "${}") + private Optional belegnummer; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Elternzeit.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Elternzeit.java new file mode 100644 index 00000000..13b62c02 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Elternzeit.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Elternzeit"}, template = "${}") +public class Elternzeit { + @Adapt(ref = {"Elternzeit.von"}, template = "${}") + private Optional von; + + @Adapt(ref = {"Elternzeit.bis"}, template = "${}") + private Optional bis; + + @Adapt(ref = {"Elternzeit.umfang"}, template = "${}") + private ZahlenWert umfang; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ErweiterbareListen.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ErweiterbareListen.java new file mode 100644 index 00000000..48c0e0be --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ErweiterbareListen.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ErweiterbareListen"}, template = "${}") +public class ErweiterbareListen { + @Adapt(ref = {"ErweiterbareListen.bereich"}, template = "${}") + private String bereich; + + @Adapt(ref = {"ErweiterbareListen.feldbezeichnung"}, template = "${}") + private String feldbezeichnung; + + @Adapt(ref = {"ErweiterbareListen.inhalte"}, template = "${}") + private List inhalte; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ErweitertesKonto.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ErweitertesKonto.java new file mode 100644 index 00000000..a3e161da --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ErweitertesKonto.java @@ -0,0 +1,37 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ErweitertesKonto"}, template = "${}") +public class ErweitertesKonto extends Konto { + @Adapt(ref = {"ErweitertesKonto.startDatum"}, template = "${}") + private Optional startDatum; + + @Adapt(ref = {"ErweitertesKonto.endDatum"}, template = "${}") + private Optional endDatum; + + @Adapt(ref = {"ErweitertesKonto.bewilligungsDatum"}, template = "${}") + private Optional bewilligungsDatum; + + @Adapt(ref = {"ErweitertesKonto.verlaengertBisDatum"}, template = "${}") + private Optional verlaengertBisDatum; + + @Adapt(ref = {"ErweitertesKonto.aufstockungsDatum"}, template = "${}") + private Optional aufstockungsDatum; + + @Adapt(ref = {"ErweitertesKonto.foerderquote"}, template = "${}") + private Optional foerderquote; + + @Adapt(ref = {"ErweitertesKonto.aktenzeichen"}, template = "${}") + private Optional aktenzeichen; + + @Adapt(ref = {"ErweitertesKonto.finanzierteKomplettsumme"}, template = "${}") + private Optional finanzierteKomplettsumme; + + @Adapt(ref = {"ErweitertesKonto.referenzSponsor"}, template = "${}") + private Optional referenzSponsor; + + @Adapt(ref = {"ErweitertesKonto.hatProgrammpauschale"}, template = "${}") + private boolean hatProgrammpauschale; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventReport.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventReport.java new file mode 100644 index 00000000..3618aa05 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventReport.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"EventReport"}, template = "${}") +public class EventReport { + @Adapt(ref = {"EventReport.eventStart"}, template = "${}") + private ZonedDateTime eventStart; + + @Adapt(ref = {"EventReport.eventEnd"}, template = "${}") + private Optional eventEnd; + + @Adapt(ref = {"EventReport.status"}, template = "${}") + private EventStatus status; + + @Adapt(ref = {"EventReport.message"}, template = "${}") + private String message; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventReportEntry.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventReportEntry.java new file mode 100644 index 00000000..993c7f1a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventReportEntry.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"EventReportEntry"}, template = "${}") +public class EventReportEntry { + @Adapt(ref = {"EventReportEntry.type"}, template = "${}") + private EventType type; + + @Adapt(ref = {"EventReportEntry.targetId"}, template = "${}") + private Optional targetId; + + @Adapt(ref = {"EventReportEntry.count"}, template = "${}") + private Optional count; + + @Adapt(ref = {"EventReportEntry.message"}, template = "${}") + private String message; + + @Adapt(ref = {"EventReportEntry.status"}, template = "${}") + private EventStatus status; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventStatus.java new file mode 100644 index 00000000..045c42dc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventStatus.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum EventStatus { + SUCCESS, + ERROR, + INFO, + CREATED, + UPDATED, + IGNORED, + IN_PROGRESS, + SENT, + CORRECT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventType.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventType.java new file mode 100644 index 00000000..b29836f7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/EventType.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum EventType { + IMPORT_KONTO, + IMPORT_BUCHUNG, + IMPORT_PERSON, + IMPORT_INSTITUT, + IMPORT_NUTZER, + IMPORT_VERTRAG, + EXPORT_FINANZEN_KONTO, + EXPORT_FINANZEN_PLANSTELLE, + EXPORT_FINANZEN_VERTRAG; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ExternKonto.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ExternKonto.java new file mode 100644 index 00000000..d050a5d6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ExternKonto.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ExternKonto"}, template = "${}") +public class ExternKonto extends Konto { + @Adapt(ref = {"ExternKonto.startDatum"}, template = "${}") + private Optional startDatum; + + @Adapt(ref = {"ExternKonto.endDatum"}, template = "${}") + private Optional endDatum; + + @Adapt(ref = {"ExternKonto.importVermerk"}, template = "${}") + private Optional importVermerk; + + @Adapt(ref = {"ExternKonto.abgleichVerhalten"}, template = "${}") + private AbgleichType abgleichVerhalten; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/F1Konto.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/F1Konto.java new file mode 100644 index 00000000..3c42d6f6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/F1Konto.java @@ -0,0 +1,49 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"F1Konto"}, template = "${}") +public class F1Konto extends Haushaltskonto { + @Adapt(ref = {"F1Konto.startDatum"}, template = "${}") + private Optional startDatum; + + @Adapt(ref = {"F1Konto.endDatum"}, template = "${}") + private Optional endDatum; + + @Adapt(ref = {"F1Konto.originalBudgetCent"}, template = "${}") + private long originalBudgetCent; + + @Adapt(ref = {"F1Konto.sonstigeZuweisungenCent"}, template = "${}") + private long sonstigeZuweisungenCent; + + @Adapt(ref = {"F1Konto.resteCent"}, template = "${}") + private long resteCent; + + @Adapt(ref = {"F1Konto.aktuellerKontostandCent"}, template = "${}") + private long aktuellerKontostandCent; + + @Adapt(ref = {"F1Konto.kontoRahmenCent"}, template = "${}") + private long kontoRahmenCent; + + @Adapt(ref = {"F1Konto.kommunikationsStatus"}, template = "${}") + private KommunikationsStatus kommunikationsStatus; + + @Adapt(ref = {"F1Konto.strafsteuerStatus"}, template = "${}") + private StrafsteuerStatus strafsteuerStatus; + + @Adapt(ref = {"F1Konto.strafsteuerBasisCent"}, template = "${}") + private long strafsteuerBasisCent; + + @Adapt(ref = {"F1Konto.strafsteuerSAPCent"}, template = "${}") + private long strafsteuerSAPCent; + + @Adapt(ref = {"F1Konto.strafsteuerCent"}, template = "${}") + private long strafsteuerCent; + + @Adapt(ref = {"F1Konto.strafsteuerBerechnungsGrundlageCent"}, template = "${}") + private long strafsteuerBerechnungsGrundlageCent; + + @Adapt(ref = {"F1Konto.kontoStandBeginnGeschaeftsjahr"}, template = "${}") + private Optional kontoStandBeginnGeschaeftsjahr; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Fachgruppe.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Fachgruppe.java new file mode 100644 index 00000000..b8c3867c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Fachgruppe.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Fachgruppe { + MATHEMATIK, + INFORMATIK, + PHYSIK, + CHEMIE, + BIOLOGIE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Favorite.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Favorite.java new file mode 100644 index 00000000..a03b36a4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Favorite.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Favorite"}, template = "${}") +public class Favorite { + @Adapt(ref = {"Favorite.title"}, template = "${}") + private String title; + + @Adapt(ref = {"Favorite.url"}, template = "${}") + private String url; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/FilterSetting.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/FilterSetting.java new file mode 100644 index 00000000..13a8a6e4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/FilterSetting.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"FilterSetting"}, template = "${}") +public class FilterSetting extends UISetting { + @Adapt(ref = {"FilterSetting.filterValues"}, template = "${}") + private List filterValues; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Freitext.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Freitext.java new file mode 100644 index 00000000..9526b9c2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Freitext.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Freitext"}, template = "${}") +public class Freitext { + @Adapt(ref = {"Freitext.erstellDatum"}, template = "${}") + private Optional erstellDatum; + + @Adapt(ref = {"Freitext.bearbeitetDatum"}, template = "${}") + private Optional bearbeitetDatum; + + @Adapt(ref = {"Freitext.text"}, template = "${}") + private Optional text; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Frist.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Frist.java new file mode 100644 index 00000000..76c54d2b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Frist.java @@ -0,0 +1,37 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Frist"}, template = "${}") +public class Frist { + @Adapt(ref = {"Frist.beschreibung"}, template = "${}") + private String beschreibung; + + @Adapt(ref = {"Frist.status"}, template = "${}") + private Friststatus status; + + @Adapt(ref = {"Frist.fristobjekt"}, template = "${}") + private Fristobjekt fristobjekt; + + @Adapt(ref = {"Frist.faelligkeitsdatum"}, template = "${}") + private ZonedDateTime faelligkeitsdatum; + + @Adapt(ref = {"Frist.notiz"}, template = "${}") + private Optional notiz; + + @Adapt(ref = {"Frist.manuellerEintrag"}, template = "${}") + private Boolean manuellerEintrag; + + @Adapt(ref = {"Frist.bezugsobjektId"}, template = "${}") + private Long bezugsobjektId; + + @Adapt(ref = {"Frist.bezugsobjektName"}, template = "${}") + private String bezugsobjektName; + + @Adapt(ref = {"Frist.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + + @Adapt(ref = {"Frist.istAktiv"}, template = "${}") + private boolean istAktiv; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Fristobjekt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Fristobjekt.java new file mode 100644 index 00000000..0bdc48d7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Fristobjekt.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Fristobjekt { + PERSON, + KONTO, + VERTRAGSVERLAENGERUNG, + ZULAGE, + RECHNUNG, + MITTELABRUF, + PROJEKT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Friststatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Friststatus.java new file mode 100644 index 00000000..b1cfbb67 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Friststatus.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Friststatus { + PLANUNG, + IN_BEARBEITUNG, + ERLEDIGT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/GeschaeftsjahrSAPAbschoepfung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/GeschaeftsjahrSAPAbschoepfung.java new file mode 100644 index 00000000..a6df3791 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/GeschaeftsjahrSAPAbschoepfung.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"GeschaeftsjahrSAPAbschoepfung"}, template = "${}") +public class GeschaeftsjahrSAPAbschoepfung { + @Adapt(ref = {"GeschaeftsjahrSAPAbschoepfung.fachgruppe"}, template = "${}") + private String fachgruppe; + + @Adapt(ref = {"GeschaeftsjahrSAPAbschoepfung.institutsKennZiffer"}, template = "${}") + private String institutsKennZiffer; + + @Adapt(ref = {"GeschaeftsjahrSAPAbschoepfung.institutsName"}, template = "${}") + private String institutsName; + + @Adapt(ref = {"GeschaeftsjahrSAPAbschoepfung.pspElement"}, template = "${}") + private String pspElement; + + @Adapt(ref = {"GeschaeftsjahrSAPAbschoepfung.gesamtSAPAbschoepfung"}, template = "${}") + private long gesamtSAPAbschoepfung; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Geschaeftsvorgang.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Geschaeftsvorgang.java new file mode 100644 index 00000000..92876aee --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Geschaeftsvorgang.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Geschaeftsvorgang { + NONE, + MITTELABRUF, + ZUWEISUNG, + RECHNUNG; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Geschlecht.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Geschlecht.java new file mode 100644 index 00000000..1758c5f7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Geschlecht.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Geschlecht { + NONE, + WEIBLICH, + MAENNLICH, + DIVERS; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/GroupedAccessPolicy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/GroupedAccessPolicy.java new file mode 100644 index 00000000..fa887c2c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/GroupedAccessPolicy.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"GroupedAccessPolicy"}, template = "${}") +public class GroupedAccessPolicy { + @Adapt(ref = {"GroupedAccessPolicy.name"}, template = "${}") + private String name; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Haushaltskonto.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Haushaltskonto.java new file mode 100644 index 00000000..00b44f22 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Haushaltskonto.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Haushaltskonto"}, template = "${}") +public class Haushaltskonto extends Konto { + @Adapt(ref = {"Haushaltskonto.startDatum"}, template = "${}") + private Optional startDatum; + + @Adapt(ref = {"Haushaltskonto.endDatum"}, template = "${}") + private Optional endDatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ImportReport.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ImportReport.java new file mode 100644 index 00000000..f16b705a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ImportReport.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ImportReport"}, template = "${}") +public class ImportReport extends EventReport { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ImportReportEntry.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ImportReportEntry.java new file mode 100644 index 00000000..f7ad81c8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ImportReportEntry.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ImportReportEntry"}, template = "${}") +public class ImportReportEntry extends EventReportEntry { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Industrieprojekt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Industrieprojekt.java new file mode 100644 index 00000000..98df2b99 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Industrieprojekt.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Industrieprojekt"}, template = "${}") +public class Industrieprojekt extends ErweitertesKonto { + @Adapt(ref = {"Industrieprojekt.auftraggeber"}, template = "${}") + private Optional auftraggeber; + + @Adapt(ref = {"Industrieprojekt.istSammelkonto"}, template = "${}") + private boolean istSammelkonto; + + @Adapt(ref = {"Industrieprojekt.mehrwertsteuer"}, template = "${}") + private Optional mehrwertsteuer; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/InstanzSetting.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/InstanzSetting.java new file mode 100644 index 00000000..c70815de --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/InstanzSetting.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"InstanzSetting"}, template = "${}") +public class InstanzSetting extends Setting { + @Adapt(ref = {"InstanzSetting.value"}, template = "${}") + private String value; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Institut.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Institut.java new file mode 100644 index 00000000..c0fc5281 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Institut.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Institut"}, template = "${}") +public class Institut { + @Adapt(ref = {"Institut.institutsKennZiffer"}, template = "${}") + private String institutsKennZiffer; + + @Adapt(ref = {"Institut.institutsName"}, template = "${}") + private String institutsName; + + @Adapt(ref = {"Institut.professorName"}, template = "${}") + private String professorName; + + @Adapt(ref = {"Institut.lokalInstitut"}, template = "${}") + private boolean lokalInstitut; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Jahresurlaub.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Jahresurlaub.java new file mode 100644 index 00000000..676077e6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Jahresurlaub.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Jahresurlaub"}, template = "${}") +public class Jahresurlaub { + @Adapt(ref = {"Jahresurlaub.jahr"}, template = "${}") + private int jahr; + + @Adapt(ref = {"Jahresurlaub.tageAnzahl"}, template = "${}") + private long tageAnzahl; + + @Adapt(ref = {"Jahresurlaub.stundenAnzahl"}, template = "${}") + private Optional stundenAnzahl; + + @Adapt(ref = {"Jahresurlaub.istManuellerEintrag"}, template = "${}") + private boolean istManuellerEintrag; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/KommunikationsStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/KommunikationsStatus.java new file mode 100644 index 00000000..1592bef2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/KommunikationsStatus.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum KommunikationsStatus { + KOM_STAT_NO_ACTION_NEEDED, + KOM_STAT_BEGR_FEHLT, + KOM_STAT_ANTW_FEHLT, + KOM_STAT_ANTW_CHANGED, + KOM_STAT_BEGR_CHANGED, + KOM_STAT_ANTW_BEGR_CHANGED, + KOM_STAT_ANTW_FEHLT_CHANGED; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Konto.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Konto.java new file mode 100644 index 00000000..7745ddce --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Konto.java @@ -0,0 +1,49 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Konto"}, template = "${}") +public class Konto { + @Adapt(ref = {"Konto.name"}, template = "${}") + private String name; + + @Adapt(ref = {"Konto.pspElement"}, template = "${}") + private Optional pspElement; + + @Adapt(ref = {"Konto.kontotyp"}, template = "${}") + private Optional kontotyp; + + @Adapt(ref = {"Konto.sapDatum"}, template = "${}") + private Optional sapDatum; + + @Adapt(ref = {"Konto.internesAktenzeichen"}, template = "${}") + private Optional internesAktenzeichen; + + @Adapt(ref = {"Konto.istPlanKonto"}, template = "${}") + private boolean istPlanKonto; + + @Adapt(ref = {"Konto.istVerbuchungsKonto"}, template = "${}") + private boolean istVerbuchungsKonto; + + @Adapt(ref = {"Konto.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"Konto.vergabeVerordnung"}, template = "${}") + private Optional vergabeVerordnung; + + @Adapt(ref = {"Konto.farbe"}, template = "${}") + private Optional farbe; + + @Adapt(ref = {"Konto.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + + @Adapt(ref = {"Konto.optStartDatum"}, template = "${}") + private Optional optStartDatum; + + @Adapt(ref = {"Konto.optEndDatum"}, template = "${}") + private Optional optEndDatum; + + @Adapt(ref = {"Konto.gueltigerGeschaeftsvorgang"}, template = "${}") + private Geschaeftsvorgang gueltigerGeschaeftsvorgang; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Kostenstelle.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Kostenstelle.java new file mode 100644 index 00000000..8f269d0c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Kostenstelle.java @@ -0,0 +1,31 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Kostenstelle"}, template = "${}") +public class Kostenstelle { + @Adapt(ref = {"Kostenstelle.verbuchungsBeginn"}, template = "${}") + private ZonedDateTime verbuchungsBeginn; + + @Adapt(ref = {"Kostenstelle.verbuchungsEnde"}, template = "${}") + private ZonedDateTime verbuchungsEnde; + + @Adapt(ref = {"Kostenstelle.bezeichnung"}, template = "${}") + private KostenstelleBezeichnung bezeichnung; + + @Adapt(ref = {"Kostenstelle.kommentar"}, template = "${}") + private List kommentar; + + @Adapt(ref = {"Kostenstelle.beschaeftigungsUmfang"}, template = "${}") + private ZahlenWert beschaeftigungsUmfang; + + @Adapt(ref = {"Kostenstelle.buchungenNeuErzeugen"}, template = "${}") + private boolean buchungenNeuErzeugen; + + @Adapt(ref = {"Kostenstelle.buchungenLoeschen"}, template = "${}") + private boolean buchungenLoeschen; + + @Adapt(ref = {"Kostenstelle.gesperrt"}, template = "${}") + private boolean gesperrt; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/KostenstelleBezeichnung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/KostenstelleBezeichnung.java new file mode 100644 index 00000000..75daa7ba --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/KostenstelleBezeichnung.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum KostenstelleBezeichnung { + NONE, + AN_INSTITUT, + GMBH, + ANDERER_LEHRSTUHL, + ANDERES_INSTITUT, + VORUEBERGEHEND_ABWESEND, + ANDERE_FINANZIERUNG; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Legende.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Legende.java new file mode 100644 index 00000000..cc357b0b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Legende.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Legende"}, template = "${}") +public class Legende { + @Adapt(ref = {"Legende.type"}, template = "${}") + private LegendeTyp type; + + @Adapt(ref = {"Legende.expression"}, template = "${}") + private String expression; + + @Adapt(ref = {"Legende.description"}, template = "${}") + private String description; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/LegendeTyp.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/LegendeTyp.java new file mode 100644 index 00000000..de541e2e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/LegendeTyp.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum LegendeTyp { + KONTO_FARBE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Lehre.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Lehre.java new file mode 100644 index 00000000..8ebfd407 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Lehre.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Lehre"}, template = "${}") +public class Lehre extends Projekt { + @Adapt(ref = {"Lehre.stunden"}, template = "${}") + private Optional stunden; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MacocoUser.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MacocoUser.java new file mode 100644 index 00000000..7638e00a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MacocoUser.java @@ -0,0 +1,43 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"MacocoUser"}, template = "${}") +public class MacocoUser { + @Adapt(ref = {"MacocoUser.username"}, template = "${}") + private String username; + + @Adapt(ref = {"MacocoUser.encodedPassword"}, template = "${}") + private Optional encodedPassword; + + @Adapt(ref = {"MacocoUser.passwordSaltBase64"}, template = "${}") + private String passwordSaltBase64; + + @Adapt(ref = {"MacocoUser.registrationDate"}, template = "${}") + private ZonedDateTime registrationDate; + + @Adapt(ref = {"MacocoUser.initials"}, template = "${}") + private Optional initials; + + @Adapt(ref = {"MacocoUser.activated"}, template = "${}") + private MacocoUserActivationStatus activated; + + @Adapt(ref = {"MacocoUser.enabled"}, template = "${}") + private boolean enabled; + + @Adapt(ref = {"MacocoUser.email"}, template = "${}") + private String email; + + @Adapt(ref = {"MacocoUser.authentifiziert"}, template = "${}") + private boolean authentifiziert; + + @Adapt(ref = {"MacocoUser.timID"}, template = "${}") + private Optional timID; + + @Adapt(ref = {"MacocoUser.sapAccessToken"}, template = "${}") + private Optional sapAccessToken; + + @Adapt(ref = {"MacocoUser.sapRefreshToKen"}, template = "${}") + private Optional sapRefreshToKen; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MacocoUserActivationStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MacocoUserActivationStatus.java new file mode 100644 index 00000000..ffaaac92 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MacocoUserActivationStatus.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum MacocoUserActivationStatus { + AKTIVIERT, + MAIL_NICHT_GESENDET, + MAIL_FEHLERHAFT, + MAIL_GESENDET; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MailAlert.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MailAlert.java new file mode 100644 index 00000000..6f118b40 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MailAlert.java @@ -0,0 +1,52 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"MailAlert"}, template = "${}") +public class MailAlert { + @Adapt(ref = {"MailAlert.name"}, template = "${}") + private String name; + + @Adapt(ref = {"MailAlert.alertCondition"}, template = "${}") + private String alertCondition; + + @Adapt(ref = {"MailAlert.mailBody"}, template = "${}") + private String mailBody; + + @Adapt(ref = {"MailAlert.repetitions"}, template = "${}") + private int repetitions; + + @Adapt(ref = {"MailAlert.firstAlert"}, template = "${}") + private ZonedDateTime firstAlert; + + @Adapt(ref = {"MailAlert.lastAlert"}, template = "${}") + private ZonedDateTime lastAlert; + + @Adapt(ref = {"MailAlert.receipients"}, template = "${}") + private Optional receipients; + + @Adapt(ref = {"MailAlert.ccs"}, template = "${}") + private Optional ccs; + + @Adapt(ref = {"MailAlert.bccs"}, template = "${}") + private Optional bccs; + + @Adapt(ref = {"MailAlert.a1"}, template = "${}") + private long a1; + + @Adapt(ref = {"MailAlert.a2"}, template = "${}") + private long a2; + + @Adapt(ref = {"MailAlert.flag1"}, template = "${}") + private boolean flag1; + + @Adapt(ref = {"MailAlert.flag2"}, template = "${}") + private boolean flag2; + + @Adapt(ref = {"MailAlert.s1"}, template = "${}") + private String s1; + + @Adapt(ref = {"MailAlert.s2"}, template = "${}") + private String s2; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Mittelabruf.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Mittelabruf.java new file mode 100644 index 00000000..a12b1f1b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Mittelabruf.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Mittelabruf"}, template = "${}") +public class Mittelabruf extends Buchungseintrag { + @Adapt(ref = {"Mittelabruf.abrufdatum"}, template = "${}") + private ZonedDateTime abrufdatum; + + @Adapt(ref = {"Mittelabruf.status"}, template = "${}") + private MittelabrufStatus status; + + @Adapt(ref = {"Mittelabruf.zeitraum"}, template = "${}") + private String zeitraum; + + @Adapt(ref = {"Mittelabruf.pauschaleManuell"}, template = "${}") + private Optional pauschaleManuell; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MittelabrufStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MittelabrufStatus.java new file mode 100644 index 00000000..0af37dde --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MittelabrufStatus.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum MittelabrufStatus { + ABGERUFEN, + SAP, + SAP_STORNIERT, + PLANUNG, + EINGEREICHT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Mittelzuweisung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Mittelzuweisung.java new file mode 100644 index 00000000..a70c8267 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Mittelzuweisung.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Mittelzuweisung"}, template = "${}") +public class Mittelzuweisung extends Buchungseintrag { + @Adapt(ref = {"Mittelzuweisung.verfallDatum"}, template = "${}") + private Optional verfallDatum; + + @Adapt(ref = {"Mittelzuweisung.kennung"}, template = "${}") + private Optional kennung; + + @Adapt(ref = {"Mittelzuweisung.status"}, template = "${}") + private MittelzuweisungStatus status; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MittelzuweisungStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MittelzuweisungStatus.java new file mode 100644 index 00000000..53d7ad69 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/MittelzuweisungStatus.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum MittelzuweisungStatus { + BESCHEID_ERHALTEN, + SAP, + SAP_STORNIERT, + PLANUNG, + FEHLERHAFT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Nebentaetigkeit.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Nebentaetigkeit.java new file mode 100644 index 00000000..c2cd08b8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Nebentaetigkeit.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Nebentaetigkeit"}, template = "${}") +public class Nebentaetigkeit { + @Adapt(ref = {"Nebentaetigkeit.von"}, template = "${}") + private Optional von; + + @Adapt(ref = {"Nebentaetigkeit.bis"}, template = "${}") + private Optional bis; + + @Adapt(ref = {"Nebentaetigkeit.umfang"}, template = "${}") + private ZahlenWert umfang; + + @Adapt(ref = {"Nebentaetigkeit.arbeitgeber"}, template = "${}") + private Optional arbeitgeber; + + @Adapt(ref = {"Nebentaetigkeit.taetigkeitsbereich"}, template = "${}") + private Optional taetigkeitsbereich; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Organigramm.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Organigramm.java new file mode 100644 index 00000000..6eafb595 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Organigramm.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Organigramm"}, template = "${}") +public class Organigramm { + @Adapt(ref = {"Organigramm.istStabstelle"}, template = "${}") + private boolean istStabstelle; + + @Adapt(ref = {"Organigramm.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"Organigramm.bezeichnung"}, template = "${}") + private String bezeichnung; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/OrganigrammKonto.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/OrganigrammKonto.java new file mode 100644 index 00000000..d80aaea8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/OrganigrammKonto.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"OrganigrammKonto"}, template = "${}") +public class OrganigrammKonto { + @Adapt(ref = {"OrganigrammKonto.von"}, template = "${}") + private Optional von; + + @Adapt(ref = {"OrganigrammKonto.bis"}, template = "${}") + private Optional bis; + + @Adapt(ref = {"OrganigrammKonto.umfang"}, template = "${}") + private Optional umfang; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/OrganigrammPersonInfo.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/OrganigrammPersonInfo.java new file mode 100644 index 00000000..6aac8b45 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/OrganigrammPersonInfo.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"OrganigrammPersonInfo"}, template = "${}") +public class OrganigrammPersonInfo { + @Adapt(ref = {"OrganigrammPersonInfo.von"}, template = "${}") + private Optional von; + + @Adapt(ref = {"OrganigrammPersonInfo.bis"}, template = "${}") + private Optional bis; + + @Adapt(ref = {"OrganigrammPersonInfo.umfang"}, template = "${}") + private Optional umfang; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Organisation.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Organisation.java new file mode 100644 index 00000000..18bc55a3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Organisation.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Organisation"}, template = "${}") +public class Organisation extends Projekt { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PSPImportFilter.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PSPImportFilter.java new file mode 100644 index 00000000..9a31372d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PSPImportFilter.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"PSPImportFilter"}, template = "${}") +public class PSPImportFilter { + @Adapt(ref = {"PSPImportFilter.importThisPSPelement"}, template = "${}") + private boolean importThisPSPelement; + + @Adapt(ref = {"PSPImportFilter.importBookings"}, template = "${}") + private boolean importBookings; + + @Adapt(ref = {"PSPImportFilter.name"}, template = "${}") + private String name; + + @Adapt(ref = {"PSPImportFilter.pspElement"}, template = "${}") + private String pspElement; + + @Adapt(ref = {"PSPImportFilter.startDate"}, template = "${}") + private Optional startDate; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Person.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Person.java new file mode 100644 index 00000000..44e13235 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Person.java @@ -0,0 +1,103 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Person"}, template = "${}") +public class Person { + @Adapt(ref = {"Person.vorname"}, template = "${}") + private String vorname; + + @Adapt(ref = {"Person.vorsatzwort"}, template = "${}") + private Optional vorsatzwort; + + @Adapt(ref = {"Person.nachname"}, template = "${}") + private String nachname; + + @Adapt(ref = {"Person.kuerzel"}, template = "${}") + private String kuerzel; + + @Adapt(ref = {"Person.personalnummer"}, template = "${}") + private Optional personalnummer; + + @Adapt(ref = {"Person.gebDatum"}, template = "${}") + private Optional gebDatum; + + @Adapt(ref = {"Person.beschBeginn"}, template = "${}") + private Optional beschBeginn; + + @Adapt(ref = {"Person.beschEnde"}, template = "${}") + private Optional beschEnde; + + @Adapt(ref = {"Person.kommentar"}, template = "${}") + private List kommentar; + + @Adapt(ref = {"Person.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"Person.istStundenzettelpflichtig"}, template = "${}") + private boolean istStundenzettelpflichtig; + + @Adapt(ref = {"Person.stundenzettelpflichtigVon"}, template = "${}") + private Optional stundenzettelpflichtigVon; + + @Adapt(ref = {"Person.stundenzettelpflichtigBis"}, template = "${}") + private Optional stundenzettelpflichtigBis; + + @Adapt(ref = {"Person.ueberStundenUebertrag"}, template = "${}") + private Optional ueberStundenUebertrag; + + @Adapt(ref = {"Person.darfWochenendarbeiten"}, template = "${}") + private boolean darfWochenendarbeiten; + + @Adapt(ref = {"Person.hoechstbeschaeftigungBis"}, template = "${}") + private Optional hoechstbeschaeftigungBis; + + @Adapt(ref = {"Person.staatsangehoerigkeiten"}, template = "${}") + private List staatsangehoerigkeiten; + + @Adapt(ref = {"Person.telefonnummer"}, template = "${}") + private Optional telefonnummer; + + @Adapt(ref = {"Person.arbeitserlaubnisBis"}, template = "${}") + private Optional arbeitserlaubnisBis; + + @Adapt(ref = {"Person.extern"}, template = "${}") + private boolean extern; + + @Adapt(ref = {"Person.titel"}, template = "${}") + private Optional titel; + + @Adapt(ref = {"Person.rufname"}, template = "${}") + private Optional rufname; + + @Adapt(ref = {"Person.adresse"}, template = "${}") + private Optional adresse; + + @Adapt(ref = {"Person.email"}, template = "${}") + private Optional email; + + @Adapt(ref = {"Person.geburtsname"}, template = "${}") + private Optional geburtsname; + + @Adapt(ref = {"Person.geschlecht"}, template = "${}") + private Optional geschlecht; + + @Adapt(ref = {"Person.lBVNummer"}, template = "${}") + private Optional lBVNummer; + + @Adapt(ref = {"Person.entfristung"}, template = "${}") + private Optional entfristung; + + @Adapt(ref = {"Person.qualifikationstitel"}, template = "${}") + private Optional qualifikationstitel; + + @Adapt(ref = {"Person.speicherungGewuenscht"}, template = "${}") + private boolean speicherungGewuenscht; + + @Adapt(ref = {"Person.urlaubskommentar"}, template = "${}") + private Optional urlaubskommentar; + + @Adapt(ref = {"Person.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportData.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportData.java new file mode 100644 index 00000000..0f50f598 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportData.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"PersonalExportData"}, template = "${}") +public class PersonalExportData { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportDataEntry.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportDataEntry.java new file mode 100644 index 00000000..e1242460 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportDataEntry.java @@ -0,0 +1,31 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"PersonalExportDataEntry"}, template = "${}") +public class PersonalExportDataEntry { + @Adapt(ref = {"PersonalExportDataEntry.personalnummer"}, template = "${}") + private String personalnummer; + + @Adapt(ref = {"PersonalExportDataEntry.vorsatzwort"}, template = "${}") + private Optional vorsatzwort; + + @Adapt(ref = {"PersonalExportDataEntry.nachname"}, template = "${}") + private String nachname; + + @Adapt(ref = {"PersonalExportDataEntry.vorname"}, template = "${}") + private String vorname; + + @Adapt(ref = {"PersonalExportDataEntry.von"}, template = "${}") + private ZonedDateTime von; + + @Adapt(ref = {"PersonalExportDataEntry.bis"}, template = "${}") + private ZonedDateTime bis; + + @Adapt(ref = {"PersonalExportDataEntry.pspElement"}, template = "${}") + private String pspElement; + + @Adapt(ref = {"PersonalExportDataEntry.prozentsatz"}, template = "${}") + private Long prozentsatz; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportEinstellungen.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportEinstellungen.java new file mode 100644 index 00000000..e0ef5c5b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportEinstellungen.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"PersonalExportEinstellungen"}, template = "${}") +public class PersonalExportEinstellungen { + @Adapt(ref = {"PersonalExportEinstellungen.pspElement"}, template = "${}") + private String pspElement; + + @Adapt(ref = {"PersonalExportEinstellungen.bezeichnungen"}, template = "${}") + private List bezeichnungen; + + @Adapt(ref = {"PersonalExportEinstellungen.startmonat"}, template = "${}") + private ZonedDateTime startmonat; + + @Adapt(ref = {"PersonalExportEinstellungen.endmonat"}, template = "${}") + private ZonedDateTime endmonat; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportReport.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportReport.java new file mode 100644 index 00000000..9e15c8e7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportReport.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"PersonalExportReport"}, template = "${}") +public class PersonalExportReport extends EventReport { + @Adapt(ref = {"PersonalExportReport.dataId"}, template = "${}") + private Long dataId; + + @Adapt(ref = {"PersonalExportReport.datenStart"}, template = "${}") + private ZonedDateTime datenStart; + + @Adapt(ref = {"PersonalExportReport.datenEnd"}, template = "${}") + private ZonedDateTime datenEnd; + + @Adapt(ref = {"PersonalExportReport.countData"}, template = "${}") + private Long countData; + + @Adapt(ref = {"PersonalExportReport.countError"}, template = "${}") + private Long countError; + + @Adapt(ref = {"PersonalExportReport.exportStatus"}, template = "${}") + private EventStatus exportStatus; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportReportEntry.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportReportEntry.java new file mode 100644 index 00000000..ee2a9df3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/PersonalExportReportEntry.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"PersonalExportReportEntry"}, template = "${}") +public class PersonalExportReportEntry extends EventReportEntry { + @Adapt(ref = {"PersonalExportReportEntry.vorsatzwort"}, template = "${}") + private Optional vorsatzwort; + + @Adapt(ref = {"PersonalExportReportEntry.vorname"}, template = "${}") + private String vorname; + + @Adapt(ref = {"PersonalExportReportEntry.nachname"}, template = "${}") + private String nachname; + + @Adapt(ref = {"PersonalExportReportEntry.beschaeftigungsArt"}, template = "${}") + private BeschaeftigungsArt beschaeftigungsArt; + + @Adapt(ref = {"PersonalExportReportEntry.monat"}, template = "${}") + private ZonedDateTime monat; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Planstelle.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Planstelle.java new file mode 100644 index 00000000..f8cabc69 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Planstelle.java @@ -0,0 +1,37 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Planstelle"}, template = "${}") +public class Planstelle { + @Adapt(ref = {"Planstelle.bezeichnung"}, template = "${}") + private String bezeichnung; + + @Adapt(ref = {"Planstelle.minEntgeltgruppe"}, template = "${}") + private Optional minEntgeltgruppe; + + @Adapt(ref = {"Planstelle.minEntgeltstufe"}, template = "${}") + private Optional minEntgeltstufe; + + @Adapt(ref = {"Planstelle.maxEntgeltgruppe"}, template = "${}") + private Optional maxEntgeltgruppe; + + @Adapt(ref = {"Planstelle.maxEntgeltstufe"}, template = "${}") + private Optional maxEntgeltstufe; + + @Adapt(ref = {"Planstelle.verfuegbarVon"}, template = "${}") + private Optional verfuegbarVon; + + @Adapt(ref = {"Planstelle.verfuegbarBis"}, template = "${}") + private Optional verfuegbarBis; + + @Adapt(ref = {"Planstelle.kommentar"}, template = "${}") + private Optional kommentar; + + @Adapt(ref = {"Planstelle.planUmfang"}, template = "${}") + private ZahlenWert planUmfang; + + @Adapt(ref = {"Planstelle.aktiv"}, template = "${}") + private boolean aktiv; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Projekt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Projekt.java new file mode 100644 index 00000000..30a3b07d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Projekt.java @@ -0,0 +1,49 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Projekt"}, template = "${}") +public class Projekt { + @Adapt(ref = {"Projekt.name"}, template = "${}") + private String name; + + @Adapt(ref = {"Projekt.kuerzel"}, template = "${}") + private String kuerzel; + + @Adapt(ref = {"Projekt.minPM"}, template = "${}") + private Optional minPM; + + @Adapt(ref = {"Projekt.maxPM"}, template = "${}") + private Optional maxPM; + + @Adapt(ref = {"Projekt.finanzPM"}, template = "${}") + private Optional finanzPM; + + @Adapt(ref = {"Projekt.laufzeitVon"}, template = "${}") + private Optional laufzeitVon; + + @Adapt(ref = {"Projekt.laufzeitBis"}, template = "${}") + private Optional laufzeitBis; + + @Adapt(ref = {"Projekt.kommentar"}, template = "${}") + private Optional kommentar; + + @Adapt(ref = {"Projekt.foerdergeberAZ"}, template = "${}") + private Optional foerdergeberAZ; + + @Adapt(ref = {"Projekt.status"}, template = "${}") + private ProjektStatus status; + + @Adapt(ref = {"Projekt.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"Projekt.lockedForStundenzettel"}, template = "${}") + private boolean lockedForStundenzettel; + + @Adapt(ref = {"Projekt.lockedMonths"}, template = "${}") + private List lockedMonths; + + @Adapt(ref = {"Projekt.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektArt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektArt.java new file mode 100644 index 00000000..f0ba382e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektArt.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum ProjektArt { + AUFTRAG, + ORGANISATION, + LEHRE, + NONE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektStatus.java new file mode 100644 index 00000000..c29fcabf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektStatus.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum ProjektStatus { + IN_DEFINITION, + BEANTRAGT, + GENEHMIGT, + ABGELEHNT, + LAUFEND, + FACHLICH_ABGESCHLOSSEN, + ENDBERICHT_EINGEREICHT, + INPRUEFUNG, + GEPRUEFT, + ABGERECHNET, + ABGESCHLOSSEN, + ARCHIVIERT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektTyp.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektTyp.java new file mode 100644 index 00000000..a7c370e6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ProjektTyp.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum ProjektTyp { + HOHEITLICH, + INDUSTRIE, + UNIVERSITAET, + SONSTIGE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Rechnungsstellung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Rechnungsstellung.java new file mode 100644 index 00000000..8669c2be --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Rechnungsstellung.java @@ -0,0 +1,37 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Rechnungsstellung"}, template = "${}") +public class Rechnungsstellung extends Buchungseintrag { + @Adapt(ref = {"Rechnungsstellung.rechnungsdatum"}, template = "${}") + private Optional rechnungsdatum; + + @Adapt(ref = {"Rechnungsstellung.rechnungsnummer"}, template = "${}") + private Optional rechnungsnummer; + + @Adapt(ref = {"Rechnungsstellung.status"}, template = "${}") + private RechnungsstellungStatus status; + + @Adapt(ref = {"Rechnungsstellung.belegnummern"}, template = "${}") + private List belegnummern; + + @Adapt(ref = {"Rechnungsstellung.gemeinkosten"}, template = "${}") + private Optional gemeinkosten; + + @Adapt(ref = {"Rechnungsstellung.honorierung"}, template = "${}") + private Optional honorierung; + + @Adapt(ref = {"Rechnungsstellung.projekt"}, template = "${}") + private Optional projekt; + + @Adapt(ref = {"Rechnungsstellung.debitor"}, template = "${}") + private Optional debitor; + + @Adapt(ref = {"Rechnungsstellung.mehrwertsteuer"}, template = "${}") + private Optional mehrwertsteuer; + + @Adapt(ref = {"Rechnungsstellung.zahlungsziel"}, template = "${}") + private Optional zahlungsziel; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RechnungsstellungStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RechnungsstellungStatus.java new file mode 100644 index 00000000..ae67b089 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RechnungsstellungStatus.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum RechnungsstellungStatus { + OFFENE_RECHNUNG, + SAP, + SAP_STORNIERT, + PLANUNG; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RefreshToken.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RefreshToken.java new file mode 100644 index 00000000..09c4653e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RefreshToken.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"RefreshToken"}, template = "${}") +public class RefreshToken { + @Adapt(ref = {"RefreshToken.userId"}, template = "${}") + private long userId; + + @Adapt(ref = {"RefreshToken.token"}, template = "${}") + private String token; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RoleAssignment.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RoleAssignment.java new file mode 100644 index 00000000..ff120f40 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/RoleAssignment.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"RoleAssignment"}, template = "${}") +public class RoleAssignment { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPAbschoepfung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPAbschoepfung.java new file mode 100644 index 00000000..2597b648 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPAbschoepfung.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"SAPAbschoepfung"}, template = "${}") +public class SAPAbschoepfung { + @Adapt(ref = {"SAPAbschoepfung.betragCent"}, template = "${}") + private Long betragCent; + + @Adapt(ref = {"SAPAbschoepfung.year"}, template = "${}") + private ZonedDateTime year; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPVerbindung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPVerbindung.java new file mode 100644 index 00000000..9c5c9fd0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPVerbindung.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"SAPVerbindung"}, template = "${}") +public class SAPVerbindung { + @Adapt(ref = {"SAPVerbindung.status"}, template = "${}") + private SAPverbindungsStatus status; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPimportMode.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPimportMode.java new file mode 100644 index 00000000..fdb0eeda --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPimportMode.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum SAPimportMode { + DEFAULT, + F1KONTO; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPverbindungsStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPverbindungsStatus.java new file mode 100644 index 00000000..2f387d95 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/SAPverbindungsStatus.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum SAPverbindungsStatus { + VERBUNDEN, + GETRENNT, + PROBLEM; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Setting.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Setting.java new file mode 100644 index 00000000..0fd5fdbe --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Setting.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Setting"}, template = "${}") +public class Setting { + @Adapt(ref = {"Setting.identifier"}, template = "${}") + private String identifier; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Sonderurlaub.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Sonderurlaub.java new file mode 100644 index 00000000..76a92995 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Sonderurlaub.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Sonderurlaub"}, template = "${}") +public class Sonderurlaub { + @Adapt(ref = {"Sonderurlaub.datumVon"}, template = "${}") + private ZonedDateTime datumVon; + + @Adapt(ref = {"Sonderurlaub.datumBis"}, template = "${}") + private ZonedDateTime datumBis; + + @Adapt(ref = {"Sonderurlaub.anzahl"}, template = "${}") + private long anzahl; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Sonstiges.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Sonstiges.java new file mode 100644 index 00000000..ae6c9765 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Sonstiges.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Sonstiges"}, template = "${}") +public class Sonstiges extends ErweitertesKonto { + @Adapt(ref = {"Sonstiges.auftraggeber"}, template = "${}") + private Optional auftraggeber; + + @Adapt(ref = {"Sonstiges.geschaeftsvorgang"}, template = "${}") + private Geschaeftsvorgang geschaeftsvorgang; + + @Adapt(ref = {"Sonstiges.mehrwertsteuer"}, template = "${}") + private Optional mehrwertsteuer; + + @Adapt(ref = {"Sonstiges.istSammelkonto"}, template = "${}") + private boolean istSammelkonto; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stellentyp.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stellentyp.java new file mode 100644 index 00000000..f4e04bcf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stellentyp.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Stellentyp"}, template = "${}") +public class Stellentyp { + @Adapt(ref = {"Stellentyp.entgeltgruppe"}, template = "${}") + private Optional entgeltgruppe; + + @Adapt(ref = {"Stellentyp.entgeltstufe"}, template = "${}") + private Optional entgeltstufe; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stellenzuweisung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stellenzuweisung.java new file mode 100644 index 00000000..d5aa688d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stellenzuweisung.java @@ -0,0 +1,34 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Stellenzuweisung"}, template = "${}") +public class Stellenzuweisung { + @Adapt(ref = {"Stellenzuweisung.erstellDatum"}, template = "${}") + private ZonedDateTime erstellDatum; + + @Adapt(ref = {"Stellenzuweisung.startDatum"}, template = "${}") + private Optional startDatum; + + @Adapt(ref = {"Stellenzuweisung.endDatum"}, template = "${}") + private Optional endDatum; + + @Adapt(ref = {"Stellenzuweisung.wert"}, template = "${}") + private long wert; + + @Adapt(ref = {"Stellenzuweisung.kennung"}, template = "${}") + private String kennung; + + @Adapt(ref = {"Stellenzuweisung.status"}, template = "${}") + private StellenzuweisungStatus status; + + @Adapt(ref = {"Stellenzuweisung.stellenumfang"}, template = "${}") + private ZahlenWert stellenumfang; + + @Adapt(ref = {"Stellenzuweisung.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"Stellenzuweisung.aenderungsdatum"}, template = "${}") + private Optional aenderungsdatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StellenzuweisungStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StellenzuweisungStatus.java new file mode 100644 index 00000000..c0bba7a8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StellenzuweisungStatus.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum StellenzuweisungStatus { + BESETZT, + TEILBESETZT, + UNBESETZT, + PLANUNG; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stornierungsstatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stornierungsstatus.java new file mode 100644 index 00000000..c25934b0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stornierungsstatus.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Stornierungsstatus { + NONE, + VERBOTEN, + STORNIERUNGSANTRAG, + STORNIERT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerGeschaeftsjahr.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerGeschaeftsjahr.java new file mode 100644 index 00000000..5f7ca617 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerGeschaeftsjahr.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"StrafsteuerGeschaeftsjahr"}, template = "${}") +public class StrafsteuerGeschaeftsjahr { + @Adapt(ref = {"StrafsteuerGeschaeftsjahr.jahr"}, template = "${}") + private ZonedDateTime jahr; + + @Adapt(ref = {"StrafsteuerGeschaeftsjahr.current"}, template = "${}") + private boolean current; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerSperrDatum.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerSperrDatum.java new file mode 100644 index 00000000..cdd21595 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerSperrDatum.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"StrafsteuerSperrDatum"}, template = "${}") +public class StrafsteuerSperrDatum { + @Adapt(ref = {"StrafsteuerSperrDatum.sperrDatum"}, template = "${}") + private ZonedDateTime sperrDatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerStatus.java new file mode 100644 index 00000000..ad11ddba --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StrafsteuerStatus.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum StrafsteuerStatus { + NONE, + SOME, + ALL; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stundenzettel.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stundenzettel.java new file mode 100644 index 00000000..5cb7fbec --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Stundenzettel.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Stundenzettel"}, template = "${}") +public class Stundenzettel { + @Adapt(ref = {"Stundenzettel.status"}, template = "${}") + private StundenzettelStatus status; + + @Adapt(ref = {"Stundenzettel.zeit"}, template = "${}") + private ZonedDateTime zeit; + + @Adapt(ref = {"Stundenzettel.abgegebenVonUserId"}, template = "${}") + private Optional abgegebenVonUserId; + + @Adapt(ref = {"Stundenzettel.abgabeDatum"}, template = "${}") + private Optional abgabeDatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelChangeOperation.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelChangeOperation.java new file mode 100644 index 00000000..7cba635b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelChangeOperation.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum StundenzettelChangeOperation { + INVALID, + ABGEGEBEN, + INTERN_GEPRUEFT, + INTERN_ABGESCHLOSSEN; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelEintrag.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelEintrag.java new file mode 100644 index 00000000..8d05c8de --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelEintrag.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"StundenzettelEintrag"}, template = "${}") +public class StundenzettelEintrag { + @Adapt(ref = {"StundenzettelEintrag.uhrzeitVon"}, template = "${}") + private ZonedDateTime uhrzeitVon; + + @Adapt(ref = {"StundenzettelEintrag.uhrzeitBis"}, template = "${}") + private Optional uhrzeitBis; + + @Adapt(ref = {"StundenzettelEintrag.beschreibung"}, template = "${}") + private Optional beschreibung; + + @Adapt(ref = {"StundenzettelEintrag.stunden"}, template = "${}") + private long stunden; + + @Adapt(ref = {"StundenzettelEintrag.pauseOderSonstiges"}, template = "${}") + private StundenzettelProjekt pauseOderSonstiges; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelHistorieEintrag.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelHistorieEintrag.java new file mode 100644 index 00000000..6a51455f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelHistorieEintrag.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"StundenzettelHistorieEintrag"}, template = "${}") +public class StundenzettelHistorieEintrag { + @Adapt(ref = {"StundenzettelHistorieEintrag.bearbeitungsdatum"}, template = "${}") + private ZonedDateTime bearbeitungsdatum; + + @Adapt(ref = {"StundenzettelHistorieEintrag.editorId"}, template = "${}") + private Optional editorId; + + @Adapt(ref = {"StundenzettelHistorieEintrag.editedBy"}, template = "${}") + private Optional editedBy; + + @Adapt(ref = {"StundenzettelHistorieEintrag.monat"}, template = "${}") + private String monat; + + @Adapt(ref = {"StundenzettelHistorieEintrag.jahr"}, template = "${}") + private int jahr; + + @Adapt(ref = {"StundenzettelHistorieEintrag.changeOperation"}, template = "${}") + private String changeOperation; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelProjekt.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelProjekt.java new file mode 100644 index 00000000..a436e6fc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelProjekt.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum StundenzettelProjekt { + PAUSE, + SONSTIGE, + NONE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelStatus.java new file mode 100644 index 00000000..bae441b4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/StundenzettelStatus.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum StundenzettelStatus { + IN_ERFASSUNG, + INTERN_INPRUEFUNG, + INTERN_ABGESCHLOSSEN, + DRITTMITTELABTEILUNG_INPRUEFUNG, + DRITTMITTELABTEILUNG_ABGESCHLOSSEN, + ENDBERICHT_EINGEREICHT, + GEPRUEFT, + FOERDERGEBER_ABGESCHLOSSEN, + ABGESCHLOSSEN; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableIdentifierTyp.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableIdentifierTyp.java new file mode 100644 index 00000000..a52b2a2a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableIdentifierTyp.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum TableIdentifierTyp { + DEFAULT, + USER, + INSTITUT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableSetting.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableSetting.java new file mode 100644 index 00000000..00c87b77 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableSetting.java @@ -0,0 +1,28 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"TableSetting"}, template = "${}") +public class TableSetting extends UISetting { + @Adapt(ref = {"TableSetting.typ"}, template = "${}") + private TableIdentifierTyp typ; + + @Adapt(ref = {"TableSetting.zeigeInaktive"}, template = "${}") + private boolean zeigeInaktive; + + @Adapt(ref = {"TableSetting.zeilenLimit"}, template = "${}") + private int zeilenLimit; + + @Adapt(ref = {"TableSetting.gruppiereNach"}, template = "${}") + private Optional gruppiereNach; + + @Adapt(ref = {"TableSetting.sortiereNach"}, template = "${}") + private Optional sortiereNach; + + @Adapt(ref = {"TableSetting.sortiereDir"}, template = "${}") + private Optional sortiereDir; + + @Adapt(ref = {"TableSetting.isShowColor"}, template = "${}") + private boolean isShowColor; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableSettingSpalten.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableSettingSpalten.java new file mode 100644 index 00000000..bad9f6ec --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/TableSettingSpalten.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"TableSettingSpalten"}, template = "${}") +public class TableSettingSpalten { + @Adapt(ref = {"TableSettingSpalten.name"}, template = "${}") + private String name; + + @Adapt(ref = {"TableSettingSpalten.istAktiv"}, template = "${}") + private boolean istAktiv; + + @Adapt(ref = {"TableSettingSpalten.breite"}, template = "${}") + private long breite; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Titel.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Titel.java new file mode 100644 index 00000000..eeeff1e5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Titel.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Titel { + NONE, + E1, + E2, + E3, + E4, + E5, + E6, + E7, + E8, + E9, + E10, + E11, + E12, + E13, + E14, + E15, + E16, + E17, + E18, + E19, + E20; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UISetting.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UISetting.java new file mode 100644 index 00000000..4b7c2458 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UISetting.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"UISetting"}, template = "${}") +public class UISetting extends Setting { + @Adapt(ref = {"UISetting.seite"}, template = "${}") + private String seite; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UrlaubEinstellungen.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UrlaubEinstellungen.java new file mode 100644 index 00000000..30aa1cc6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UrlaubEinstellungen.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"UrlaubEinstellungen"}, template = "${}") +public class UrlaubEinstellungen { + @Adapt(ref = {"UrlaubEinstellungen.urlaubsanspruchInStunden"}, template = "${}") + private boolean urlaubsanspruchInStunden; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UrlaubsGenehmigung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UrlaubsGenehmigung.java new file mode 100644 index 00000000..69799154 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UrlaubsGenehmigung.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"UrlaubsGenehmigung"}, template = "${}") +public class UrlaubsGenehmigung { + @Adapt(ref = {"UrlaubsGenehmigung.beantragt"}, template = "${}") + private Optional beantragt; + + @Adapt(ref = {"UrlaubsGenehmigung.genehmigt"}, template = "${}") + private Optional genehmigt; + + @Adapt(ref = {"UrlaubsGenehmigung.geprueft"}, template = "${}") + private Optional geprueft; + + @Adapt(ref = {"UrlaubsGenehmigung.storniert"}, template = "${}") + private Optional storniert; + + @Adapt(ref = {"UrlaubsGenehmigung.status"}, template = "${}") + private Urlaubssstatus status; + + @Adapt(ref = {"UrlaubsGenehmigung.stornierungsStatus"}, template = "${}") + private Stornierungsstatus stornierungsStatus; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Urlaubssstatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Urlaubssstatus.java new file mode 100644 index 00000000..5699adc1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Urlaubssstatus.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Urlaubssstatus { + NONE, + BEANTRAGT, + GEPRUEFT, + PRUEFUNG_ABGELEHNT, + GENEHMIGT, + GENEHMIGUNG_ABGELEHNT; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UserNotification.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UserNotification.java new file mode 100644 index 00000000..c41c7091 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UserNotification.java @@ -0,0 +1,28 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"UserNotification"}, template = "${}") +public class UserNotification { + @Adapt(ref = {"UserNotification.notificationType"}, template = "${}") + private UserNotificationType notificationType; + + @Adapt(ref = {"UserNotification.title"}, template = "${}") + private String title; + + @Adapt(ref = {"UserNotification.message"}, template = "${}") + private String message; + + @Adapt(ref = {"UserNotification.timeStamp"}, template = "${}") + private ZonedDateTime timeStamp; + + @Adapt(ref = {"UserNotification.link"}, template = "${}") + private Optional link; + + @Adapt(ref = {"UserNotification.seen"}, template = "${}") + private boolean seen; + + @Adapt(ref = {"UserNotification.pinned"}, template = "${}") + private boolean pinned; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UserNotificationType.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UserNotificationType.java new file mode 100644 index 00000000..5a07fcd9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/UserNotificationType.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum UserNotificationType { + SUCCESS, + INFO, + WARNING, + DANGER; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vergabeverordnung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vergabeverordnung.java new file mode 100644 index 00000000..ff8c2f32 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vergabeverordnung.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Vergabeverordnung { + VOL_A, + VOB_A, + UVGO_NRW, + UVGO_BUND, + NONE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vertrag.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vertrag.java new file mode 100644 index 00000000..acc4bc8b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vertrag.java @@ -0,0 +1,40 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Vertrag"}, template = "${}") +public class Vertrag { + @Adapt(ref = {"Vertrag.vertragsBeginn"}, template = "${}") + private ZonedDateTime vertragsBeginn; + + @Adapt(ref = {"Vertrag.vertragsEnde"}, template = "${}") + private ZonedDateTime vertragsEnde; + + @Adapt(ref = {"Vertrag.aenderungsvertrag"}, template = "${}") + private boolean aenderungsvertrag; + + @Adapt(ref = {"Vertrag.vertragsStatus"}, template = "${}") + private String vertragsStatus; + + @Adapt(ref = {"Vertrag.kommentar"}, template = "${}") + private List kommentar; + + @Adapt(ref = {"Vertrag.planUmfang"}, template = "${}") + private ZahlenWert planUmfang; + + @Adapt(ref = {"Vertrag.arbeitsstundenProWoche"}, template = "${}") + private Optional arbeitsstundenProWoche; + + @Adapt(ref = {"Vertrag.kuendigungsschutz"}, template = "${}") + private boolean kuendigungsschutz; + + @Adapt(ref = {"Vertrag.vertragsgrundlage"}, template = "${}") + private Optional vertragsgrundlage; + + @Adapt(ref = {"Vertrag.aktion"}, template = "${}") + private Optional aktion; + + @Adapt(ref = {"Vertrag.aktionsDatum"}, template = "${}") + private Optional aktionsDatum; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/VertragStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/VertragStatus.java new file mode 100644 index 00000000..da4d2127 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/VertragStatus.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum VertragStatus { + AKTIV, + PLANUNG, + ABGELAUFEN; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vertragsgrundlage.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vertragsgrundlage.java new file mode 100644 index 00000000..48c1557e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Vertragsgrundlage.java @@ -0,0 +1,29 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Vertragsgrundlage { + NONE, + E1, + E2, + E3, + E4, + E5, + E6, + E7, + E8, + E9, + E10, + E11, + E12, + E13, + E14, + E15, + E16, + E17, + E18, + E19, + E20, + E21, + E22, + E23, + E24; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZahlenTyp.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZahlenTyp.java new file mode 100644 index 00000000..64f76eb8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZahlenTyp.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum ZahlenTyp { + EURO, + STUNDE, + PROZENT, + NONE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZahlenWert.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZahlenWert.java new file mode 100644 index 00000000..8bb3fd27 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZahlenWert.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ZahlenWert"}, template = "${}") +public class ZahlenWert { + @Adapt(ref = {"ZahlenWert.zahlenTyp"}, template = "${}") + private ZahlenTyp zahlenTyp; + + @Adapt(ref = {"ZahlenWert.wert"}, template = "${}") + private long wert; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZeitlicheVerbuchungskontoZuordnung.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZeitlicheVerbuchungskontoZuordnung.java new file mode 100644 index 00000000..69de00dc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/ZeitlicheVerbuchungskontoZuordnung.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ZeitlicheVerbuchungskontoZuordnung"}, template = "${}") +public class ZeitlicheVerbuchungskontoZuordnung { + @Adapt(ref = {"ZeitlicheVerbuchungskontoZuordnung.programmPauschale"}, template = "${}") + private Optional programmPauschale; + + @Adapt(ref = {"ZeitlicheVerbuchungskontoZuordnung.gemeinkostensatz"}, template = "${}") + private Optional gemeinkostensatz; + + @Adapt(ref = {"ZeitlicheVerbuchungskontoZuordnung.startDate"}, template = "${}") + private Optional startDate; + + @Adapt(ref = {"ZeitlicheVerbuchungskontoZuordnung.endDate"}, template = "${}") + private Optional endDate; + + @Adapt(ref = {"ZeitlicheVerbuchungskontoZuordnung.istHauptVerbuchungskonto"}, template = "${}") + private boolean istHauptVerbuchungskonto; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Zulage.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Zulage.java new file mode 100644 index 00000000..62ac766f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Zulage.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Zulage"}, template = "${}") +public class Zulage { + @Adapt(ref = {"Zulage.pspelement"}, template = "${}") + private Optional pspelement; + + @Adapt(ref = {"Zulage.bezeichnung"}, template = "${}") + private Optional bezeichnung; + + @Adapt(ref = {"Zulage.von"}, template = "${}") + private ZonedDateTime von; + + @Adapt(ref = {"Zulage.bis"}, template = "${}") + private ZonedDateTime bis; + + @Adapt(ref = {"Zulage.arbeitgeberbrutto"}, template = "${}") + private Long arbeitgeberbrutto; + + @Adapt(ref = {"Zulage.arbeitnehmerbrutto"}, template = "${}") + private Long arbeitnehmerbrutto; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Zuweisungsart.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Zuweisungsart.java new file mode 100644 index 00000000..75216128 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/adapter/Empty/Zuweisungsart.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco.adapter.empty; + +public enum Zuweisungsart { + KONTO, + STELLENZUWEISUNG, + PLANSTELLE, + SONSTIGES; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/concrete/Placeholder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/concrete/Placeholder.java new file mode 100644 index 00000000..80bf0d71 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/macoco/concrete/Placeholder.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.macoco; + +public class Placeholder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/LanguageInfrastructureConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/LanguageInfrastructureConc.cd new file mode 100644 index 00000000..10e46f1f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/LanguageInfrastructureConc.cd @@ -0,0 +1,8 @@ +classdiagram LanguageInfrastructureConc { + + <> class G1Parser; + <> class G1Traverser; + <> class ASTFooBuilder; + + <> class G1Mill; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/MillOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/MillOut.cd new file mode 100644 index 00000000..9299c9d5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/MillOut.cd @@ -0,0 +1,17 @@ +classdiagram LanguageInfrastructureConc { + + <> class G1Parser; + <> class G1Traverser; + <> class ASTFooBuilder; + + <> class G1Mill { + static void init(); + static void initMe(G1Mill me); + <> G1Parser _G1Parser(); + <> G1Traverser _G1Traverser(); + <> ASTFooBuilder _ASTFooBuilder(); + <> static G1Parser g1Parser(); + <> static G1Traverser g1Traverser(); + <> static ASTFooBuilder aSTFooBuilder(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/MillRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/MillRef.cd new file mode 100644 index 00000000..27c4eca4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/MillRef.cd @@ -0,0 +1,11 @@ +classdiagram MillRef { + + class MillProduct; + + class Mill { + static void init(); + static void initMe(Mill me); + <> MillProduct _MillProduct(); + <> static MillProduct millProduct(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/adapter/LanguageInfrastructure/Mill.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/adapter/LanguageInfrastructure/Mill.java new file mode 100644 index 00000000..778f8469 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/adapter/LanguageInfrastructure/Mill.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.mill.adapter.languageinfrastructure; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Mill"}, template = "${}") +public class Mill { + @Adapt(ref = {"Mill.init"}, template = "${}") + public void init() { + } + + @Adapt(ref = {"Mill.initMe"}, template = "${}") + public void initMe(Mill me) { + } + + @Adapt(ref = {"Mill._MillProduct"}, template = "${}") + public MillProduct _MillProduct() { + return null; + } + + @Adapt(ref = {"Mill.millProduct"}, template = "${}") + public MillProduct millProduct() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/adapter/LanguageInfrastructure/MillProduct.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/adapter/LanguageInfrastructure/MillProduct.java new file mode 100644 index 00000000..049b626c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/adapter/LanguageInfrastructure/MillProduct.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.mill.adapter.languageinfrastructure; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"MillProduct"}, template = "${}") +public class MillProduct { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/ASTFooBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/ASTFooBuilder.java new file mode 100644 index 00000000..c2e219e9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/ASTFooBuilder.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.mill; + +public class ASTFooBuilder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Mill.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Mill.java new file mode 100644 index 00000000..5028e657 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Mill.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.mill; + +public class G1Mill { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Parser.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Parser.java new file mode 100644 index 00000000..2c83ddf4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Parser.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.mill; + +public class G1Parser { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Traverser.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Traverser.java new file mode 100644 index 00000000..a4f36cb9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/mill/concrete/G1Traverser.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.mill; + +public class G1Traverser { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/ObserverRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/ObserverRef.cd new file mode 100644 index 00000000..75a32843 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/ObserverRef.cd @@ -0,0 +1,14 @@ +classdiagram ObserverRef { + + class Observer { + void update(); + } + + class Subject { + void register(Observer o); + void unregister(Observer o); + void notifyObservers(); + } + + association Subject -> (observers) Observer [*] <>; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/adapter/Observer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/adapter/Observer.java new file mode 100644 index 00000000..e79fe3bd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/adapter/Observer.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.observer; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Observer"}, template = "${}") +public class Observer { + @Adapt(ref = {"Observer.update"}, template = "${}") + public void update() { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/adapter/Subject.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/adapter/Subject.java new file mode 100644 index 00000000..9c68f917 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/adapter/Subject.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.observer; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Subject"}, template = "${}") +public class Subject { + @Adapt(ref = {"Subject.register"}, template = "${}") + public void register(Observer o) { + } + + @Adapt(ref = {"Subject.unregister"}, template = "${}") + public void unregister(Observer o) { + } + + @Adapt(ref = {"Subject.notifyObservers"}, template = "${}") + public void notifyObservers() { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/MutualObserversConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/MutualObserversConc.cd new file mode 100644 index 00000000..0d26ddd4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/MutualObserversConc.cd @@ -0,0 +1,5 @@ +classdiagram MutualObserversConc { + <> class Attacker; + <> class Defender; + association watches [*] Attacker <-> Defender [*]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/MutualObserversOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/MutualObserversOut.cd new file mode 100644 index 00000000..552c8571 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/MutualObserversOut.cd @@ -0,0 +1,18 @@ +classdiagram MutualObserversConc { + + <> class Attacker { + void update(); + void register(Defender o); + void unregister(Defender o); + void notifyObservers(); + + } + <> class Defender { + void update(); + void register(Attacker o); + void unregister(Attacker o); + void notifyObservers(); + } + association watches [*] Attacker (attackers) <-> (defenders) Defender[*]; + +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/concrete/MutualObservers/Attacker.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/concrete/MutualObservers/Attacker.java new file mode 100644 index 00000000..8d0f746c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/concrete/MutualObservers/Attacker.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.observer.mutualobservers.concrete.mutualobservers; + +public class Attacker { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/concrete/MutualObservers/Defender.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/concrete/MutualObservers/Defender.java new file mode 100644 index 00000000..c13b47d5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/observer/mutualObservers/concrete/MutualObservers/Defender.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.observer.mutualobservers.concrete.mutualobservers; + +public class Defender { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/EcommerceDomain.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/EcommerceDomain.cd new file mode 100644 index 00000000..cd57e5eb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/EcommerceDomain.cd @@ -0,0 +1,19 @@ +import java.lang.String; +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram EcommerceDomain { + + <> class Product { + <> String sku; + double price; + } + + <> class Order { + <> long id; + } + + <> class Customer { + <> long id; + <> String email; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/RepositoryOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/RepositoryOut.cd new file mode 100644 index 00000000..e1db2189 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/RepositoryOut.cd @@ -0,0 +1,42 @@ +import java.lang.String; +import java.util.List; +import java.util.Optional; + +classdiagram EcommerceDomain { + <> class Product { + <> String sku; + double price; + } + + <> class Order { + <> long id; + } + + <> class Customer { + <> long id; + <> String email; + } + + <>class ProductRepository { + <> Optional findBySku(String sku); + List findAll(); + Product save(Product product); + <> void removeBySku(String sku); + } + + <>class OrderRepository { + <> Optional findById(long id); + List findAll(); + Order save(Order order); + <> void removeById(long id); + } + + <>class CustomerRepository { + <> Optional findById(long id); + <> Optional findByEmail(String email); + List findAll(); + Customer save(Customer customer); + <> void removeById(long id); + <> void removeByEmail(String email); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/RepositoryRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/RepositoryRef.cd new file mode 100644 index 00000000..b21e2ee2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/repository/RepositoryRef.cd @@ -0,0 +1,16 @@ +import java.util.List; +import java.util.Optional; + +classdiagram RepositoryRef { + + class Entity { + any key; + } + + <> class EntityRepository { + <> Optional findByKey(any key); + List findAll(); + Entity save(Entity entity); + <> void removeByKey(any key); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TaskManagementRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TaskManagementRef.cd new file mode 100644 index 00000000..1acdc1e9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TaskManagementRef.cd @@ -0,0 +1,28 @@ +import java.lang.String; +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram TaskManagementRef { + + class Task { + String title; + String description; + TaskStatus status; + } + + class Member { + String name; + String email; + } + + class Project { + String name; + } + + enum TaskStatus { + Open, InProgress, Done; + } + + association Member -> (assignedTasks) Task [*]; + association Project -> (members) Member [*]; + association Project -> Task [*]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TicketSystemConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TicketSystemConc.cd new file mode 100644 index 00000000..d4ce2ed2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TicketSystemConc.cd @@ -0,0 +1,15 @@ +import java.lang.String; +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram TicketSystemConc { + + <> class Ticket { + <> String summary; + <> String detail; + } + + <> class Workspace { + <> String label; + boolean archived; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TicketSystemOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TicketSystemOut.cd new file mode 100644 index 00000000..1589820a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/TicketSystemOut.cd @@ -0,0 +1,29 @@ +import java.lang.String; +/* (c) https://github.com/MontiCore/monticore */ + +classdiagram TicketSystemConc { + + <> class Ticket { + <> String summary; + <> String detail; + TaskStatus status; + } + + <> class Workspace { + <> String label; + boolean archived; + } + + class Member { + String name; + String email; + } + + enum TaskStatus { + Open, InProgress, Done; + } + + association Member -> (assignedTickets) Ticket [*]; + association Workspace -> (members) Member [*]; + association Workspace -> Ticket [*]; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Member.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Member.java new file mode 100644 index 00000000..db33b304 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Member.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.paper_examples.task_management.adapter.ticketsystem; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Member"}, template = "${}") +public class Member { + @Adapt(ref = {"Member.name"}, template = "${}") + private String name; + + @Adapt(ref = {"Member.email"}, template = "${}") + private String email; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Project.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Project.java new file mode 100644 index 00000000..1a546671 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Project.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.paper_examples.task_management.adapter.ticketsystem; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Project"}, template = "${}") +public class Project { + @Adapt(ref = {"Project.name"}, template = "${}") + private String name; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Task.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Task.java new file mode 100644 index 00000000..d850a279 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/Task.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.paper_examples.task_management.adapter.ticketsystem; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Task"}, template = "${}") +public class Task { + @Adapt(ref = {"Task.title"}, template = "${}") + private String title; + + @Adapt(ref = {"Task.description"}, template = "${}") + private String description; + + @Adapt(ref = {"Task.status"}, template = "${}") + private TaskStatus status; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/TaskStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/TaskStatus.java new file mode 100644 index 00000000..069fa28a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/adapter/TicketSystem/TaskStatus.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.paper_examples.task_management.adapter.ticketsystem; + +public enum TaskStatus { + Open, + InProgress, + Done; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/concrete/Ticket.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/concrete/Ticket.java new file mode 100644 index 00000000..013d6232 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/concrete/Ticket.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.paper_examples.task_management; + +public class Ticket { + private String summary; + + private String detail; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/concrete/Workspace.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/concrete/Workspace.java new file mode 100644 index 00000000..a73696df --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/paper-examples/task-management/concrete/Workspace.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.paper_examples.task_management; + +public class Workspace { + private String label; + + private boolean archived; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/DomainModel.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/DomainModel.cd new file mode 100644 index 00000000..e1bde702 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/DomainModel.cd @@ -0,0 +1,17 @@ +import java.lang.String; + + classdiagram DomainModel { + <> class Employee { + <> String id; + <> String taxId; + String firstName; + String lastName; + int number; + int salary; + } + + <> class Department { + String managerName; + <> int depId; + } + } diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/RepositoryOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/RepositoryOut.cd new file mode 100644 index 00000000..c0aa6ce4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/RepositoryOut.cd @@ -0,0 +1,36 @@ +import java.lang.String; +import java.util.List; +import java.util.Optional; + +classdiagram DomainModel { + <> class Employee { + <> String id; + <> String taxId; + String firstName; + String lastName; + int number; + int salary; + } + + <> class Department { + String managerName; + <> int depId; + } + + <>class DepartmentRepository { + <> Optional findByDepId(int depId); + List findAll(); + Department save(Department department); + <> void removeByDepId(int depId); + + } + + <>class EmployeeRepository { + <> Optional findById(String id); + <> Optional findByTaxId(String taxId); + List findAll(); + Employee save(Employee employee); + <> void removeById(String id); + <> void removeByTaxId(String taxId); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/RepositoryRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/RepositoryRef.cd new file mode 100644 index 00000000..4d2bb87c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/repository/RepositoryRef.cd @@ -0,0 +1,16 @@ +import java.util.List; +import java.util.Optional; + +classdiagram RepositoryRef { + + class Entity { + any id; + } + + <> class EntityRepository { + <> Optional findById(any id); + List findAll(); + Entity save(Entity entity); + <> void removeById(any id); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorConc.cd new file mode 100644 index 00000000..7c4c7428 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorConc.cd @@ -0,0 +1,8 @@ +classdiagram StaticDelegatorConc { + + <> class MyLanguageMill { + <> static void foo(); + <> static int getId(); + <> static void doSomething(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorOut.cd new file mode 100644 index 00000000..74b6676c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorOut.cd @@ -0,0 +1,12 @@ +classdiagram StaticDelegatorConc { + + <> class MyLanguageMill { + <> static void foo(); + <> static int getId(); + <> static void doSomething(); + + <> void _foo(); + <> int _getId(); + <> void _doSomething(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorRef.cd new file mode 100644 index 00000000..f53adaf4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/StaticDelegatorRef.cd @@ -0,0 +1,7 @@ +classdiagram StaticDelegatorRef { + + class StaticDelegator { + <> any _method(); + static any method(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/adapter/StaticDelegator/StaticDelegator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/adapter/StaticDelegator/StaticDelegator.java new file mode 100644 index 00000000..9cd1cbeb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/adapter/StaticDelegator/StaticDelegator.java @@ -0,0 +1,17 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.staticdelegator.adapter.staticdelegator; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"StaticDelegator"}, template = "${}") +public class StaticDelegator { + @Adapt(ref = {"StaticDelegator._method"}, template = "${}") + public Object _method() { + return null; + } + + @Adapt(ref = {"StaticDelegator.method"}, template = "${}") + public Object method() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsConc.cd new file mode 100644 index 00000000..a914554f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsConc.cd @@ -0,0 +1,8 @@ +classdiagram InstanceMethodExistsConc { + + <> class MyLanguageMill { + <> int _foo; + <> int _getId; + <> boolean _doSomething; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsOut.cd new file mode 100644 index 00000000..cfdb9ab9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/InstanceMethodExistsOut.cd @@ -0,0 +1,12 @@ +classdiagram InstanceMethodExistsConc { + + <> class MyLanguageMill { + <> int _foo; + <> int _getId; + <> boolean _doSomething; + + <> static int foo; + <> static int getId; + <> static boolean doSomething; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticDelegatorRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticDelegatorRef.cd new file mode 100644 index 00000000..9b9d2d65 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticDelegatorRef.cd @@ -0,0 +1,12 @@ +classdiagram StaticDelegatorRef { + + /* + * Because there is not yet a "method forEach method" completion implementation, we test the + * same pattern with attributes. + */ + + class StaticDelegator { + <> any _method; + static any method; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticExistsConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticExistsConc.cd new file mode 100644 index 00000000..f09aab16 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticExistsConc.cd @@ -0,0 +1,8 @@ +classdiagram StaticExistsConc { + + <> class MyLanguageMill { + <> static int foo; + <> static int getId; + <> static boolean doSomething; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticExistsOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticExistsOut.cd new file mode 100644 index 00000000..46491610 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/StaticExistsOut.cd @@ -0,0 +1,12 @@ +classdiagram StaticExistsConc { + + <> class MyLanguageMill { + <> static int foo; + <> static int getId; + <> static boolean doSomething; + + <> int _foo; + <> int _getId; + <> boolean _doSomething; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/adapter/InstanceMethodExists/StaticDelegator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/adapter/InstanceMethodExists/StaticDelegator.java new file mode 100644 index 00000000..10c9da19 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/adapter/InstanceMethodExists/StaticDelegator.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.staticdelegator.attrworkaround.adapter.instancemethodexists; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"StaticDelegator"}, template = "${}") +public class StaticDelegator { + @Adapt(ref = {"StaticDelegator._method"}, template = "${}") + private Object _method; + + @Adapt(ref = {"StaticDelegator.method"}, template = "${}") + private Object method; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/adapter/StaticExists/StaticDelegator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/adapter/StaticExists/StaticDelegator.java new file mode 100644 index 00000000..80e73a42 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/adapter/StaticExists/StaticDelegator.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.staticdelegator.attrworkaround.adapter.staticexists; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"StaticDelegator"}, template = "${}") +public class StaticDelegator { + @Adapt(ref = {"StaticDelegator._method"}, template = "${}") + private Object _method; + + @Adapt(ref = {"StaticDelegator.method"}, template = "${}") + private Object method; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/concrete/MyLanguageMill.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/concrete/MyLanguageMill.java new file mode 100644 index 00000000..e6810e2a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/attrWorkaround/concrete/MyLanguageMill.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.staticdelegator.attrworkaround; + +public class MyLanguageMill { + private int foo; + + private int getId; + + private boolean doSomething; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/concrete/MyLanguageMill.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/concrete/MyLanguageMill.java new file mode 100644 index 00000000..a0e7edcd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/staticDelegator/concrete/MyLanguageMill.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.staticdelegator; + +public class MyLanguageMill { + public void foo() { + } + + public int getId() { + return 0; + } + + public void doSomething() { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesConc.cd new file mode 100644 index 00000000..fb4256b0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesConc.cd @@ -0,0 +1,5 @@ +classdiagram TransitiveDependenciesConc { + + <> class Employee; + <> class Department; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesOut.cd new file mode 100644 index 00000000..15e1b714 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesOut.cd @@ -0,0 +1,11 @@ +classdiagram TransitiveDependenciesConc { + + <> class Employee; + <> class Department; + + <> class EmployeeBuilder; + <> class DepartmentBuilder; + + <> class EmployeeBuilderFactory; + <> class DepartmentBuilderFactory; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesRef.cd new file mode 100644 index 00000000..dfdf85e6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesRef.cd @@ -0,0 +1,8 @@ +classdiagram TransitiveDependenciesRef { + + class DataClass; + + <> class DataClassBuilder; + + <> class DataClassBuilderFactory; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesSwappedOrderRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesSwappedOrderRef.cd new file mode 100644 index 00000000..94091796 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/TransitiveDependenciesSwappedOrderRef.cd @@ -0,0 +1,10 @@ +classdiagram TransitiveDependenciesRef { + + <> class DataClassBuilderFactory; + + class DataClass; + + <> class DataClassBuilder; + + +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/DataClass.java new file mode 100644 index 00000000..1d4e22c2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/DataClass.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/DataClassBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/DataClassBuilder.java new file mode 100644 index 00000000..765c3fab --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/DataClassBuilder.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClassBuilder"}, template = "${}") +public class DataClassBuilder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/TransitiveDependencies/DataClassBuilderFactory.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/TransitiveDependencies/DataClassBuilderFactory.java new file mode 100644 index 00000000..fd814d0c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/adapter/TransitiveDependencies/DataClassBuilderFactory.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies.adapter.transitivedependencies; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClassBuilderFactory"}, template = "${}") +public class DataClassBuilderFactory { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/concrete/Department.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/concrete/Department.java new file mode 100644 index 00000000..c3f8f8aa --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/concrete/Department.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies; + +public class Department { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/concrete/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/concrete/Employee.java new file mode 100644 index 00000000..00de4a9c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/transitiveDependencies/concrete/Employee.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.transitivedependencies; + +public class Employee { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorConc.cd new file mode 100644 index 00000000..e3f35685 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorConc.cd @@ -0,0 +1,9 @@ +classdiagram VisitorConc { + + <> interface Node; + + // TODO remove incarnation mappings once <> is implemented + <> class RootNode implements Node; + <> class InnerNode implements Node; + <> class LeafNode implements Node; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorOut.cd new file mode 100644 index 00000000..e9a246d1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorOut.cd @@ -0,0 +1,17 @@ +classdiagram VisitorConc { + + <> interface Node { + void accept(NodeVisitor visitor); + } + + <> class RootNode implements Node; + <> class InnerNode implements Node; + <> class LeafNode implements Node; + + <> interface NodeVisitor { + void visit(Node node); + <> void visit(RootNode rootNode); + <> void visit(InnerNode innerNode); + <> void visit(LeafNode leafNode); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorRef.cd new file mode 100644 index 00000000..c22ac725 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/VisitorRef.cd @@ -0,0 +1,14 @@ +classdiagram VisitorRef { + + interface Visitable { + void accept(VisitableVisitor visitor); + } + + // TODO use <> here once it is implemented + class ConcreteVisitable implements Visitable; + + <> interface VisitableVisitor { + void visit(Visitable visitable); + <> void visit(ConcreteVisitable concreteVisitable); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/ConcreteVisitable.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/ConcreteVisitable.java new file mode 100644 index 00000000..2a1415ac --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/ConcreteVisitable.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.visitor.adapter.visitor; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"ConcreteVisitable"}, template = "${}") +public class ConcreteVisitable { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/Visitable.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/Visitable.java new file mode 100644 index 00000000..ed5ab0e8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/Visitable.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.visitor.adapter.visitor; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Visitable"}, template = "${}") +public interface Visitable { + @Adapt(ref = {"Visitable.accept"}, template = "${}") + void accept(VisitableVisitor visitor); + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/VisitableVisitor.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/VisitableVisitor.java new file mode 100644 index 00000000..c159765b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/adapter/Visitor/VisitableVisitor.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.visitor.adapter.visitor; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"VisitableVisitor"}, template = "${}") +public interface VisitableVisitor { + @Adapt(ref = {"VisitableVisitor.visit(Visitable)"}, template = "${}") + void visit(Visitable visitable); + + @Adapt(ref = {"VisitableVisitor.visit(ConcreteVisitable)"}, template = "${}") + void visit(ConcreteVisitable concreteVisitable); + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/InnerNode.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/InnerNode.java new file mode 100644 index 00000000..887c0390 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/InnerNode.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.visitor; + +public class InnerNode { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/LeafNode.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/LeafNode.java new file mode 100644 index 00000000..e3d3cc18 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/LeafNode.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.visitor; + +public class LeafNode { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/Node.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/Node.java new file mode 100644 index 00000000..37e3b697 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/Node.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.visitor; + +public interface Node { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/RootNode.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/RootNode.java new file mode 100644 index 00000000..aa3425cb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/evaluation/visitor/concrete/RootNode.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.evaluation.visitor; + +public class RootNode { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperConc.cd new file mode 100644 index 00000000..5d6f1a7f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperConc.cd @@ -0,0 +1,12 @@ +classdiagram HelperConc { + + class A { + } + class B { + } + <> class C { + } + <> class D { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperOut.cd new file mode 100644 index 00000000..a72bc4a3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperOut.cd @@ -0,0 +1,10 @@ +classdiagram HelperConc { + class A; + class B; + <> class C; + <> class D; + + association A (roleNameLeft) <-> (roleNameRight_B) B; + association A (roleNameLeft) <-> (roleNameRight_C) C; + association A (roleNameLeft) <-> (roleNameRight_D) D; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperRef.cd new file mode 100644 index 00000000..ee0c99a6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/HelperRef.cd @@ -0,0 +1,7 @@ +classdiagram HelperRef { + class A { + } + class B { + } + association A (roleNameLeft) <-> (roleNameRight) B; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/adapter/A.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/adapter/A.java new file mode 100644 index 00000000..8312f6e0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/adapter/A.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.helper; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"A"}, template = "${}") +public class A { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/adapter/B.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/adapter/B.java new file mode 100644 index 00000000..7731ee20 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/adapter/B.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.helper; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"B"}, template = "${}") +public class B { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/B.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/B.java new file mode 100644 index 00000000..e5fbdd1e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/B.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.helper; + +public class B { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/C.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/C.java new file mode 100644 index 00000000..663dea95 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/C.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.helper; + +public class C { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/D.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/D.java new file mode 100644 index 00000000..ad52eb30 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/helper/concrete/D.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.helper; + +public class D { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceConc.cd new file mode 100644 index 00000000..0027167c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceConc.cd @@ -0,0 +1,5 @@ +classdiagram AttributeInheritanceConc { + class Teacher { + int number; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceOut.cd new file mode 100644 index 00000000..0e71630b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceOut.cd @@ -0,0 +1,7 @@ +classdiagram AttributeInheritanceConc { + class Person { + int number; + } + + class Teacher extends Person; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceRef.cd new file mode 100644 index 00000000..dbe0aa16 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeInheritanceRef.cd @@ -0,0 +1,7 @@ +classdiagram AttributeInheritanceRef { + class Person { + int number; + } + class Teacher extends Person { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeTypeMismatchConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeTypeMismatchConc.cd new file mode 100644 index 00000000..70d8b019 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeTypeMismatchConc.cd @@ -0,0 +1,5 @@ +classdiagram AttributeTypeMismatchConc { + class Teacher { + double number; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeTypeMismatchRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeTypeMismatchRef.cd new file mode 100644 index 00000000..0b063677 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/AttributeTypeMismatchRef.cd @@ -0,0 +1,7 @@ +classdiagram AttributeTypeMismatchRef { + class Person { + int number; + } + class Teacher extends Person { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/MissingInheritanceConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/MissingInheritanceConc.cd new file mode 100644 index 00000000..c40ed444 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/MissingInheritanceConc.cd @@ -0,0 +1,4 @@ +classdiagram MissingInheritanceConc { + class Teacher { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/MissingInheritanceRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/MissingInheritanceRef.cd new file mode 100644 index 00000000..663eab31 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/MissingInheritanceRef.cd @@ -0,0 +1,6 @@ +classdiagram MissingInheritanceRef { + class Person { + } + class Teacher extends Person { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/adapter/Person.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/adapter/Person.java new file mode 100644 index 00000000..3c2cc8e1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/adapter/Person.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.inheritance; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Person"}, template = "${}") +public class Person { + @Adapt(ref = {"Person.number"}, template = "${}") + private int number; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/adapter/Teacher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/adapter/Teacher.java new file mode 100644 index 00000000..c60c76a7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/adapter/Teacher.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.inheritance; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Teacher"}, template = "${}") +public class Teacher extends Person { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/concrete/Teacher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/concrete/Teacher.java new file mode 100644 index 00000000..cbdaf4ee --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/inheritance/concrete/Teacher.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.inheritance; + +public class Teacher { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/Reference.cd new file mode 100644 index 00000000..8e3b88c3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/Reference.cd @@ -0,0 +1,8 @@ +classdiagram Reference { + class Account { + void withdraw(int amount); + void deposit(int amount); + void transfer(int amount, Account account); + int getBalance(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/adapter/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/adapter/Account.java new file mode 100644 index 00000000..63ea126d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/adapter/Account.java @@ -0,0 +1,24 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Account"}, template = "${}") +public class Account { + @Adapt(ref = {"Account.withdraw"}, template = "${}") + public void withdraw(int amount) { + } + + @Adapt(ref = {"Account.deposit"}, template = "${}") + public void deposit(int amount) { + } + + @Adapt(ref = {"Account.transfer"}, template = "${}") + public void transfer(int amount, Account account) { + } + + @Adapt(ref = {"Account.getBalance"}, template = "${}") + public int getBalance() { + return 0; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/ClassEmptyConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/ClassEmptyConc.cd new file mode 100644 index 00000000..cf9318f0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/ClassEmptyConc.cd @@ -0,0 +1,3 @@ +classdiagram MethodMissingConc { + class Account; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/ClassMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/ClassMissingConc.cd new file mode 100644 index 00000000..4edd8558 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/ClassMissingConc.cd @@ -0,0 +1,3 @@ +classdiagram ClassMissingConc { + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodInSuperClassConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodInSuperClassConc.cd new file mode 100644 index 00000000..a37f9ecf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodInSuperClassConc.cd @@ -0,0 +1,10 @@ +classdiagram MethodInSuperClassConc { + class AccountBase { + void withdraw(int amount); + void deposit(int amount); + void transfer(int amount, AccountBase account); + int getBalance(); + } + + class Account extends AccountBase; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodInSuperClassOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodInSuperClassOut.cd new file mode 100644 index 00000000..e0244180 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodInSuperClassOut.cd @@ -0,0 +1,13 @@ +classdiagram MethodInSuperClassConc { + class AccountBase { + void withdraw(int amount); + void deposit(int amount); + void transfer(int amount, AccountBase account); + int getBalance(); + } + + class Account extends AccountBase { + // The signature in AccountBase is not enough to serve as incarnation of the reference method + void transfer(int amount, Account account); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodMissingConc.cd new file mode 100644 index 00000000..37ce1c4a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MethodMissingConc.cd @@ -0,0 +1,7 @@ +classdiagram MethodMissingConc { + class Account { + void withdraw(int amount); + void deposit(int amount); + void transfer(int amount, Account account); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MultipleMethodsMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MultipleMethodsMissingConc.cd new file mode 100644 index 00000000..cbfe1aea --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/MultipleMethodsMissingConc.cd @@ -0,0 +1,6 @@ +classdiagram MultipleMethodsMissingConc { + class Account { + void withdraw(int amount); + void deposit(int amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/WrongSignatureConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/WrongSignatureConc.cd new file mode 100644 index 00000000..bf8efcf3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/WrongSignatureConc.cd @@ -0,0 +1,6 @@ +classdiagram WrongSignatureConc { + class Account { + void withdraw(double amount); + void deposit(double amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/WrongSignatureOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/WrongSignatureOut.cd new file mode 100644 index 00000000..acf22e0b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/WrongSignatureOut.cd @@ -0,0 +1,10 @@ +classdiagram WrongSignatureConc { + class Account { + void withdraw(double amount); + void deposit(double amount); + void withdraw(int amount); + void deposit(int amount); + void transfer(int amount, Account account); + int getBalance(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/ClassEmpty/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/ClassEmpty/Account.java new file mode 100644 index 00000000..b7711ad3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/ClassEmpty/Account.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.classempty; + +public class Account { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodInSuperClass/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodInSuperClass/Account.java new file mode 100644 index 00000000..a73b0a3c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodInSuperClass/Account.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.methodinsuperclass; + +public class Account extends AccountBase { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodInSuperClass/AccountBase.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodInSuperClass/AccountBase.java new file mode 100644 index 00000000..048de4c9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodInSuperClass/AccountBase.java @@ -0,0 +1,17 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.methodinsuperclass; + +public class AccountBase { + public void withdraw(int amount) { + } + + public void deposit(int amount) { + } + + public void transfer(int amount, AccountBase account) { + } + + public int getBalance() { + return 0; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodMissing/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodMissing/Account.java new file mode 100644 index 00000000..2b5cfecf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MethodMissing/Account.java @@ -0,0 +1,13 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.methodmissing; + +public class Account { + public void withdraw(int amount) { + } + + public void deposit(int amount) { + } + + public void transfer(int amount, Account account) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MultipleMethodsMissing/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MultipleMethodsMissing/Account.java new file mode 100644 index 00000000..2453ba82 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/MultipleMethodsMissing/Account.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.multiplemethodsmissing; + +public class Account { + public void withdraw(int amount) { + } + + public void deposit(int amount) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/WrongSignature/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/WrongSignature/Account.java new file mode 100644 index 00000000..16f11f17 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/basic/valid/concrete/WrongSignature/Account.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.methods.basic.valid.concrete.wrongsignature; + +public class Account { + public void withdraw(double amount) { + } + + public void deposit(double amount) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeConc.cd new file mode 100644 index 00000000..75a16f5b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram ForEachAttributeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeConc.cd new file mode 100644 index 00000000..a54aa0dc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentReturnTypeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeOut.cd new file mode 100644 index 00000000..693e3e9a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeOut.cd @@ -0,0 +1,15 @@ +import java.lang.String; + +classdiagram ForEachAttributeDifferentReturnTypeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + + <> boolean isFirstNameModified(); + <> boolean isLastNameModified(); + <> boolean isNumberModified(); + <> boolean isSalaryModified(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeRef.cd new file mode 100644 index 00000000..4f0a0057 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeDifferentReturnTypeRef.cd @@ -0,0 +1,8 @@ + +classdiagram ForEachAttributeDifferentReturnTypeRef { + class DataClass { + any attribute; + + <> boolean isAttributeModified(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersConc.cd new file mode 100644 index 00000000..8c26d335 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram ForEachAttributeMultipleParametersConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersOut.cd new file mode 100644 index 00000000..3fdd20c4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersOut.cd @@ -0,0 +1,19 @@ +import java.lang.String; + +classdiagram ForEachAttributeMultipleParametersConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + + <> + void updateFirstName(String firstName, String changedBy); + <> + void updateLastName(String lastName, String changedBy); + <> + void updateNumber(int number, String changedBy); + <> + void updateSalary(int salary, String changedBy); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersRef.cd new file mode 100644 index 00000000..49e9de0a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeMultipleParametersRef.cd @@ -0,0 +1,9 @@ +import java.lang.String; + +classdiagram ForEachAttributeMultipleParametersRef { + class DataClass { + any attribute; + // the first parameter is adapted to the attribute and the second always stays the same + <> void updateAttribute(any attribute, String changedBy); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeOut.cd new file mode 100644 index 00000000..053fb7bc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeOut.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram ForEachAttributeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + class Builder { + <> Builder firstName(String firstName); + <> Builder lastName(String lastName); + <> Builder number(int number); + <> Builder salary(int salary); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeRef.cd new file mode 100644 index 00000000..f3849837 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeRef.cd @@ -0,0 +1,12 @@ + +classdiagram ForEachAttributeRef { + class DataClass { + any attribute; + } + + class Builder { + // Incarnations will have the same name as the referenced attribute incarnations by convention + // because the referenced attribute has the same name as the annotated attribute! + <> Builder attribute(any attribute); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeClassMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeClassMIConc.cd new file mode 100644 index 00000000..7f9af6f2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeClassMIConc.cd @@ -0,0 +1,15 @@ +import java.lang.String; + +classdiagram ForEachAttributeSameReturnTypeClassMIConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeClassMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeClassMIOut.cd new file mode 100644 index 00000000..d54467f2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeClassMIOut.cd @@ -0,0 +1,23 @@ +import java.lang.String; + +classdiagram ForEachAttributeSameReturnTypeClassMIConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + // added methods + <> String getFirstName(); + <> String getLastName(); + <> int getNumber(); + <> int getSalary(); + } + + <> class Department { + <> String managerName; + <> int depId; + // added methods + <> String getManagerName(); + <> int getDepId(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeConc.cd new file mode 100644 index 00000000..9b099c4a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram ForEachAttributeSameReturnTypeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeOut.cd new file mode 100644 index 00000000..a786ac1c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeOut.cd @@ -0,0 +1,15 @@ +import java.lang.String; + +classdiagram ForEachAttributeSameReturnTypeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + + <> String getFirstName(); + <> String getLastName(); + <> int getNumber(); + <> int getSalary(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeRef.cd new file mode 100644 index 00000000..d154c0e9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachAttributeSameReturnTypeRef.cd @@ -0,0 +1,7 @@ + +classdiagram ForEachAttributeSameReturnTypeRef { + class DataClass { + any attribute; + <> any getAttribute(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeConc.cd new file mode 100644 index 00000000..51ff6e6b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeConc.cd @@ -0,0 +1,5 @@ +classdiagram ForEachTypeSameReturnTypeConc { + <> class HandshakeMessage; + <> class DataMessage; + <> class AckMessage; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeNoNameMatchOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeNoNameMatchOut.cd new file mode 100644 index 00000000..f2ab72a0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeNoNameMatchOut.cd @@ -0,0 +1,11 @@ +classdiagram ForEachTypeSameReturnTypeConc { + <> class HandshakeMessage; + <> class DataMessage; + <> class AckMessage; + + class MessageBus { + <> void send(HandshakeMessage handshakeMessage); + <> void send(DataMessage dataMessage); + <> void send(AckMessage ackMessage); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeNoNameMatchRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeNoNameMatchRef.cd new file mode 100644 index 00000000..70d3ab56 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeNoNameMatchRef.cd @@ -0,0 +1,7 @@ +classdiagram ForEachTypeSameParameterTypeNoNameMatchRef { + class Message; + + class MessageBus { + <> void send(Message message); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeOut.cd new file mode 100644 index 00000000..11c64bb9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeOut.cd @@ -0,0 +1,11 @@ +classdiagram ForEachTypeSameReturnTypeConc { + <> class HandshakeMessage; + <> class DataMessage; + <> class AckMessage; + + class MessageBus { + <> void sendHandshakeMessage(HandshakeMessage handshakeMessage); + <> void sendDataMessage(DataMessage dataMessage); + <> void sendAckMessage(AckMessage ackMessage); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeRef.cd new file mode 100644 index 00000000..16e00859 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameParameterTypeRef.cd @@ -0,0 +1,7 @@ +classdiagram ForEachTypeSameReturnTypeRef { + class Message; + + class MessageBus { + <> void sendMessage(Message message); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeConc.cd new file mode 100644 index 00000000..aef1bf4d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeConc.cd @@ -0,0 +1,5 @@ +classdiagram ForEachTypeSameReturnTypeConc { + <> class EmployeeBuilder; + <> class DepartmentBuilder; + <> class CompanyBuilder; +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeNoNameMatchOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeNoNameMatchOut.cd new file mode 100644 index 00000000..015cba1a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeNoNameMatchOut.cd @@ -0,0 +1,11 @@ +classdiagram ForEachTypeSameReturnTypeConc { + <> class EmployeeBuilder; + <> class DepartmentBuilder; + <> class CompanyBuilder; + + class Mill { + <> EmployeeBuilder create_EmployeeBuilder(); + <> DepartmentBuilder create_DepartmentBuilder(); + <> CompanyBuilder create_CompanyBuilder(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeNoNameMatchRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeNoNameMatchRef.cd new file mode 100644 index 00000000..7fd3ef7a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeNoNameMatchRef.cd @@ -0,0 +1,9 @@ +classdiagram ForEachTypeSameReturnTypeNoNameMatchRef { + class Builder; + + class Mill { + // we intentionally not use 'Builder' as infix of the method name here, to demonstrate that + // the tools needs to add a suffix to the method for each type if it cannot adapt the name + <> Builder create(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeOut.cd new file mode 100644 index 00000000..b1784a33 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeOut.cd @@ -0,0 +1,11 @@ +classdiagram ForEachTypeSameReturnTypeConc { + <> class EmployeeBuilder; + <> class DepartmentBuilder; + <> class CompanyBuilder; + + class Mill { + <> EmployeeBuilder employeeBuilder(); + <> DepartmentBuilder departmentBuilder(); + <> CompanyBuilder companyBuilder(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeRef.cd new file mode 100644 index 00000000..f80c8709 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/ForEachTypeSameReturnTypeRef.cd @@ -0,0 +1,7 @@ +classdiagram ForEachTypeSameReturnTypeRef { + class Builder; + + class Mill { + <> Builder builder(); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/DataClass.java new file mode 100644 index 00000000..0067e23c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/DataClass.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttribute/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttribute/Builder.java new file mode 100644 index 00000000..8a62db25 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttribute/Builder.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachattribute; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}") +public class Builder { + @Adapt(ref = {"Builder.attribute"}, template = "${}") + public Builder attribute(Object attribute) { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttributeDifferentReturnType/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttributeDifferentReturnType/DataClass.java new file mode 100644 index 00000000..3e09e6bc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttributeDifferentReturnType/DataClass.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachattributedifferentreturntype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + + @Adapt(ref = {"DataClass.isAttributeModified"}, template = "${}") + public boolean isAttributeModified() { + return false; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttributeMultipleParameters/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttributeMultipleParameters/DataClass.java new file mode 100644 index 00000000..58f95f11 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachAttributeMultipleParameters/DataClass.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachattributemultipleparameters; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { + @Adapt(ref = {"DataClass.attribute"}, template = "${}") + private Object attribute; + + @Adapt(ref = {"DataClass.updateAttribute"}, template = "${}") + public void updateAttribute(Object attribute, String changedBy) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameParameterType/Message.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameParameterType/Message.java new file mode 100644 index 00000000..8df97281 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameParameterType/Message.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachtypesameparametertype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Message"}, template = "${}") +public class Message { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameParameterType/MessageBus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameParameterType/MessageBus.java new file mode 100644 index 00000000..42127968 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameParameterType/MessageBus.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachtypesameparametertype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"MessageBus"}, template = "${}") +public class MessageBus { + @Adapt(ref = {"MessageBus.sendMessage"}, template = "${}") + public void sendMessage(Message message) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameReturnType/Mill.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameReturnType/Mill.java new file mode 100644 index 00000000..1a6bd361 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/adapter/ForEachTypeSameReturnType/Mill.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach.adapter.foreachtypesamereturntype; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Mill"}, template = "${}") +public class Mill { + @Adapt(ref = {"Mill.builder"}, template = "${}") + public Builder builder() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/CompanyBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/CompanyBuilder.java new file mode 100644 index 00000000..10852c78 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/CompanyBuilder.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach; + +public class CompanyBuilder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/DepartmentBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/DepartmentBuilder.java new file mode 100644 index 00000000..8494ee19 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/DepartmentBuilder.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach; + +public class DepartmentBuilder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/EmployeeBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/EmployeeBuilder.java new file mode 100644 index 00000000..26e39252 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/forEach/concrete/EmployeeBuilder.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.foreach; + +public class EmployeeBuilder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd new file mode 100644 index 00000000..70c5b808 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterAndReturnTypeMIConc.cd @@ -0,0 +1,8 @@ +classdiagram ParameterAndReturnTypeMIConc { + + <> class PersonalAccount; + <> class BusinessAccount; + + <> class NormalTransaction; + <> class InstantTransaction; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterAndReturnTypeMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterAndReturnTypeMIOut.cd new file mode 100644 index 00000000..81d0ad23 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterAndReturnTypeMIOut.cd @@ -0,0 +1,22 @@ +classdiagram ParameterAndReturnTypeMIConc { + + <> class PersonalAccount; + <> class BusinessAccount; + + <> class NormalTransaction; + <> class InstantTransaction; + + class Bank { + <> void createPersonalAccount(PersonalAccount personalAccount); + <> void createBusinessAccount(BusinessAccount businessAccount); + <> NormalTransaction transfer_NormalTransaction(PersonalAccount from, PersonalAccount to, int amount); + <> NormalTransaction transfer_NormalTransaction(PersonalAccount from, BusinessAccount to, int amount); + <> NormalTransaction transfer_NormalTransaction(BusinessAccount from, PersonalAccount to, int amount); + <> NormalTransaction transfer_NormalTransaction(BusinessAccount from, BusinessAccount to, int amount); + + <> InstantTransaction transfer_InstantTransaction(PersonalAccount from, PersonalAccount to, int amount); + <> InstantTransaction transfer_InstantTransaction(PersonalAccount from, BusinessAccount to, int amount); + <> InstantTransaction transfer_InstantTransaction(BusinessAccount from, PersonalAccount to, int amount); + <> InstantTransaction transfer_InstantTransaction(BusinessAccount from, BusinessAccount to, int amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIConc.cd new file mode 100644 index 00000000..18756dee --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIConc.cd @@ -0,0 +1,5 @@ +classdiagram ParameterTypeMIConc { + + <> class PersonalAccount; + <> class BusinessAccount; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOneExistsConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOneExistsConc.cd new file mode 100644 index 00000000..44462a71 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOneExistsConc.cd @@ -0,0 +1,9 @@ +classdiagram ParameterTypeMIOneExistsConc { + + <> class PersonalAccount; + <> class BusinessAccount; + + class Bank { + void createAccount(PersonalAccount account); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOneExistsOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOneExistsOut.cd new file mode 100644 index 00000000..2d7133ce --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOneExistsOut.cd @@ -0,0 +1,20 @@ +classdiagram ParameterTypeMIOneExistsConc { + + <> class PersonalAccount; + <> class BusinessAccount; + + class Transaction; + + class Bank { + void createAccount(PersonalAccount account); + // NO second createAccount method, for BusinessAccount because we already have one incarnation + // that satisfies the conformance check + // if we want the tool to complete all combinations: use "forEach" in reference model + // --- + // for 'transfer' all possible combinations are completed as there was not a single incarnation + <> Transaction transfer(PersonalAccount from, PersonalAccount to, int amount); + <> Transaction transfer(PersonalAccount from, BusinessAccount to, int amount); + <> Transaction transfer(BusinessAccount from, PersonalAccount to, int amount); + <> Transaction transfer(BusinessAccount from, BusinessAccount to, int amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOut.cd new file mode 100644 index 00000000..df3a3e5a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ParameterTypeMIOut.cd @@ -0,0 +1,16 @@ +classdiagram ParameterTypeMIConc { + + <> class PersonalAccount; + <> class BusinessAccount; + + class Transaction; + + class Bank { + <> void createPersonalAccount(PersonalAccount personalAccount); + <> void createBusinessAccount(BusinessAccount businessAccount); + <> Transaction transfer(PersonalAccount from, PersonalAccount to, int amount); + <> Transaction transfer(PersonalAccount from, BusinessAccount to, int amount); + <> Transaction transfer(BusinessAccount from, PersonalAccount to, int amount); + <> Transaction transfer(BusinessAccount from, BusinessAccount to, int amount); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/Reference.cd new file mode 100644 index 00000000..116b5429 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/Reference.cd @@ -0,0 +1,10 @@ +classdiagram Reference { + class Account; + + class Transaction; + + class Bank { + void createAccount(Account account); + Transaction transfer(Account from, Account to, int amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIConc.cd new file mode 100644 index 00000000..438e69c1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIConc.cd @@ -0,0 +1,5 @@ +classdiagram ReturnTypeMIConc { + + <> class NormalTransaction; + <> class InstantTransaction; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOneExistsConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOneExistsConc.cd new file mode 100644 index 00000000..997b247c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOneExistsConc.cd @@ -0,0 +1,11 @@ +classdiagram ReturnTypeMIOneExistsConc { + + <> class NormalTransaction; + <> class InstantTransaction; + + class Account; + + class Bank { + NormalTransaction transfer(Account from, Account to, int amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOneExistsOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOneExistsOut.cd new file mode 100644 index 00000000..d49ec11c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOneExistsOut.cd @@ -0,0 +1,15 @@ +classdiagram ReturnTypeMIOneExistsConc { + + <> class NormalTransaction; + <> class InstantTransaction; + + class Account; + + class Bank { + void createAccount(Account account); + NormalTransaction transfer(Account from, Account to, int amount); + // NO second 'transfer' method, for InstantTransaction because we already have one incarnation + // that satisfies the conformance check + // if we want the tool to complete all combinations: use "forEach" in reference model + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOut.cd new file mode 100644 index 00000000..557fde3b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/ReturnTypeMIOut.cd @@ -0,0 +1,13 @@ +classdiagram ReturnTypeMIConc { + + <> class NormalTransaction; + <> class InstantTransaction; + + class Account; + + class Bank { + void createAccount(Account account); + <> NormalTransaction transfer_NormalTransaction(Account from, Account to, int amount); + <> InstantTransaction transfer_InstantTransaction(Account from, Account to, int amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/adapter/Bank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/adapter/Bank.java new file mode 100644 index 00000000..ea6e428a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/adapter/Bank.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Bank"}, template = "${}") +public class Bank { + @Adapt(ref = {"Bank.createAccount"}, template = "${}") + public void createAccount(Account account) { + } + + @Adapt(ref = {"Bank.transfer"}, template = "${}") + public Transaction transfer(Account from, Account to, int amount) { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/adapter/Transaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/adapter/Transaction.java new file mode 100644 index 00000000..1972a775 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/adapter/Transaction.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Transaction"}, template = "${}") +public class Transaction { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/Bank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/Bank.java new file mode 100644 index 00000000..a83b80cd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/Bank.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; + +public class Bank { + public NormalTransaction transfer(Account from, Account to, int amount) { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/BusinessAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/BusinessAccount.java new file mode 100644 index 00000000..cb73b734 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/BusinessAccount.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; + +public class BusinessAccount { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/InstantTransaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/InstantTransaction.java new file mode 100644 index 00000000..9f33d75b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/InstantTransaction.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; + +public class InstantTransaction { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/NormalTransaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/NormalTransaction.java new file mode 100644 index 00000000..f84c5991 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/NormalTransaction.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; + +public class NormalTransaction { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ParameterTypeMIOneExists/Bank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ParameterTypeMIOneExists/Bank.java new file mode 100644 index 00000000..f7db939e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ParameterTypeMIOneExists/Bank.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.parametertypemioneexists; + +public class Bank { + public void createAccount(PersonalAccount account) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/PersonalAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/PersonalAccount.java new file mode 100644 index 00000000..d7a188c7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/PersonalAccount.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation; + +public class PersonalAccount { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/Account.java new file mode 100644 index 00000000..bdd9735d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/Account.java @@ -0,0 +1,3 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.returntypemioneexists; + +public class Account {} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/Bank.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/Bank.java new file mode 100644 index 00000000..616ad003 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/Bank.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.returntypemioneexists; + +public class Bank { + public NormalTransaction transfer(Account from, Account to, int amount) { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/InstantTransaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/InstantTransaction.java new file mode 100644 index 00000000..d5444c2f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/InstantTransaction.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.returntypemioneexists; + +public class InstantTransaction { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/NormalTransaction.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/NormalTransaction.java new file mode 100644 index 00000000..fb25c289 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/multiIncarnation/concrete/ReturnTypeMIOneExists/NormalTransaction.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.methods.multiincarnation.concrete.returntypemioneexists; + +public class NormalTransaction { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedIncarnatedConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedIncarnatedConc.cd new file mode 100644 index 00000000..3fe6e40d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedIncarnatedConc.cd @@ -0,0 +1,8 @@ +import java.lang.String; + +classdiagram ParameterTypeUnderspecifiedIncarnatedConc { + + <> class MyOperator { + <> void foo(String param); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedIncarnatedOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedIncarnatedOut.cd new file mode 100644 index 00000000..c1867e35 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedIncarnatedOut.cd @@ -0,0 +1,9 @@ +import java.lang.String; + +classdiagram ParameterTypeUnderspecifiedIncarnatedConc { + + <> class MyOperator { + // nothing changed. we just make sure the tool can process the input and does not add anything + <> void foo(String param); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedNoIncConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedNoIncConc.cd new file mode 100644 index 00000000..00410f44 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedNoIncConc.cd @@ -0,0 +1,8 @@ +classdiagram ParameterTypeUnderspecifiedRef { + + <> class MyOperator { + // NOTE: we have no incarnation of "operation(any)" + // -> leads to error because a parameter is underspecified + void foo(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedRef.cd new file mode 100644 index 00000000..aec36122 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ParameterTypeUnderspecifiedRef.cd @@ -0,0 +1,6 @@ +classdiagram ParameterTypeUnderspecifiedRef { + + class Operator { + void operation(any param); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedIncarnatedConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedIncarnatedConc.cd new file mode 100644 index 00000000..a60a25bf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedIncarnatedConc.cd @@ -0,0 +1,6 @@ +classdiagram ReturnTypeUnderspecifiedIncarnatedConc { + + <> class MyOperator { + <> void foo(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedIncarnatedOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedIncarnatedOut.cd new file mode 100644 index 00000000..5dd5d438 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedIncarnatedOut.cd @@ -0,0 +1,7 @@ +classdiagram ReturnTypeUnderspecifiedIncarnatedConc { + + <> class MyOperator { + // nothing changed. we just make sure the tool can process the input and does not add anything + <> void foo(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedNoIncConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedNoIncConc.cd new file mode 100644 index 00000000..8673bb5c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedNoIncConc.cd @@ -0,0 +1,8 @@ +classdiagram ReturnTypeUnderspecifiedRef { + + <> class MyOperator { + // NOTE: we have no incarnation of "operation()" + // -> leads to error because the return type is underspecified + void foo(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedRef.cd new file mode 100644 index 00000000..fabe4433 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/ReturnTypeUnderspecifiedRef.cd @@ -0,0 +1,6 @@ +classdiagram ReturnTypeUnderspecifiedRef { + + class Operator { + any operation(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/adapter/ParameterTypeUnderspecifiedIncarnated/Operator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/adapter/ParameterTypeUnderspecifiedIncarnated/Operator.java new file mode 100644 index 00000000..0b3d52d2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/adapter/ParameterTypeUnderspecifiedIncarnated/Operator.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.cdconcretization.methods.underspecified.adapter.parametertypeunderspecifiedincarnated; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Operator"}, template = "${}") +public class Operator { + @Adapt(ref = {"Operator.operation"}, template = "${}") + public void operation(Object param) { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/adapter/ReturnTypeUnderspecifiedIncarnated/Operator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/adapter/ReturnTypeUnderspecifiedIncarnated/Operator.java new file mode 100644 index 00000000..1f8f0bb3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/adapter/ReturnTypeUnderspecifiedIncarnated/Operator.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.cdconcretization.methods.underspecified.adapter.returntypeunderspecifiedincarnated; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Operator"}, template = "${}") +public class Operator { + @Adapt(ref = {"Operator.operation"}, template = "${}") + public Object operation() { + return null; + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/concrete/MyOperator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/concrete/MyOperator.java new file mode 100644 index 00000000..48dbb93b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/methods/underspecified/concrete/MyOperator.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.methods.underspecified; + +public class MyOperator { + public void foo() { + } + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIConc.cd new file mode 100644 index 00000000..44a59579 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIConc.cd @@ -0,0 +1,6 @@ +classdiagram AttributeTypeMIConc { + + <> class Salery; + + <> class CompanyIdentifier; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIOut.cd new file mode 100644 index 00000000..9c8366fa --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIOut.cd @@ -0,0 +1,11 @@ +classdiagram AttributeTypeMIConc { + + <> class Salery; + + <> class CompanyIdentifier; + + class Employee { + <> CompanyIdentifier value_CompanyIdentifier; + <> Salery value_Salery; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIRef.cd new file mode 100644 index 00000000..f17066a7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/AttributeTypeMIRef.cd @@ -0,0 +1,8 @@ +classdiagram AttributeTypeMIRef { + + class SomeValueType; + + class Employee { + SomeValueType value; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIConc.cd new file mode 100644 index 00000000..0f1756ec --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIConc.cd @@ -0,0 +1,15 @@ +classdiagram BothAssocSidesMIConc { + + <> class SuperTeacher1 { + } + <> class SuperTeacher2 { + } + <> class Teacher1 { + } + <> class Teacher2 { + } + <> class Department1 { + } + <> class Department2 { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMINameAdaptationOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMINameAdaptationOut.cd new file mode 100644 index 00000000..aadd20ad --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMINameAdaptationOut.cd @@ -0,0 +1,15 @@ +classdiagram BothAssocSidesMIConc { + <> class SuperTeacher1; + <> class SuperTeacher2; + + <> class Teacher1 extends SuperTeacher2, SuperTeacher1; + <> class Teacher2 extends SuperTeacher2, SuperTeacher1; + + <> class Department1; + <> class Department2; + + association Teacher1 (hasTeacher1) -- (teachesIn_Department1) Department1; + association Teacher1 (hasTeacher1) -- (teachesIn_Department2) Department2; + association Teacher2 (hasTeacher2) -- (teachesIn_Department1) Department1; + association Teacher2 (hasTeacher2) -- (teachesIn_Department2) Department2; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOneAssocExistsConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOneAssocExistsConc.cd new file mode 100644 index 00000000..98af766d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOneAssocExistsConc.cd @@ -0,0 +1,18 @@ +classdiagram BothAssocSidesMIConc { + + <> class SuperTeacher1 { + } + <> class SuperTeacher2 { + } + <> class Teacher1 { + } + <> class Teacher2 { + } + <> class Department1 { + } + <> class Department2 { + } + + association Teacher1 (hasTeacher) -- (teachesIn) Department1; + association Teacher2 (hasTeacher) -- (teachesIn) Department2; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOneAssocExistsOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOneAssocExistsOut.cd new file mode 100644 index 00000000..95f0521f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOneAssocExistsOut.cd @@ -0,0 +1,25 @@ +classdiagram BothAssocSidesMIConc { + <>class SuperTeacher1 { + + } + <>class SuperTeacher2 { + + } + <>class Teacher1 extends SuperTeacher2,SuperTeacher1 { + + } + <>class Teacher2 extends SuperTeacher2,SuperTeacher1 { + + } + <>class Department1 { + + } + <>class Department2 { + + } + + association Teacher1(hasTeacher1)--(teachesIn)Department1; + association Teacher2(hasTeacher2)--(teachesIn)Department2; + // no more associations are added here because each type incarnation already has one association +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOut.cd new file mode 100644 index 00000000..aadd20ad --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIOut.cd @@ -0,0 +1,15 @@ +classdiagram BothAssocSidesMIConc { + <> class SuperTeacher1; + <> class SuperTeacher2; + + <> class Teacher1 extends SuperTeacher2, SuperTeacher1; + <> class Teacher2 extends SuperTeacher2, SuperTeacher1; + + <> class Department1; + <> class Department2; + + association Teacher1 (hasTeacher1) -- (teachesIn_Department1) Department1; + association Teacher1 (hasTeacher1) -- (teachesIn_Department2) Department2; + association Teacher2 (hasTeacher2) -- (teachesIn_Department1) Department1; + association Teacher2 (hasTeacher2) -- (teachesIn_Department2) Department2; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIRef.cd new file mode 100644 index 00000000..40bd1aeb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/BothAssocSidesMIRef.cd @@ -0,0 +1,10 @@ +classdiagram BothAssocSidesMIRef { + + class SuperTeacher { + } + class Teacher extends SuperTeacher { + } + class Department { + } + association Teacher (hasTeacher) -- (teachesIn) Department; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIConc.cd new file mode 100644 index 00000000..6262d296 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIConc.cd @@ -0,0 +1,8 @@ +classdiagram ClassMIConc { + <> class Operations { + int supervisorID; + } + <> class Administration { + int managerID; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIOut.cd new file mode 100644 index 00000000..90d23212 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIOut.cd @@ -0,0 +1,11 @@ +classdiagram ClassMIConc { + <> class Operations { + int supervisorID; + int deptID; + } + + <> class Administration { + int managerID; + int deptID; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIRef.cd new file mode 100644 index 00000000..b0ba1e67 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/ClassMIRef.cd @@ -0,0 +1,5 @@ +classdiagram ClassMIRef { + class Department { + int deptID; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIConc.cd new file mode 100644 index 00000000..9d55fe13 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIConc.cd @@ -0,0 +1,6 @@ +classdiagram InterfaceMIConc { + + <> interface IEmployee1; + + <> interface IEmployee2; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIOut.cd new file mode 100644 index 00000000..d019f698 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIOut.cd @@ -0,0 +1,9 @@ +classdiagram InterfaceMIConc { + + <> interface IEmployee1; + + <> interface IEmployee2; + + class Employee implements IEmployee1, IEmployee2; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIRef.cd new file mode 100644 index 00000000..4aaab1c0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/InterfaceMIRef.cd @@ -0,0 +1,6 @@ +classdiagram InterfaceMIRef { + + interface IEmployee; + + class Employee implements IEmployee; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/UnequalCardCon.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/UnequalCardCon.cd new file mode 100644 index 00000000..b6cd7839 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/UnequalCardCon.cd @@ -0,0 +1,14 @@ +classdiagram UnequalCardCon { + class Company { + } + + <> class Worker { + } + + <> class Manager { + } + + association Company -- Manager [1]; + association Company -- Worker [0..1]; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/UnequalCardRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/UnequalCardRef.cd new file mode 100644 index 00000000..686d34af --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/UnequalCardRef.cd @@ -0,0 +1,9 @@ +classdiagram RefTest { + class Company { + } + + class Employee { + } + association Company -- Employee [1..*]; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/AttributeTypeMI/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/AttributeTypeMI/Employee.java new file mode 100644 index 00000000..e9ef570c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/AttributeTypeMI/Employee.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.attributetypemi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee { + @Adapt(ref = {"Employee.value"}, template = "${}") + private SomeValueType value; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/AttributeTypeMI/SomeValueType.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/AttributeTypeMI/SomeValueType.java new file mode 100644 index 00000000..1cc918f6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/AttributeTypeMI/SomeValueType.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.attributetypemi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"SomeValueType"}, template = "${}") +public class SomeValueType { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/BothAssocSidesMI/SuperTeacher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/BothAssocSidesMI/SuperTeacher.java new file mode 100644 index 00000000..24bacff5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/BothAssocSidesMI/SuperTeacher.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.bothassocsidesmi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"SuperTeacher"}, template = "${}") +public class SuperTeacher { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/BothAssocSidesMI/Teacher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/BothAssocSidesMI/Teacher.java new file mode 100644 index 00000000..7cb94433 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/BothAssocSidesMI/Teacher.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.bothassocsidesmi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Teacher"}, template = "${}") +public class Teacher extends SuperTeacher { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/ClassMI/Department.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/ClassMI/Department.java new file mode 100644 index 00000000..9acb67d6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/ClassMI/Department.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.classmi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Department"}, template = "${}") +public class Department { + @Adapt(ref = {"Department.deptID"}, template = "${}") + private int deptID; + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/InterfaceMI/Employee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/InterfaceMI/Employee.java new file mode 100644 index 00000000..48d96e12 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/InterfaceMI/Employee.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.interfacemi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Employee"}, template = "${}") +public class Employee { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/InterfaceMI/IEmployee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/InterfaceMI/IEmployee.java new file mode 100644 index 00000000..00e39b48 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/adapter/InterfaceMI/IEmployee.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation.adapter.interfacemi; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"IEmployee"}, template = "${}") +public interface IEmployee { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/concrete/IEmployee1.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/concrete/IEmployee1.java new file mode 100644 index 00000000..91a933f6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/concrete/IEmployee1.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation; + +public interface IEmployee1 { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/concrete/IEmployee2.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/concrete/IEmployee2.java new file mode 100644 index 00000000..9b4af290 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/multipleIncarnation/concrete/IEmployee2.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.multipleincarnation; + +public interface IEmployee2 { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingConc.cd new file mode 100644 index 00000000..5ba6d713 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingConc.cd @@ -0,0 +1,4 @@ +classdiagram EnumMemberMissingConc { + enum Colour{RED,YELLOW,BLUE,PURPLE; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingOut.cd new file mode 100644 index 00000000..f6ebc8dc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingOut.cd @@ -0,0 +1,5 @@ +classdiagram EnumMemberMissingConc { + enum Colour { + RED,GREEN,YELLOW,BLUE,PURPLE; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingRef.cd new file mode 100644 index 00000000..1146df58 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/EnumMemberMissingRef.cd @@ -0,0 +1,4 @@ +classdiagram EnumMemberMissingRef { + enum Colour{RED,GREEN,BLUE; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/adapter/EnumMemberMissing/Colour.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/adapter/EnumMemberMissing/Colour.java new file mode 100644 index 00000000..1bfc9e4c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/adapter/EnumMemberMissing/Colour.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.types.enums.adapter.enummembermissing; + +public enum Colour { + RED, + GREEN, + BLUE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/concrete/Colour.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/concrete/Colour.java new file mode 100644 index 00000000..0833199c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/enums/concrete/Colour.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.cdconcretization.types.enums; + +public enum Colour { + RED, + YELLOW, + BLUE, + PURPLE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeConc.cd new file mode 100644 index 00000000..8ae1ffe0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeConc.cd @@ -0,0 +1,7 @@ +classdiagram ForEachTypeConc { + <> class Employee; + + <> class Department; + + <> class Company; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeConc.cd new file mode 100644 index 00000000..80367cf2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeConc.cd @@ -0,0 +1,15 @@ +import java.lang.String; + +classdiagram ForEachTypeForEachAttributeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeOut.cd new file mode 100644 index 00000000..d1cf4a5f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeOut.cd @@ -0,0 +1,29 @@ +import java.lang.String; + +classdiagram ForEachTypeForEachAttributeConc { + <> class Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> class Department { + <> String managerName; + <> int depId; + } + + <> + class Builder_Employee { + <> String firstName; + <> String lastName; + <> int number; + <> int salary; + } + + <> + class Builder_Department { + <> String managerName; + <> int depId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeRef.cd new file mode 100644 index 00000000..ad1cf61a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeForEachAttributeRef.cd @@ -0,0 +1,12 @@ + +classdiagram ForEachTypeForEachAttributeRef { + class DataClass { + any attribute; + } + + <> class Builder { + // Incarnations will have the same name as the referenced attribute incarnations by convention + // because the referenced attribute has the same name as the annotated attribute! + <> any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceConc.cd new file mode 100644 index 00000000..c4d7012c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceConc.cd @@ -0,0 +1,7 @@ +classdiagram ForEachTypeInfixReplaceConc { + <> class Employee; + + <> class Department; + + <> class Company; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceDisabledOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceDisabledOut.cd new file mode 100644 index 00000000..087c344f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceDisabledOut.cd @@ -0,0 +1,19 @@ +classdiagram ForEachTypeInfixReplaceConc { + <> class Employee; + + <> class Department; + + <> class Company; + + <> class DataClassBuilder_Employee { + + } + + <> class DataClassBuilder_Department { + + } + + <> class DataClassBuilder_Company{ + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceOut.cd new file mode 100644 index 00000000..3766efa2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceOut.cd @@ -0,0 +1,19 @@ +classdiagram ForEachTypeInfixReplaceConc { + <> class Employee; + + <> class Department; + + <> class Company; + + <> class EmployeeBuilder { + + } + + <> class DepartmentBuilder { + + } + + <> class CompanyBuilder { + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceRef.cd new file mode 100644 index 00000000..e8f9de57 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeInfixReplaceRef.cd @@ -0,0 +1,11 @@ +classdiagram ForEachTypeRef { + class DataClass { + + } + + // DataClass is an infix of 'DataClassBuilder'. Therefore, 'DataClass' is replaced with the + // incarnation name of 'DataClass' in the output + <> class DataClassBuilder { + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeNoTargetIncConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeNoTargetIncConc.cd new file mode 100644 index 00000000..a591fd91 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeNoTargetIncConc.cd @@ -0,0 +1,3 @@ +classdiagram ForEachTypeNoTargetIncConc { + +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeNoTargetIncOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeNoTargetIncOut.cd new file mode 100644 index 00000000..1362e902 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeNoTargetIncOut.cd @@ -0,0 +1,7 @@ +classdiagram ForEachTypeNoTargetIncConc { + class DataClass; + + <> class Builder { + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeOut.cd new file mode 100644 index 00000000..3523c9e2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeOut.cd @@ -0,0 +1,19 @@ +classdiagram ForEachTypeConc { + <> class Employee; + + <> class Department; + + <> class Company; + + <> class Builder_Employee { + + } + + <> class Builder_Department { + + } + + <> class Builder_Company { + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeRef.cd new file mode 100644 index 00000000..3ecc5c4c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/ForEachTypeRef.cd @@ -0,0 +1,9 @@ +classdiagram ForEachTypeRef { + class DataClass { + + } + + <> class Builder { + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/adapter/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/adapter/Builder.java new file mode 100644 index 00000000..9a9656f0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/adapter/Builder.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.types.foreach; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}") +public class Builder { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/adapter/DataClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/adapter/DataClass.java new file mode 100644 index 00000000..efaec237 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/forEach/adapter/DataClass.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.types.foreach; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"DataClass"}, template = "${}") +public class DataClass { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingConc.cd new file mode 100644 index 00000000..3da6126a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingConc.cd @@ -0,0 +1,12 @@ +classdiagram TypeMissingConc { + + class Course { + } + + enum CourseType { + LECTURE, + SEMINAR, + TUTORIAL; + + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingOut.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingOut.cd new file mode 100644 index 00000000..38c788ae --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingOut.cd @@ -0,0 +1,22 @@ +classdiagram TypeMissingConc { + class Course implements Teachable; + + interface Teachable; + + enum CourseType { + /* + * NOTE: The enum value position from the reference CD is preserved! + * This is important as reference code could access the values by index. + */ + LECTURE, + EXERCICE, + SEMINAR, + TUTORIAL; + } + + enum CourseStatus { + ACTIVE, + COMPLETED, + CANCELLED; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingRef.cd new file mode 100644 index 00000000..5322c241 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/TypeMissingRef.cd @@ -0,0 +1,18 @@ +classdiagram TypeMissingRef { + interface Teachable { + } + + class Course implements Teachable { + } + + enum CourseStatus { + ACTIVE, + COMPLETED, + CANCELLED; + } + + enum CourseType { + LECTURE, + EXERCICE; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/Course.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/Course.java new file mode 100644 index 00000000..49da829d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/Course.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.types.valid.adapter.typemissing; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Course"}, template = "${}") +public class Course { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/CourseStatus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/CourseStatus.java new file mode 100644 index 00000000..f28b5800 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/CourseStatus.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.types.valid.adapter.typemissing; + +public enum CourseStatus { + ACTIVE, + COMPLETED, + CANCELLED; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/CourseType.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/CourseType.java new file mode 100644 index 00000000..d3484986 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/CourseType.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.cdconcretization.types.valid.adapter.typemissing; + +public enum CourseType { + LECTURE, + EXERCICE; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/Teachable.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/Teachable.java new file mode 100644 index 00000000..4aef778d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/adapter/TypeMissing/Teachable.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.types.valid.adapter.typemissing; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Teachable"}, template = "${}") +public interface Teachable { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/concrete/Course.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/concrete/Course.java new file mode 100644 index 00000000..32be6f1c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/concrete/Course.java @@ -0,0 +1,4 @@ +package de.monticore.codeAdaption.cdconcretization.types.valid; + +public class Course { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/concrete/CourseType.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/concrete/CourseType.java new file mode 100644 index 00000000..1cdc4363 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/types/valid/concrete/CourseType.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.cdconcretization.types.valid; + +public enum CourseType { + LECTURE, + SEMINAR, + TUTORIAL; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/util/MethodSignatureStrings.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/util/MethodSignatureStrings.cd new file mode 100644 index 00000000..54ce82ee --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/cdconcretization/util/MethodSignatureStrings.cd @@ -0,0 +1,17 @@ +import java.lang.String; + +classdiagram MethodSignatureStrings { + class A { + // m1 must be resolved with full signature as the name itself is not unique + void m1(); + void m1(int i); + void m1(String s); + void m1(int i, String s); + void m1(int i, String s, double d); + void m1(int i, String s, double d, boolean b); + + // m2 and m3 can be resolved with the name only, no matter if they have parameters or not + int m2(); + String m3(int i, String s); + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/Concrete.cd new file mode 100644 index 00000000..7ee082fe --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/Concrete.cd @@ -0,0 +1,7 @@ +import java.lang.String; + +classdiagram Concrete { + <> class ConcreteService { + <> String name; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/Reference.cd new file mode 100644 index 00000000..4498c6ca --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/Reference.cd @@ -0,0 +1,7 @@ +import java.lang.String; + +classdiagram Reference { + class Service { + String name; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/Service.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/Service.java new file mode 100644 index 00000000..1b785d52 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/Service.java @@ -0,0 +1,12 @@ +package Concrete; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Service"}, template = "${}") +public class Service { + private ServiceSupport support = new ServiceSupport(); + + public Service decorate() { + return support.decorate(this); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/ServiceSupport.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/ServiceSupport.java new file mode 100644 index 00000000..c1ebddfc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/ServiceSupport.java @@ -0,0 +1,9 @@ +package Concrete; + +class ServiceSupport { + private SupportValue value = new SupportValue(); + + Service decorate(Service service) { + return value.apply(service); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/SupportValue.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/SupportValue.java new file mode 100644 index 00000000..9e0aeb21 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/SupportValue.java @@ -0,0 +1,7 @@ +package Concrete; + +class SupportValue { + Service apply(Service service) { + return service; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/UnrelatedHelper.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/UnrelatedHelper.java new file mode 100644 index 00000000..9edf01ba --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/adapter/UnrelatedHelper.java @@ -0,0 +1,7 @@ +package Concrete; + +class UnrelatedHelper { + String unused() { + return "unused"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/concrete/ConcreteService.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/concrete/ConcreteService.java new file mode 100644 index 00000000..4631d008 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/concrete/ConcreteService.java @@ -0,0 +1,4 @@ +package Concrete; + +public class ConcreteService extends ConcreteServiceTOP { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/concrete/ConcreteServiceTOP.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/concrete/ConcreteServiceTOP.java new file mode 100644 index 00000000..3e3f05d6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/concrete/ConcreteServiceTOP.java @@ -0,0 +1,13 @@ +package Concrete; + +public class ConcreteServiceTOP { + protected String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/Concrete.cd new file mode 100644 index 00000000..9cc67d33 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/Concrete.cd @@ -0,0 +1,4 @@ +classdiagram Concrete { + <> class PrimaryService; + <> class BackupService; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/Reference.cd new file mode 100644 index 00000000..41489d98 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/Reference.cd @@ -0,0 +1,3 @@ +classdiagram Reference { + class Service; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/adapter/Service.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/adapter/Service.java new file mode 100644 index 00000000..6057e608 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/adapter/Service.java @@ -0,0 +1,12 @@ +package Concrete; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Service"}, template = "${}") +public class Service { + private final ServiceSupport support = new ServiceSupport(); + + public Service normalize() { + return support.normalize(this); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/adapter/ServiceSupport.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/adapter/ServiceSupport.java new file mode 100644 index 00000000..fae9b769 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/adapter/ServiceSupport.java @@ -0,0 +1,7 @@ +package Concrete; + +class ServiceSupport { + Service normalize(Service service) { + return service; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/concrete/BackupService.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/concrete/BackupService.java new file mode 100644 index 00000000..2c6866a4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/concrete/BackupService.java @@ -0,0 +1,4 @@ +package Concrete; + +public class BackupService { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/concrete/PrimaryService.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/concrete/PrimaryService.java new file mode 100644 index 00000000..971e804f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/concretizationHelpers/divergent/concrete/PrimaryService.java @@ -0,0 +1,4 @@ +package Concrete; + +public class PrimaryService { +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AmbiguousOverloadConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AmbiguousOverloadConc.cd new file mode 100644 index 00000000..815a63d2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AmbiguousOverloadConc.cd @@ -0,0 +1,5 @@ +classdiagram AmbiguousOverloadConc { + <> class Service { + <> void syncConcrete(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AmbiguousOverloadRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AmbiguousOverloadRef.cd new file mode 100644 index 00000000..cee66525 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AmbiguousOverloadRef.cd @@ -0,0 +1,6 @@ +classdiagram AmbiguousOverloadRef { + class Service { + void sync(); + void sync(int value); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AnyConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AnyConc.cd new file mode 100644 index 00000000..a4009697 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AnyConc.cd @@ -0,0 +1,3 @@ +classdiagram AnyConc { + <> class Employee; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AnyRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AnyRef.cd new file mode 100644 index 00000000..ddd0f98c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AnyRef.cd @@ -0,0 +1,5 @@ +classdiagram AnyRef { + class DataClass { + any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationAmbiguousDirectionConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationAmbiguousDirectionConc.cd new file mode 100644 index 00000000..b4a26e66 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationAmbiguousDirectionConc.cd @@ -0,0 +1,6 @@ +classdiagram AssociationAmbiguousDirectionConc { + <> class LeftNode; + <> class RightNode; + + association LeftNode -- RightNode; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationAmbiguousDirectionRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationAmbiguousDirectionRef.cd new file mode 100644 index 00000000..f8c8c409 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationAmbiguousDirectionRef.cd @@ -0,0 +1,5 @@ +classdiagram AssociationAmbiguousDirectionRef { + class Node; + + association Node -- Node; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationCardinalityConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationCardinalityConc.cd new file mode 100644 index 00000000..3234b66b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationCardinalityConc.cd @@ -0,0 +1,6 @@ +classdiagram AssociationCardinalityConc { + <> class Company; + <> class Employee; + + association Company (company) -- (employees) Employee [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationCardinalityRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationCardinalityRef.cd new file mode 100644 index 00000000..b7edf231 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationCardinalityRef.cd @@ -0,0 +1,6 @@ +classdiagram AssociationCardinalityRef { + class Company; + class Employee; + + association Company (company) -- (employees) Employee [1..*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationRoleConflictConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationRoleConflictConc.cd new file mode 100644 index 00000000..210eee7c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationRoleConflictConc.cd @@ -0,0 +1,7 @@ +classdiagram AssociationRoleConflictConc { + <> class WiMi; + <> class StudentData; + class Lecture; + + association WiMi -> (observes) Lecture; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationRoleConflictRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationRoleConflictRef.cd new file mode 100644 index 00000000..f732d427 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/AssociationRoleConflictRef.cd @@ -0,0 +1,6 @@ +classdiagram AssociationRoleConflictRef { + class Observer; + class Observable; + + association Observer -> (observes) Observable; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/EnumOrderConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/EnumOrderConc.cd new file mode 100644 index 00000000..4281281f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/EnumOrderConc.cd @@ -0,0 +1,6 @@ +classdiagram EnumOrderConc { + <> enum Status { + DONE, + NEW; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/EnumOrderRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/EnumOrderRef.cd new file mode 100644 index 00000000..c69676cf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/EnumOrderRef.cd @@ -0,0 +1,6 @@ +classdiagram EnumOrderRef { + enum Status { + NEW, + DONE; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/FieldConflictConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/FieldConflictConc.cd new file mode 100644 index 00000000..064ee3fa --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/FieldConflictConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; +classdiagram FieldConflictConc { + class Base { + int id; + } + + <> class Employee extends Base { + String id; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/FieldConflictRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/FieldConflictRef.cd new file mode 100644 index 00000000..78831b0c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/FieldConflictRef.cd @@ -0,0 +1,3 @@ +classdiagram FieldConflictRef { + class Person; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachAttributeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachAttributeConc.cd new file mode 100644 index 00000000..38aa59b2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachAttributeConc.cd @@ -0,0 +1,7 @@ +import java.lang.String; +classdiagram ForEachAttributeConc { + <> class Employee { + <> String firstName; + <> int age; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachAttributeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachAttributeRef.cd new file mode 100644 index 00000000..98fd5e5a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachAttributeRef.cd @@ -0,0 +1,9 @@ +classdiagram ForEachAttributeRef { + class DataClass { + any attribute; + } + + class Builder { + <> any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodConc.cd new file mode 100644 index 00000000..e503f05a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodConc.cd @@ -0,0 +1,9 @@ +import java.lang.String; +classdiagram ForEachMethodConc { + <> class Employee { + <> String firstName; + <> int age; + String getFirstName(); + int getAge(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodExactConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodExactConc.cd new file mode 100644 index 00000000..31331a5b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodExactConc.cd @@ -0,0 +1,10 @@ +import java.lang.String; +classdiagram ForEachMethodExactConc { + <> class Employee { + <> String copyEmployee(); + } + + <> class Department { + <> int copyDepartment(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodExactRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodExactRef.cd new file mode 100644 index 00000000..180f2eb4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodExactRef.cd @@ -0,0 +1,9 @@ +classdiagram ForEachMethodExactRef { + class DataClass { + any copy(); + } + + class Builder { + <> any copy(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodRef.cd new file mode 100644 index 00000000..f68af9f5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodRef.cd @@ -0,0 +1,6 @@ +classdiagram ForEachMethodRef { + class DataClass { + any attribute; + <> any getAttribute(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodSubstringConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodSubstringConc.cd new file mode 100644 index 00000000..699c6249 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodSubstringConc.cd @@ -0,0 +1,9 @@ +import java.lang.String; +classdiagram ForEachMethodSubstringConc { + <> class Employee { + <> String firstName; + <> int age; + String getFirstNameable(); + int getAgeable(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodSubstringRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodSubstringRef.cd new file mode 100644 index 00000000..c2d0c4e0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMethodSubstringRef.cd @@ -0,0 +1,6 @@ +classdiagram ForEachMethodSubstringRef { + class DataClass { + any attribute; + <> any getAttributable(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMissingConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMissingConc.cd new file mode 100644 index 00000000..443867c0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMissingConc.cd @@ -0,0 +1,6 @@ +import java.lang.String; +classdiagram ForEachMissingConc { + <> class Employee { + <> String firstName; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMissingRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMissingRef.cd new file mode 100644 index 00000000..ea85545b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachMissingRef.cd @@ -0,0 +1,9 @@ +classdiagram ForEachMissingRef { + class DataClass { + any attribute; + } + + class Builder { + <> any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachTypeConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachTypeConc.cd new file mode 100644 index 00000000..d95e9cea --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachTypeConc.cd @@ -0,0 +1,4 @@ +classdiagram ForEachTypeConc { + <> class Employee; + <> class Department; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachTypeRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachTypeRef.cd new file mode 100644 index 00000000..8371334d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ForEachTypeRef.cd @@ -0,0 +1,4 @@ +classdiagram ForEachTypeRef { + class DataClass; + <> class DataClassBuilder; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/MethodConflictConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/MethodConflictConc.cd new file mode 100644 index 00000000..5121a41a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/MethodConflictConc.cd @@ -0,0 +1,7 @@ +import java.lang.String; +classdiagram MethodConflictConc { + <> class Service { + int value(); + String value(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/MethodConflictRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/MethodConflictRef.cd new file mode 100644 index 00000000..2fc0e3ad --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/MethodConflictRef.cd @@ -0,0 +1,3 @@ +classdiagram MethodConflictRef { + class Service; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/TypeKindConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/TypeKindConc.cd new file mode 100644 index 00000000..9efc4226 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/TypeKindConc.cd @@ -0,0 +1,3 @@ +classdiagram TypeKindConc { + <> interface Service; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/TypeKindRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/TypeKindRef.cd new file mode 100644 index 00000000..3cbce00a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/TypeKindRef.cd @@ -0,0 +1,3 @@ +classdiagram TypeKindRef { + class Service; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/UnknownExplicitConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/UnknownExplicitConc.cd new file mode 100644 index 00000000..68f1618b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/UnknownExplicitConc.cd @@ -0,0 +1,6 @@ +classdiagram UnknownExplicitConc { + <> class ConcreteType { + <> int value; + <> void run(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/UnknownExplicitRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/UnknownExplicitRef.cd new file mode 100644 index 00000000..cd09638f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/UnknownExplicitRef.cd @@ -0,0 +1,6 @@ +classdiagram UnknownExplicitRef { + class Known { + int value; + void run(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ValidManualConc.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ValidManualConc.cd new file mode 100644 index 00000000..ee79b820 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ValidManualConc.cd @@ -0,0 +1,6 @@ +classdiagram ValidManualConc { + <> class BankAccount { + <> int amount; + <> int readAmount(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ValidManualRef.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ValidManualRef.cd new file mode 100644 index 00000000..054c09fd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/ValidManualRef.cd @@ -0,0 +1,6 @@ +classdiagram ValidManualRef { + class Account { + int balance; + int getBalance(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/adapter/Account.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/adapter/Account.java new file mode 100644 index 00000000..d045a9f7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/adapter/Account.java @@ -0,0 +1,12 @@ +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(template = "Account", value = {"Account"}) +class Account { + @Adapt(template = "balance", value = {"balance"}) + private int balance; + + @Adapt(template = "getBalance", value = {"getBalance"}) + int getBalance() { + return balance; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/concrete/BankAccount.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/concrete/BankAccount.java new file mode 100644 index 00000000..8b8ddab0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/conflicts/concrete/BankAccount.java @@ -0,0 +1,3 @@ +class BankAccount { + private int amount; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Concrete.cd index 5133fbcf..31a59f94 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Concrete.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Concrete.cd @@ -1,5 +1,5 @@ -package de.monticore.codeAdaption.evaluation; import java.lang.String; +import java.util.Date; classdiagram Concrete { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Reference.cd index 22969782..cda8aaca 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Reference.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/Reference.cd @@ -1,5 +1,5 @@ -package de.monticore.codeAdaption.evaluation; import java.lang.String; +import java.util.Date; classdiagram Reference { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/Concrete.cd new file mode 100644 index 00000000..0d538109 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/Concrete.cd @@ -0,0 +1,6 @@ +classdiagram Concrete { + <> class Sprint; + <> class Ticket; + + association [1] Sprint (sprint) <-> (tickets) Ticket [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/Reference.cd new file mode 100644 index 00000000..76f309b0 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/Reference.cd @@ -0,0 +1,6 @@ +classdiagram Reference { + class Project; + class Task; + + association [1] Project (project) <-> (tasks) Task [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/adapter/Project.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/adapter/Project.java new file mode 100644 index 00000000..9667c826 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/adapter/Project.java @@ -0,0 +1,12 @@ +package Concrete; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Project"}, template = "${}") +public class Project extends ProjectTOP { + + @Adapt(ref = {"Task"}, template = "replace${}") + public void replaceTask(Task replacement) { + this.tasks = replacement; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/adapter/Task.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/adapter/Task.java new file mode 100644 index 00000000..2cc0fcf9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_bidirectional_role_write/adapter/Task.java @@ -0,0 +1,12 @@ +package Concrete; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Task"}, template = "${}") +public class Task extends TaskTOP { + + @Adapt(ref = {"Project"}, template = "moveTo${}") + public void moveToProject(Project replacement) { + this.project = replacement; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/Concrete.cd new file mode 100644 index 00000000..0052bc09 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/Concrete.cd @@ -0,0 +1,8 @@ +classdiagram Concrete { + <> class Teacher; + <> class MathDept; + <> class ScienceDept; + + association Teacher -> (mathDepts) MathDept [*]; + association Teacher -> (scienceDepts) ScienceDept [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/Reference.cd new file mode 100644 index 00000000..4e923a9a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/Reference.cd @@ -0,0 +1,6 @@ +classdiagram Reference { + class Teacher; + class Department; + + association Teacher -> (departments) Department [*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/adapter/Teacher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/adapter/Teacher.java new file mode 100644 index 00000000..2a1e8d8d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/association_role_usage_mi/adapter/Teacher.java @@ -0,0 +1,15 @@ +package Concrete; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Teacher"}, template = "${}") +public class Teacher extends TeacherTOP { + + @Adapt(ref = {"Department"}, template = "first${}") + public Department firstDepartment(Department fallback) { + for (Department department : this.departments) { + return department; + } + return fallback; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Concrete.cd index 261cda75..b079d688 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Concrete.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Concrete.cd @@ -1,5 +1,5 @@ -package de.monticore.codeAdaption.evaluation.confCheckingCD; import java.lang.String; +import java.util.Date; classdiagram Concrete { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Reference.cd index 7b31ec6a..cda8aaca 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Reference.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/confCheckingCD/Reference.cd @@ -1,5 +1,5 @@ -package de.monticore.codeAdaption.evaluation.confCheckingCD; import java.lang.String; +import java.util.Date; classdiagram Reference { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/Adapter.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/Adapter.cd new file mode 100644 index 00000000..df9d7565 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/Adapter.cd @@ -0,0 +1,10 @@ +classdiagram Adapter { + + interface Target { void operation(); } + + class Adapter implements Target ; + + class Adaptee; + + association Adapter -> Adaptee [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/Composition.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/Composition.cd new file mode 100644 index 00000000..eff0f052 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/Composition.cd @@ -0,0 +1,10 @@ +classdiagram Composition { + + interface Component { void execute(); } + + class Leaf implements Component ; + + class Composite implements Component ; + + association Composite -> Component [1..*]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/DesignPatterns.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/DesignPatterns.cd new file mode 100644 index 00000000..a2a6825d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/DesignPatterns.cd @@ -0,0 +1,35 @@ +classdiagram DesignPatterns { + + <> + interface Graphic { + <> void render(); + } + + <> + class Circle implements Graphic { + public void render(); + } + + <> + class Rectangle implements Graphic { + public void render(); + } + + <> + class Picture implements Graphic { + public void render(); + } + + association Picture -> Graphic [1..*]; + + <> interface FlexImage { + <> void flex(); + } + + <> + class NPGAdapter implements FlexImage; + + <> class NPGImage; + + association NPGAdapter -> NPGImage [1]; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/adapter/Adaptee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/adapter/Adaptee.java new file mode 100644 index 00000000..6db0a593 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/adapter/Adaptee.java @@ -0,0 +1,3 @@ +package DesignPatterns; + +public class Adaptee {} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/adapter/Adapter.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/adapter/Adapter.java new file mode 100644 index 00000000..9d7db36f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/adapter/Adapter.java @@ -0,0 +1,11 @@ +package DesignPatterns; + +import de.monticore.codeAdaption.utils.Adapt; + +public class Adapter implements Target { + + @Adapt(ref = "Target.operation()", template = "${}") + public void operation() { + // do nothing + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/composition/Composite.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/composition/Composite.java new file mode 100644 index 00000000..03100ab9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/composition/Composite.java @@ -0,0 +1,31 @@ +package DesignPatterns; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.ArrayList; +import java.util.List; + +public class Composite implements Component { + private final List componentList = new ArrayList<>(); + + @Adapt(ref = "Component.execute()", template = "${}") + public void execute() { + for (Component component : componentList) { + component.execute(); + } + } + + @Adapt(ref = "Component", template = "add${}") + public void addComponent(Component component) { + componentList.add(component); + } + + @Adapt(ref = "Component", template = "remove${}") + public void removeComponent(Component component) { + componentList.remove(component); + } + + @Adapt(ref = "Component", template = "get${}") + public List getComponent() { + return componentList; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/composition/Leaf.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/composition/Leaf.java new file mode 100644 index 00000000..26ba2a63 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/design_patterns/hwc/composition/Leaf.java @@ -0,0 +1,11 @@ +package DesignPatterns; + +import de.monticore.codeAdaption.utils.Adapt; + +public class Leaf implements Component { + + @Adapt(ref = "Component.execute()", template = "${}") + public void execute() { + // do nothing + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/Concrete.cd new file mode 100644 index 00000000..7763f0ca --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/Concrete.cd @@ -0,0 +1,10 @@ +import java.lang.String; + +classdiagram Concrete { + <> class MyClass { + <> String renamedField; + <> void renamedMethod(); + <> String pattern1Shared; + <> String pattern2Shared; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/Reference.cd new file mode 100644 index 00000000..31e9af06 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/Reference.cd @@ -0,0 +1,9 @@ +import java.lang.String; + +classdiagram Reference { + class MyClass { + String oldField; + void oldMethod(); + String sharedField; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/reference/MyClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/reference/MyClass.java new file mode 100644 index 00000000..4d08f1e6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/single_file_multi_pattern/reference/MyClass.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.evaluation.single_file_multi_pattern; + +public class MyClass { + private String oldField; + private String sharedField; + public void oldMethod() { } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Concrete.cd index 065c8f96..1450357e 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Concrete.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Concrete.cd @@ -1,5 +1,5 @@ -package de.monticore.codeAdaption.evaluation.testcase_1; import java.lang.String; +import java.util.Date; classdiagram Concrete { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Reference.cd index dfd8aef5..632a9473 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Reference.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_1/Reference.cd @@ -1,5 +1,5 @@ -package de.monticore.codeAdaption.evaluation.testcase_1; import java.lang.String; +import java.util.Date; classdiagram Reference { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/Concrete.cd new file mode 100644 index 00000000..298191eb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/Concrete.cd @@ -0,0 +1,22 @@ +import java.lang.String; + +classdiagram Concrete { + + <> class DatabaseConnection { + <> String connectionString; + <> boolean isConnected; + void connect(); + void disconnect(); + } + + <> class Logger { + <> String logFile; + void log(String message); + } + + class Application { + DatabaseConnection db; + Logger logger; + void initialize(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/Reference.cd new file mode 100644 index 00000000..db17ee21 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/Reference.cd @@ -0,0 +1,8 @@ + +classdiagram Reference { + class Singleton { + static Singleton instance; + static Singleton getInstance(); + any attribute; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/adapter/Singleton.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/adapter/Singleton.java new file mode 100644 index 00000000..bc458f17 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/adapter/Singleton.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.evaluation.testcase_10_singleton_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Singleton"}, template = "${}") +public class Singleton { + private static Singleton instance; + + @Adapt(ref = {"Singleton.instance"}, template = "get${cap_first}") + public static Singleton getInstance() { + if (instance == null) { + instance = new Singleton(); + } + return instance; + } + + private Singleton() { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/Application.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/Application.java new file mode 100644 index 00000000..a71bb9e2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/Application.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.evaluation.testcase_10_singleton_pattern; + +/** + * Application domain class + */ +public class Application { + private DatabaseConnection db; + private Logger logger; + + public Application() { + this.db = new DatabaseConnection(); + this.logger = new Logger(); + } + + public Application(DatabaseConnection db, Logger logger) { + this.db = db; + this.logger = logger; + } + + public void initialize() { + db.connect(); + logger.log("Application initialized"); + } + + public DatabaseConnection getDb() { return db; } + public Logger getLogger() { return logger; } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/DatabaseConnection.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/DatabaseConnection.java new file mode 100644 index 00000000..42c03a20 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/DatabaseConnection.java @@ -0,0 +1,30 @@ +package de.monticore.codeAdaption.evaluation.testcase_10_singleton_pattern; + +/** + * DatabaseConnection domain class + */ +public class DatabaseConnection { + private String connectionString; + private boolean isConnected; + + public DatabaseConnection() { + this.connectionString = "jdbc:default"; + this.isConnected = false; + } + + public DatabaseConnection(String connectionString) { + this.connectionString = connectionString; + this.isConnected = false; + } + + public String getConnectionString() { return connectionString; } + public boolean isConnected() { return isConnected; } + + public void connect() { + this.isConnected = true; + } + + public void disconnect() { + this.isConnected = false; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/Logger.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/Logger.java new file mode 100644 index 00000000..4bd92142 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_10_singleton_pattern/concrete/Logger.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.evaluation.testcase_10_singleton_pattern; + +/** + * Logger domain class + */ +public class Logger { + private String logFile; + + public Logger() { + this.logFile = "application.log"; + } + + public Logger(String logFile) { + this.logFile = logFile; + } + + public String getLogFile() { return logFile; } + + public void log(String message) { + System.out.println("LOG: " + message); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/Concrete.cd new file mode 100644 index 00000000..2e4e8f07 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/Concrete.cd @@ -0,0 +1,33 @@ +import java.lang.String; + +classdiagram Concrete { + + <> abstract class DataProcessor { + String source; + String destination; + <> abstract void readData(); + <> abstract void transformData(); + <> void validateData(); + } + + <> class CSVProcessor extends DataProcessor { + String delimiter; + <> void readData(); + <> void transformData(); + <> void validateData(); + } + + <> class JSONProcessor extends DataProcessor { + boolean prettyPrint; + <> void readData(); + <> void transformData(); + <> void validateData(); + } + + <> class XMLProcessor extends DataProcessor { + String encoding; + <> void readData(); + <> void transformData(); + <> void validateData(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/Reference.cd new file mode 100644 index 00000000..c43a6e3a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/Reference.cd @@ -0,0 +1,15 @@ + +classdiagram Reference { + abstract class AbstractClass { + void templateMethod(); + abstract void primitiveOperation1(); + abstract void primitiveOperation2(); + void hook(); + } + + class ConcreteClass extends AbstractClass { + void primitiveOperation1(); + void primitiveOperation2(); + void hook(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/adapter/AbstractClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/adapter/AbstractClass.java new file mode 100644 index 00000000..f0d9da80 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/adapter/AbstractClass.java @@ -0,0 +1,26 @@ +package de.monticore.codeAdaption.evaluation.testcase_11_template_method_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"AbstractClass"}, template = "${}") +public abstract class AbstractClass { + + @Adapt(ref = {"AbstractClass.templateMethod"}, template = "${}") + public void templateMethod() { + primitiveOperation1(); + primitiveOperation2(); + hook(); + } + + @Adapt(ref = {"AbstractClass.primitiveOperation1"}, template = "${}") + public abstract void primitiveOperation1(); + + @Adapt(ref = {"AbstractClass.primitiveOperation2"}, template = "${}") + public abstract void primitiveOperation2(); + + @Adapt(ref = {"AbstractClass.hook"}, template = "${}") + public void hook() { + // Default implementation + System.out.println("Hook"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/CSVProcessor.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/CSVProcessor.java new file mode 100644 index 00000000..760e0f8d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/CSVProcessor.java @@ -0,0 +1,30 @@ +package de.monticore.codeAdaption.evaluation.testcase_11_template_method_pattern; + +/** + * CSVProcessor concrete implementation of DataProcessor + */ +public class CSVProcessor extends DataProcessor { + private String delimiter; + + public CSVProcessor() { + this.delimiter = ","; + } + + public CSVProcessor(String delimiter) { + this.delimiter = delimiter; + } + + public String getDelimiter() { return delimiter; } + + public void readData() { + System.out.println("Reading CSV data"); + } + + public void transformData() { + System.out.println("Transforming CSV data"); + } + + public void validateData() { + System.out.println("Validating CSV format"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/DataProcessor.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/DataProcessor.java new file mode 100644 index 00000000..8a46d910 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/DataProcessor.java @@ -0,0 +1,29 @@ +package de.monticore.codeAdaption.evaluation.testcase_11_template_method_pattern; + +/** + * DataProcessor abstract class + */ +public abstract class DataProcessor { + private String source; + private String destination; + + public DataProcessor() { + this.source = "default"; + this.destination = "default"; + } + + public DataProcessor(String source, String destination) { + this.source = source; + this.destination = destination; + } + + public String getSource() { return source; } + public String getDestination() { return destination; } + + public abstract void readData(); + public abstract void transformData(); + + public void validateData() { + System.out.println("Validating data..."); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/JSONProcessor.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/JSONProcessor.java new file mode 100644 index 00000000..e28c6044 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/JSONProcessor.java @@ -0,0 +1,30 @@ +package de.monticore.codeAdaption.evaluation.testcase_11_template_method_pattern; + +/** + * JSONProcessor concrete implementation of DataProcessor + */ +public class JSONProcessor extends DataProcessor { + private boolean prettyPrint; + + public JSONProcessor() { + this.prettyPrint = false; + } + + public JSONProcessor(boolean prettyPrint) { + this.prettyPrint = prettyPrint; + } + + public boolean isPrettyPrint() { return prettyPrint; } + + public void readData() { + System.out.println("Reading JSON data"); + } + + public void transformData() { + System.out.println("Transforming JSON data"); + } + + public void validateData() { + System.out.println("Validating JSON schema"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/XMLProcessor.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/XMLProcessor.java new file mode 100644 index 00000000..b458d43c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_11_template_method_pattern/concrete/XMLProcessor.java @@ -0,0 +1,30 @@ +package de.monticore.codeAdaption.evaluation.testcase_11_template_method_pattern; + +/** + * XMLProcessor concrete implementation of DataProcessor + */ +public class XMLProcessor extends DataProcessor { + private String encoding; + + public XMLProcessor() { + this.encoding = "UTF-8"; + } + + public XMLProcessor(String encoding) { + this.encoding = encoding; + } + + public String getEncoding() { return encoding; } + + public void readData() { + System.out.println("Reading XML data"); + } + + public void transformData() { + System.out.println("Transforming XML data"); + } + + public void validateData() { + System.out.println("Validating XML DTD"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/Concrete.cd new file mode 100644 index 00000000..e29ccfe2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/Concrete.cd @@ -0,0 +1,35 @@ +import java.lang.String; + +classdiagram Concrete { + + <> class ConfigurationManager { + <> String environment; + <> ConfigStorageStrategy storageStrategy; + <> void setStorageStrategy(ConfigStorageStrategy strategy); + <> void saveConfig(String key, String value); + <> String loadConfig(String key); + } + + <> interface ConfigStorageStrategy { + <> void save(String key, String value); + <> String load(String key); + } + + <> class FileStorageStrategy implements ConfigStorageStrategy { + String basePath; + <> void save(String key, String value); + <> String load(String key); + } + + <> class DatabaseStorageStrategy implements ConfigStorageStrategy { + String connectionString; + <> void save(String key, String value); + <> String load(String key); + } + + <> class CloudStorageStrategy implements ConfigStorageStrategy { + String bucketName; + <> void save(String key, String value); + <> String load(String key); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/Reference.cd new file mode 100644 index 00000000..35840b03 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/Reference.cd @@ -0,0 +1,22 @@ + +classdiagram Reference { + class Singleton { + static Singleton instance; + static Singleton getInstance(); + any attribute; + } + + class Context { + Strategy strategy; + void setStrategy(Strategy strategy); + void execute(); + } + + interface Strategy { + void execute(); + } + + class ConcreteStrategy implements Strategy { + void execute(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Context.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Context.java new file mode 100644 index 00000000..77fda621 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Context.java @@ -0,0 +1,18 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Context"}, template = "${}") +public class Context { + private Strategy strategy; + + @Adapt(ref = {"Context.strategy"}, template = "set${cap_first}") + public void setStrategy(Strategy strategy) { + this.strategy = strategy; + } + + @Adapt(ref = {"Context.execute"}, template = "execute") + public void execute() { + strategy.execute(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Singleton.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Singleton.java new file mode 100644 index 00000000..29bd4d63 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Singleton.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Singleton"}, template = "${}") +public class Singleton { + private static Singleton instance; + + @Adapt(ref = {"Singleton.instance"}, template = "get${cap_first}") + public static Singleton getInstance() { + if (instance == null) { + instance = new Singleton(); + } + return instance; + } + + private Singleton() { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Strategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Strategy.java new file mode 100644 index 00000000..a05c3599 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/adapter/Strategy.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Strategy"}, template = "${}") +public interface Strategy { + + @Adapt(ref = {"Strategy.execute"}, template = "execute") + void execute(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/CloudStorageStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/CloudStorageStrategy.java new file mode 100644 index 00000000..3837c97a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/CloudStorageStrategy.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton; + +/** + * CloudStorageStrategy concrete implementation + */ +public class CloudStorageStrategy implements ConfigStorageStrategy { + private String bucketName; + + public CloudStorageStrategy() { + this.bucketName = "default-bucket"; + } + + public CloudStorageStrategy(String bucketName) { + this.bucketName = bucketName; + } + + public String getBucketName() { return bucketName; } + + public void save(String key, String value) { + System.out.println("Saving to cloud: " + key + " = " + value); + } + + public String load(String key) { + System.out.println("Loading from cloud: " + key); + return "cloud_value"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/ConfigStorageStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/ConfigStorageStrategy.java new file mode 100644 index 00000000..fd10b20e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/ConfigStorageStrategy.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton; + +/** + * ConfigStorageStrategy interface + */ +public interface ConfigStorageStrategy { + void save(String key, String value); + String load(String key); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/ConfigurationManager.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/ConfigurationManager.java new file mode 100644 index 00000000..358d710f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/ConfigurationManager.java @@ -0,0 +1,34 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton; + +/** + * ConfigurationManager domain class + */ +public class ConfigurationManager { + private String environment; + private ConfigStorageStrategy storageStrategy; + + public ConfigurationManager() { + this.environment = "production"; + this.storageStrategy = new FileStorageStrategy(); + } + + public ConfigurationManager(String environment, ConfigStorageStrategy storageStrategy) { + this.environment = environment; + this.storageStrategy = storageStrategy; + } + + public String getEnvironment() { return environment; } + public ConfigStorageStrategy getStorageStrategy() { return storageStrategy; } + + public void setStorageStrategy(ConfigStorageStrategy strategy) { + this.storageStrategy = strategy; + } + + public void saveConfig(String key, String value) { + storageStrategy.save(key, value); + } + + public String loadConfig(String key) { + return storageStrategy.load(key); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/DatabaseStorageStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/DatabaseStorageStrategy.java new file mode 100644 index 00000000..d6fc0e4a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/DatabaseStorageStrategy.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton; + +/** + * DatabaseStorageStrategy concrete implementation + */ +public class DatabaseStorageStrategy implements ConfigStorageStrategy { + private String connectionString; + + public DatabaseStorageStrategy() { + this.connectionString = "jdbc:default"; + } + + public DatabaseStorageStrategy(String connectionString) { + this.connectionString = connectionString; + } + + public String getConnectionString() { return connectionString; } + + public void save(String key, String value) { + System.out.println("Saving to database: " + key + " = " + value); + } + + public String load(String key) { + System.out.println("Loading from database: " + key); + return "db_value"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/FileStorageStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/FileStorageStrategy.java new file mode 100644 index 00000000..82eb4ba2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_12_strategy_singleton_pattern/concrete/FileStorageStrategy.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.evaluation.testcase_12_strategy_singleton; + +/** + * FileStorageStrategy concrete implementation + */ +public class FileStorageStrategy implements ConfigStorageStrategy { + private String basePath; + + public FileStorageStrategy() { + this.basePath = "/config"; + } + + public FileStorageStrategy(String basePath) { + this.basePath = basePath; + } + + public String getBasePath() { return basePath; } + + public void save(String key, String value) { + System.out.println("Saving to file: " + key + " = " + value); + } + + public String load(String key) { + System.out.println("Loading from file: " + key); + return "file_value"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Concrete.cd new file mode 100644 index 00000000..b19c3364 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Concrete.cd @@ -0,0 +1,40 @@ +import java.lang.String; + +classdiagram Concrete { + + <> abstract class DataPipeline { + String pipelineName; + String source; + <> abstract void extractData(); + <> abstract void processData(); + abstract void loadData(); + } + + <> class SalesDataPipeline extends DataPipeline { + String crmSystem; + <> void extractData(); + <> void processData(); + void loadData(); + } + + <> class InventoryDataPipeline extends DataPipeline { + String erpSystem; + <> void extractData(); + <> void processData(); + void loadData(); + } + + <> interface PipelineObserver { + <> void onPipelineStage(String stage); + } + + <> class LoggingObserver implements PipelineObserver { + String logLevel; + <> void onPipelineStage(String stage); + } + + <> class MetricsObserver implements PipelineObserver { + String metricsEndpoint; + <> void onPipelineStage(String stage); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Reference.cd new file mode 100644 index 00000000..c416ed1c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/Reference.cd @@ -0,0 +1,28 @@ +import java.util.List; + + +classdiagram Reference { + abstract class AbstractClass { + void templateMethod(); + abstract void primitiveOperation1(); + abstract void primitiveOperation2(); + void hook(); + } + + class Subject { + List observers; + boolean subscribe(Observer o); + boolean unsubscribe(Observer o); + void notifyAll(); + } + + interface Observer { + void update(); + } + + class ConcreteClass extends AbstractClass { + void primitiveOperation1(); + void primitiveOperation2(); + void hook(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/AbstractClass.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/AbstractClass.java new file mode 100644 index 00000000..011a7a2a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/AbstractClass.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"AbstractClass"}, template = "${}") +public abstract class AbstractClass { + + @Adapt(ref = {"AbstractClass.templateMethod"}, template = "templateMethod") + public void templateMethod() { + primitiveOperation1(); + primitiveOperation2(); + hook(); + } + + @Adapt(ref = {"AbstractClass.primitiveOperation1"}, template = "primitiveOperation1") + public abstract void primitiveOperation1(); + + @Adapt(ref = {"AbstractClass.primitiveOperation2"}, template = "primitiveOperation2") + public abstract void primitiveOperation2(); + + @Adapt(ref = {"AbstractClass.hook"}, template = "hook") + public void hook() { + // Default implementation + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/Observer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/Observer.java new file mode 100644 index 00000000..624fe8f1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/Observer.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Observer"}, template = "${}") +public interface Observer { + + @Adapt(ref = {"Observer.update"}, template = "update") + void update(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/Subject.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/Subject.java new file mode 100644 index 00000000..da25c78f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/adapter/Subject.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer_pattern; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.List; +import java.util.ArrayList; + +@Adapt(ref = {"Subject"}, template = "${}") +public class Subject { + private List observers = new ArrayList<>(); + + @Adapt(ref = {"Subject.subscribe"}, template = "subscribe") + public boolean subscribe(Observer o) { + return observers.add(o); + } + + @Adapt(ref = {"Subject.unsubscribe"}, template = "unsubscribe") + public boolean unsubscribe(Observer o) { + return observers.remove(o); + } + + @Adapt(ref = {"Subject.notifyAll"}, template = "notifyAll") + public void notifyAll() { + for (Observer observer : observers) { + observer.update(); + } + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/DataPipeline.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/DataPipeline.java new file mode 100644 index 00000000..cda80417 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/DataPipeline.java @@ -0,0 +1,26 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer; + +/** + * DataPipeline abstract class + */ +public abstract class DataPipeline { + private String pipelineName; + private String source; + + public DataPipeline() { + this.pipelineName = "default"; + this.source = "default"; + } + + public DataPipeline(String pipelineName, String source) { + this.pipelineName = pipelineName; + this.source = source; + } + + public String getPipelineName() { return pipelineName; } + public String getSource() { return source; } + + public abstract void extractData(); + public abstract void processData(); + public abstract void loadData(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/InventoryDataPipeline.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/InventoryDataPipeline.java new file mode 100644 index 00000000..1286aed8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/InventoryDataPipeline.java @@ -0,0 +1,30 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer; + +/** + * InventoryDataPipeline concrete implementation of DataPipeline + */ +public class InventoryDataPipeline extends DataPipeline { + private String erpSystem; + + public InventoryDataPipeline() { + this.erpSystem = "SAP"; + } + + public InventoryDataPipeline(String erpSystem) { + this.erpSystem = erpSystem; + } + + public String getErpSystem() { return erpSystem; } + + public void extractData() { + System.out.println("Extracting inventory data from ERP"); + } + + public void processData() { + System.out.println("Processing inventory data with stock rules"); + } + + public void loadData() { + System.out.println("Loading processed inventory data to warehouse"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/LoggingObserver.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/LoggingObserver.java new file mode 100644 index 00000000..73a361c3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/LoggingObserver.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer; + +/** + * LoggingObserver concrete implementation of PipelineObserver + */ +public class LoggingObserver implements PipelineObserver { + private String logLevel; + + public LoggingObserver() { + this.logLevel = "INFO"; + } + + public LoggingObserver(String logLevel) { + this.logLevel = logLevel; + } + + public String getLogLevel() { return logLevel; } + + public void onPipelineStage(String stage) { + System.out.println("[LOG] Pipeline stage: " + stage); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/MetricsObserver.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/MetricsObserver.java new file mode 100644 index 00000000..da9ca392 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/MetricsObserver.java @@ -0,0 +1,22 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer; + +/** + * MetricsObserver concrete implementation of PipelineObserver + */ +public class MetricsObserver implements PipelineObserver { + private String metricsEndpoint; + + public MetricsObserver() { + this.metricsEndpoint = "/metrics"; + } + + public MetricsObserver(String metricsEndpoint) { + this.metricsEndpoint = metricsEndpoint; + } + + public String getMetricsEndpoint() { return metricsEndpoint; } + + public void onPipelineStage(String stage) { + System.out.println("[METRICS] Recording metrics for stage: " + stage); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/PipelineObserver.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/PipelineObserver.java new file mode 100644 index 00000000..52e54a9c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/PipelineObserver.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer; + +/** + * PipelineObserver interface + */ +public interface PipelineObserver { + void onPipelineStage(String stage); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/SalesDataPipeline.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/SalesDataPipeline.java new file mode 100644 index 00000000..dd844b56 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_13_template_observer_pattern/concrete/SalesDataPipeline.java @@ -0,0 +1,30 @@ +package de.monticore.codeAdaption.evaluation.testcase_13_template_observer; + +/** + * SalesDataPipeline concrete implementation of DataPipeline + */ +public class SalesDataPipeline extends DataPipeline { + private String crmSystem; + + public SalesDataPipeline() { + this.crmSystem = "Salesforce"; + } + + public SalesDataPipeline(String crmSystem) { + this.crmSystem = crmSystem; + } + + public String getCrmSystem() { return crmSystem; } + + public void extractData() { + System.out.println("Extracting sales data from CRM"); + } + + public void processData() { + System.out.println("Processing sales data with business rules"); + } + + public void loadData() { + System.out.println("Loading processed sales data to warehouse"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/Concrete.cd new file mode 100644 index 00000000..b3fe6faa --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/Concrete.cd @@ -0,0 +1,22 @@ +import java.lang.String; + +classdiagram Concrete { + <> interface ShippingPort { + <> boolean ship(String label, boolean insured); + } + + <> class LegacyCarrier { + String endpoint; + <> boolean dispatch(String label, boolean insured); + } + + <> class CarrierAdapter implements ShippingPort { + <> LegacyCarrier carrier; + CarrierAdapter(LegacyCarrier carrier); + <> boolean ship(String label, boolean insured); + } + + <> class ShippingFactory { + <> ShippingPort createCarrier(LegacyCarrier carrier); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/Reference.cd new file mode 100644 index 00000000..ecf640d4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/Reference.cd @@ -0,0 +1,21 @@ +import java.lang.String; + +classdiagram Reference { + interface Target { + boolean request(String payload); + } + + class Adaptee { + boolean specificRequest(String payload); + } + + class Adapter implements Target { + Adaptee adaptee; + Adapter(Adaptee adaptee); + boolean request(String payload); + } + + abstract class Creator { + abstract Target create(Adaptee adaptee); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Adaptee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Adaptee.java new file mode 100644 index 00000000..e52d81bf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Adaptee.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Adaptee", template = "${}") +public class Adaptee { + @Adapt(ref = "Adaptee.specificRequest", template = "${}") + public boolean specificRequest(String payload) { + return false; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Adapter.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Adapter.java new file mode 100644 index 00000000..145bf988 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Adapter.java @@ -0,0 +1,18 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Adapter", template = "${}") +public class Adapter implements Target { + @Adapt(ref = "Adapter.adaptee", template = "${}") + private Adaptee adaptee; + + public Adapter(Adaptee adaptee) { + this.adaptee = adaptee; + } + + @Adapt(ref = "Target.request", template = "${}") + public boolean request(String payload) { + return adaptee.specificRequest(payload); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Creator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Creator.java new file mode 100644 index 00000000..b33a2625 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Creator.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Creator", template = "${}") +public abstract class Creator { + @Adapt(ref = "Creator.create", template = "${}") + public Target create(Adaptee adaptee) { + return new Adapter(adaptee); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Target.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Target.java new file mode 100644 index 00000000..cc9eb0a8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/adapter/Target.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Target", template = "${}") +public interface Target { + @Adapt(ref = "Target.request", template = "${}") + boolean request(String payload); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/CarrierAdapter.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/CarrierAdapter.java new file mode 100644 index 00000000..c2ea3a5b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/CarrierAdapter.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.concrete; + +public class CarrierAdapter implements ShippingPort { + private final LegacyCarrier carrier; + + public CarrierAdapter(LegacyCarrier carrier) { + this.carrier = carrier; + } + + @Override + public boolean ship(String label, boolean insured) { + return carrier.dispatch(label, insured); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/LegacyCarrier.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/LegacyCarrier.java new file mode 100644 index 00000000..bbdb2cf9 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/LegacyCarrier.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.concrete; + +public class LegacyCarrier { + String endpoint; + + public boolean dispatch(String label, boolean insured) { + return label != null && !label.isBlank(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/ShippingFactory.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/ShippingFactory.java new file mode 100644 index 00000000..40791398 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/ShippingFactory.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.concrete; + +public class ShippingFactory { + public ShippingPort createCarrier(LegacyCarrier carrier) { + return new CarrierAdapter(carrier); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/ShippingPort.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/ShippingPort.java new file mode 100644 index 00000000..91f2c758 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_14_adapter_factory_combined/concrete/ShippingPort.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.evaluation.testcase_14_adapter_factory_combined.concrete; + +public interface ShippingPort { + boolean ship(String label, boolean insured); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/Concrete.cd new file mode 100644 index 00000000..96ea5a44 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/Concrete.cd @@ -0,0 +1,43 @@ +import java.lang.String; +import java.util.List; + +classdiagram Concrete { + <> interface Renderable { + <> String render(int depth); + } + + <> class TextNode implements Renderable { + String text; + <> String render(int depth); + } + + <> class ImageNode implements Renderable { + String source; + <> String render(int depth); + } + + <> class GroupNode implements Renderable { + List nodes; + <> void attach(Renderable node); + <> void detach(Renderable node); + <> String render(int depth); + } + + <> abstract class RenderDecorator implements Renderable { + Renderable wrapped; + RenderDecorator(Renderable wrapped); + <> String render(int depth); + } + + <> class BorderDecorator extends RenderDecorator { + String borderStyle; + BorderDecorator(Renderable wrapped); + <> String render(int depth); + } + + <> class ShadowDecorator extends RenderDecorator { + int radius; + ShadowDecorator(Renderable wrapped); + <> String render(int depth); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/Reference.cd new file mode 100644 index 00000000..2116192e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/Reference.cd @@ -0,0 +1,26 @@ +import java.lang.String; +import java.util.List; + + +classdiagram Reference { + interface Component { + String operation(); + } + + class Leaf implements Component { + String operation(); + } + + class Composite implements Component { + List children; + void add(Component component); + void remove(Component component); + String operation(); + } + + abstract class Decorator implements Component { + Component wrapped; + Decorator(Component wrapped); + String operation(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Component.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Component.java new file mode 100644 index 00000000..5c0de4de --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Component.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Component", template = "${}") +public interface Component { + @Adapt(ref = "Component.operation", template = "${}") + String operation(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Composite.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Composite.java new file mode 100644 index 00000000..81efad41 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Composite.java @@ -0,0 +1,31 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.ArrayList; +import java.util.List; + +@Adapt(ref = "Composite", template = "${}") +public class Composite implements Component { + @Adapt(ref = "Composite.children", template = "${}") + private List children = new ArrayList<>(); + + @Adapt(ref = "Composite.add", template = "${}") + public void add(Component component) { + children.add(component); + } + + @Adapt(ref = "Composite.remove", template = "${}") + public void remove(Component component) { + children.remove(component); + } + + @Override + @Adapt(ref = "Component.operation", template = "${}") + public String operation() { + StringBuilder builder = new StringBuilder(); + for (Component child : children) { + builder.append(child.operation()); + } + return builder.toString(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Decorator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Decorator.java new file mode 100644 index 00000000..cd28e239 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Decorator.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Decorator", template = "${}") +public abstract class Decorator implements Component { + @Adapt(ref = "Decorator.wrapped", template = "${}") + protected Component wrapped; + + public Decorator(Component wrapped) { + this.wrapped = wrapped; + } + + @Override + @Adapt(ref = "Component.operation", template = "${}") + public String operation() { + return wrapped.operation(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Leaf.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Leaf.java new file mode 100644 index 00000000..089d5be2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/adapter/Leaf.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Leaf", template = "${}") +public class Leaf implements Component { + @Override + @Adapt(ref = "Component.operation", template = "${}") + public String operation() { + return ""; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/BorderDecorator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/BorderDecorator.java new file mode 100644 index 00000000..639ea08d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/BorderDecorator.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.concrete; + +public class BorderDecorator extends RenderDecorator { + String borderStyle; + + public BorderDecorator(Renderable wrapped) { + super(wrapped); + } + + @Override + public String render(int depth) { + return "[" + wrapped.render(depth) + "]"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/GroupNode.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/GroupNode.java new file mode 100644 index 00000000..6bf7ce10 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/GroupNode.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.concrete; + +import java.util.ArrayList; +import java.util.List; + +public class GroupNode implements Renderable { + private final List nodes = new ArrayList<>(); + + public void attach(Renderable node) { + nodes.add(node); + } + + public void detach(Renderable node) { + nodes.remove(node); + } + + @Override + public String render(int depth) { + StringBuilder result = new StringBuilder(); + for (Renderable node : nodes) { + result.append(node.render(depth + 1)); + } + return result.toString(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/ImageNode.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/ImageNode.java new file mode 100644 index 00000000..661966ff --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/ImageNode.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.concrete; + +public class ImageNode implements Renderable { + String source; + + @Override + public String render(int depth) { + return source == null ? "" : source; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/RenderDecorator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/RenderDecorator.java new file mode 100644 index 00000000..75a9b61e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/RenderDecorator.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.concrete; + +public abstract class RenderDecorator implements Renderable { + protected final Renderable wrapped; + + protected RenderDecorator(Renderable wrapped) { + this.wrapped = wrapped; + } + + @Override + public String render(int depth) { + return wrapped.render(depth); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/Renderable.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/Renderable.java new file mode 100644 index 00000000..9eaa05a6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/Renderable.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.concrete; + +public interface Renderable { + String render(int depth); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/ShadowDecorator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/ShadowDecorator.java new file mode 100644 index 00000000..7d91beef --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/ShadowDecorator.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.concrete; + +public class ShadowDecorator extends RenderDecorator { + int radius; + + public ShadowDecorator(Renderable wrapped) { + super(wrapped); + } + + @Override + public String render(int depth) { + return wrapped.render(depth) + "#shadow"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/TextNode.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/TextNode.java new file mode 100644 index 00000000..df1b0bfe --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_15_composite_decorator_combined/concrete/TextNode.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.evaluation.testcase_15_composite_decorator_combined.concrete; + +public class TextNode implements Renderable { + String text; + + @Override + public String render(int depth) { + return text == null ? "" : text; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/Concrete.cd new file mode 100644 index 00000000..c8afc23a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/Concrete.cd @@ -0,0 +1,32 @@ +import java.lang.String; +import java.util.List; + +classdiagram Concrete { + <> class EventBus { + List listeners; + String lastEvent; + <> void subscribe(WorkflowCommand command); + <> void unsubscribe(WorkflowCommand command); + <> void publish(String event); + } + + <> interface WorkflowCommand { + <> void run(String event, boolean async); + } + + <> class EmailCommand implements WorkflowCommand { + String address; + <> void run(String event, boolean async); + } + + <> class SmsCommand implements WorkflowCommand { + String phoneNumber; + <> void run(String event, boolean async); + } + + <> class CommandQueue { + List backlog; + <> void schedule(WorkflowCommand command); + <> void drain(String event); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/Reference.cd new file mode 100644 index 00000000..2c82af32 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/Reference.cd @@ -0,0 +1,25 @@ +import java.util.List; + + +classdiagram Reference { + class Subject { + List observers; + void attach(Observer observer); + void detach(Observer observer); + void notifyAll(); + } + + interface Observer { + void update(); + } + + interface Command { + void execute(); + } + + class Invoker { + List commands; + void enqueue(Command command); + void runAll(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Command.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Command.java new file mode 100644 index 00000000..da85b472 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Command.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Command", template = "${}") +public interface Command { + @Adapt(ref = "Command.execute", template = "${}") + void execute(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Invoker.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Invoker.java new file mode 100644 index 00000000..3b292c1b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Invoker.java @@ -0,0 +1,23 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.ArrayList; +import java.util.List; + +@Adapt(ref = "Invoker", template = "${}") +public class Invoker { + @Adapt(ref = "Invoker.commands", template = "${}") + private List commands = new ArrayList<>(); + + @Adapt(ref = "Invoker.enqueue", template = "${}") + public void enqueue(Command command) { + commands.add(command); + } + + @Adapt(ref = "Invoker.runAll", template = "${}") + public void runAll() { + for (Command command : commands) { + command.execute(); + } + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Observer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Observer.java new file mode 100644 index 00000000..b156c830 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Observer.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Observer", template = "${}") +public interface Observer { + @Adapt(ref = "Observer.update", template = "${}") + void update(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Subject.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Subject.java new file mode 100644 index 00000000..72177ec3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/adapter/Subject.java @@ -0,0 +1,28 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.adapter; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.ArrayList; +import java.util.List; + +@Adapt(ref = "Subject", template = "${}") +public class Subject { + @Adapt(ref = "Subject.observers", template = "${}") + private List observers = new ArrayList<>(); + + @Adapt(ref = "Subject.attach", template = "${}") + public void attach(Observer observer) { + observers.add(observer); + } + + @Adapt(ref = "Subject.detach", template = "${}") + public void detach(Observer observer) { + observers.remove(observer); + } + + @Adapt(ref = "Subject.notifyAll", template = "${}") + public void notifyAllObservers() { + for (Observer observer : observers) { + observer.update(); + } + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/CommandQueue.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/CommandQueue.java new file mode 100644 index 00000000..dee6d50f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/CommandQueue.java @@ -0,0 +1,18 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.concrete; + +import java.util.ArrayList; +import java.util.List; + +public class CommandQueue { + private final List backlog = new ArrayList<>(); + + public void schedule(WorkflowCommand command) { + backlog.add(command); + } + + public void drain(String event) { + for (WorkflowCommand command : backlog) { + command.run(event, false); + } + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/EmailCommand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/EmailCommand.java new file mode 100644 index 00000000..985c72a7 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/EmailCommand.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.concrete; + +public class EmailCommand implements WorkflowCommand { + String address; + + @Override + public void run(String event, boolean async) { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/EventBus.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/EventBus.java new file mode 100644 index 00000000..41ae7a5b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/EventBus.java @@ -0,0 +1,24 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.concrete; + +import java.util.ArrayList; +import java.util.List; + +public class EventBus { + private final List listeners = new ArrayList<>(); + String lastEvent; + + public void subscribe(WorkflowCommand command) { + listeners.add(command); + } + + public void unsubscribe(WorkflowCommand command) { + listeners.remove(command); + } + + public void publish(String event) { + lastEvent = event; + for (WorkflowCommand listener : listeners) { + listener.run(event, false); + } + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/SmsCommand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/SmsCommand.java new file mode 100644 index 00000000..c56bad3a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/SmsCommand.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.concrete; + +public class SmsCommand implements WorkflowCommand { + String phoneNumber; + + @Override + public void run(String event, boolean async) { + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/WorkflowCommand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/WorkflowCommand.java new file mode 100644 index 00000000..cbbab9d8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_16_observer_command_combined/concrete/WorkflowCommand.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.evaluation.testcase_16_observer_command_combined.concrete; + +public interface WorkflowCommand { + void run(String event, boolean async); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/Concrete.cd new file mode 100644 index 00000000..6583b8bc --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/Concrete.cd @@ -0,0 +1,95 @@ +import java.lang.String; +import java.util.List; + +classdiagram Concrete { + <> interface ShipmentEventListener { + <> void onShipmentEvent(ShipmentRecord shipment); + } + + <> class ShipmentEventPublisher { + <> List listeners; + <> void subscribe(ShipmentEventListener listener); + <> void unsubscribe(ShipmentEventListener listener); + <> void publishShipment(ShipmentRecord shipment); + } + + <> class AuditTrailListener implements ShipmentEventListener { + <> void onShipmentEvent(ShipmentRecord shipment); + } + + <> class CustomerNotificationListener implements ShipmentEventListener { + <> void onShipmentEvent(ShipmentRecord shipment); + } + + <> class OperationsDashboardListener implements ShipmentEventListener { + <> void onShipmentEvent(ShipmentRecord shipment); + } + + <> interface FulfillmentCommand { + <> boolean run(); + } + + <> class FulfillmentCommandQueue { + List commands; + <> void schedule(FulfillmentCommand command); + <> boolean drain(); + } + + <> class ReserveInventoryCommand implements FulfillmentCommand { + <> boolean run(); + } + + <> class BookCarrierCommand implements FulfillmentCommand { + <> boolean run(); + } + + <> class RecordShipmentCommand implements FulfillmentCommand { + <> boolean run(); + } + + <> interface RoutePolicy { + <> String selectRoute(DeliveryOrder order); + } + + <> class RoutePlanner { + <> RoutePolicy policy; + <> void usePolicy(RoutePolicy policy); + <> String planRoute(DeliveryOrder order); + } + + <> class FastestRoutePolicy implements RoutePolicy { + <> String selectRoute(DeliveryOrder order); + } + + <> class SustainableRoutePolicy implements RoutePolicy { + <> String selectRoute(DeliveryOrder order); + } + + <> interface CarrierGateway { + <> String createConsignment(DeliveryOrder order, String route); + } + + <> class LegacyCarrierApi { + <> String createLegacyConsignment(DeliveryOrder order, String route); + } + + <> class LegacyCarrierGatewayAdapter implements CarrierGateway { + <> LegacyCarrierApi legacyCarrier; + LegacyCarrierGatewayAdapter(LegacyCarrierApi legacyCarrier); + <> String createConsignment(DeliveryOrder order, String route); + } + + <> class CarrierGatewayFactory { + <> CarrierGateway createGateway(LegacyCarrierApi legacyCarrier); + } + + <> class DeliveryOrder; + class DeliveryItem; + class InventoryLedger; + class ShipmentRecord; + class ShipmentRepository; + class AuditLog; + class CustomerNotifier; + class FulfillmentCoordinator; + class FulfillmentScenario; +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/Reference.cd new file mode 100644 index 00000000..eaaf4d34 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/Reference.cd @@ -0,0 +1,55 @@ +import java.lang.String; +import java.util.List; + +classdiagram Reference { + class Order; + + class Subject { + List observers; + void attach(Observer observer); + void detach(Observer observer); + void notifyObservers(String event); + } + + interface Observer { + void update(String event); + } + + interface Command { + boolean execute(); + } + + class Invoker { + List commands; + void enqueue(Command command); + boolean runAll(); + } + + interface Strategy { + String execute(Order order); + } + + class Context { + Strategy strategy; + void setStrategy(Strategy strategy); + String execute(Order order); + } + + interface Target { + String request(Order order, String route); + } + + class Adaptee { + String specificRequest(Order order, String route); + } + + class Adapter implements Target { + Adaptee adaptee; + Adapter(Adaptee adaptee); + String request(Order order, String route); + } + + class Creator { + Target create(Adaptee adaptee); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Adaptee.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Adaptee.java new file mode 100644 index 00000000..f9531d76 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Adaptee.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Adaptee", template = "${}") +public class Adaptee { + @Adapt(ref = "Adaptee.specificRequest", template = "${}") + public String specificRequest(Order order, String route) { + return ""; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Adapter.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Adapter.java new file mode 100644 index 00000000..6420116d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Adapter.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Adapter", template = "${}") +public class Adapter implements Target { + @Adapt(ref = "Adapter.adaptee", template = "${}") + private final Adaptee adaptee; + + public Adapter(Adaptee adaptee) { + this.adaptee = adaptee; + } + + @Override + @Adapt(ref = "Target.request", template = "${}") + public String request(Order order, String route) { + return adaptee.specificRequest(order, route); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Command.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Command.java new file mode 100644 index 00000000..6d6f054e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Command.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Command", template = "${}") +public interface Command { + @Adapt(ref = "Command.execute", template = "${}") + boolean execute(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Context.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Context.java new file mode 100644 index 00000000..5c9f1ced --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Context.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Context", template = "${}") +public class Context { + @Adapt(ref = "Context.strategy", template = "${}") + private Strategy strategy; + + @Adapt(ref = "Context.setStrategy", template = "${}") + public void setStrategy(Strategy strategy) { + this.strategy = strategy; + } + + @Adapt(ref = "Context.execute", template = "${}") + public String execute(Order order) { + return strategy.execute(order); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Creator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Creator.java new file mode 100644 index 00000000..c26a5d51 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Creator.java @@ -0,0 +1,11 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Creator", template = "${}") +public class Creator { + @Adapt(ref = "Creator.create", template = "${}") + public Target create(Adaptee adaptee) { + return new Adapter(adaptee); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Invoker.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Invoker.java new file mode 100644 index 00000000..450cba9e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Invoker.java @@ -0,0 +1,28 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.ArrayList; +import java.util.List; + +@Adapt(ref = "Invoker", template = "${}") +public class Invoker { + @Adapt(ref = "Invoker.commands", template = "${}") + private final List commands = new ArrayList<>(); + + @Adapt(ref = "Invoker.enqueue", template = "${}") + public void enqueue(Command command) { + commands.add(command); + } + + @Adapt(ref = "Invoker.runAll", template = "${}") + public boolean runAll() { + boolean successful = true; + for (Command command : commands) { + if (successful) { + successful = command.execute(); + } + } + commands.clear(); + return successful; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Observer.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Observer.java new file mode 100644 index 00000000..78207d68 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Observer.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Observer", template = "${}") +public interface Observer { + @Adapt(ref = "Observer.update", template = "${}") + void update(String event); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Strategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Strategy.java new file mode 100644 index 00000000..8a6de114 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Strategy.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Strategy", template = "${}") +public interface Strategy { + @Adapt(ref = "Strategy.execute", template = "${}") + String execute(Order order); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Subject.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Subject.java new file mode 100644 index 00000000..1d05fe72 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Subject.java @@ -0,0 +1,28 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.ArrayList; +import java.util.List; + +@Adapt(ref = "Subject", template = "${}") +public class Subject { + @Adapt(ref = "Subject.observers", template = "${}") + private final List observers = new ArrayList<>(); + + @Adapt(ref = "Subject.attach", template = "${}") + public void attach(Observer observer) { + observers.add(observer); + } + + @Adapt(ref = "Subject.detach", template = "${}") + public void detach(Observer observer) { + observers.remove(observer); + } + + @Adapt(ref = "Subject.notifyObservers", template = "${}") + public void notifyObservers(String event) { + for (Observer observer : observers) { + observer.update(event); + } + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Target.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Target.java new file mode 100644 index 00000000..fc5c309a --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/adapter/Target.java @@ -0,0 +1,9 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.adapter; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = "Target", template = "${}") +public interface Target { + @Adapt(ref = "Target.request", template = "${}") + String request(Order order, String route); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/AuditLog.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/AuditLog.java new file mode 100644 index 00000000..90f6abb5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/AuditLog.java @@ -0,0 +1,21 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +import java.util.ArrayList; +import java.util.List; + +public class AuditLog { + private final List entries = new ArrayList<>(); + + public void append(String entry) { + entries.add(entry); + } + + public boolean containsOrder(String orderId) { + for (String entry : entries) { + if (entry.contains(orderId)) { + return true; + } + } + return false; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/AuditTrailListener.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/AuditTrailListener.java new file mode 100644 index 00000000..6bba37cf --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/AuditTrailListener.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class AuditTrailListener implements ShipmentEventListener { + private final AuditLog auditLog; + + public AuditTrailListener(AuditLog auditLog) { + this.auditLog = auditLog; + } + + @Override + public void onShipmentEvent(ShipmentRecord shipment) { + auditLog.append("shipment:" + shipment.getOrderId() + ":" + shipment.getLabel()); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/BookCarrierCommand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/BookCarrierCommand.java new file mode 100644 index 00000000..b0b04914 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/BookCarrierCommand.java @@ -0,0 +1,24 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class BookCarrierCommand implements FulfillmentCommand { + private final CarrierGateway gateway; + private final DeliveryOrder order; + private final String route; + private String label = ""; + + public BookCarrierCommand(CarrierGateway gateway, DeliveryOrder order, String route) { + this.gateway = gateway; + this.order = order; + this.route = route; + } + + @Override + public boolean run() { + label = gateway.createConsignment(order, route); + return label != null && !label.isBlank(); + } + + public String getLabel() { + return label; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CarrierGateway.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CarrierGateway.java new file mode 100644 index 00000000..a0b2c573 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CarrierGateway.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public interface CarrierGateway { + String createConsignment(DeliveryOrder order, String route); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CarrierGatewayFactory.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CarrierGatewayFactory.java new file mode 100644 index 00000000..d0dc030c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CarrierGatewayFactory.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class CarrierGatewayFactory { + public CarrierGateway createGateway(LegacyCarrierApi legacyCarrier) { + return new LegacyCarrierGatewayAdapter(legacyCarrier); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CustomerNotificationListener.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CustomerNotificationListener.java new file mode 100644 index 00000000..dfc9c8ba --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CustomerNotificationListener.java @@ -0,0 +1,17 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class CustomerNotificationListener implements ShipmentEventListener { + private final CustomerNotifier notifier; + private final String customerEmail; + + public CustomerNotificationListener(CustomerNotifier notifier, String customerEmail) { + this.notifier = notifier; + this.customerEmail = customerEmail; + } + + @Override + public void onShipmentEvent(ShipmentRecord shipment) { + notifier.notifyCustomer( + customerEmail, "Shipment " + shipment.getLabel() + " is booked via " + shipment.getRoute()); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CustomerNotifier.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CustomerNotifier.java new file mode 100644 index 00000000..c98c382b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/CustomerNotifier.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class CustomerNotifier { + private String latestRecipient = ""; + private String latestMessage = ""; + + public void notifyCustomer(String email, String message) { + latestRecipient = email; + latestMessage = message; + } + + public boolean notified(String email) { + return latestRecipient.equals(email) && !latestMessage.isBlank(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/DeliveryItem.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/DeliveryItem.java new file mode 100644 index 00000000..cb48a6bd --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/DeliveryItem.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class DeliveryItem { + private final String sku; + private final int quantity; + + public DeliveryItem(String sku, int quantity) { + this.sku = sku; + this.quantity = quantity; + } + + public String getSku() { + return sku; + } + + public int getQuantity() { + return quantity; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/DeliveryOrder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/DeliveryOrder.java new file mode 100644 index 00000000..278374c1 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/DeliveryOrder.java @@ -0,0 +1,53 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +import java.util.ArrayList; +import java.util.List; + +public class DeliveryOrder { + private final String orderId; + private final String customerEmail; + private final List items; + private final boolean priority; + private final boolean sustainable; + + public DeliveryOrder( + String orderId, + String customerEmail, + List items, + boolean priority, + boolean sustainable) { + this.orderId = orderId; + this.customerEmail = customerEmail; + this.items = new ArrayList<>(items); + this.priority = priority; + this.sustainable = sustainable; + } + + public String getOrderId() { + return orderId; + } + + public String getCustomerEmail() { + return customerEmail; + } + + public List getItems() { + return List.copyOf(items); + } + + public boolean isPriority() { + return priority; + } + + public boolean isSustainable() { + return sustainable; + } + + public int totalUnits() { + int units = 0; + for (DeliveryItem item : items) { + units += item.getQuantity(); + } + return units; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FastestRoutePolicy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FastestRoutePolicy.java new file mode 100644 index 00000000..58406d35 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FastestRoutePolicy.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class FastestRoutePolicy implements RoutePolicy { + @Override + public String selectRoute(DeliveryOrder order) { + return order.isPriority() ? "AIR_EXPRESS" : "ROAD_EXPRESS"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCommand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCommand.java new file mode 100644 index 00000000..fd7c77f5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCommand.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public interface FulfillmentCommand { + boolean run(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCommandQueue.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCommandQueue.java new file mode 100644 index 00000000..c175f53e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCommandQueue.java @@ -0,0 +1,19 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +/** The command list is supplied by adapted reference code. */ +public class FulfillmentCommandQueue { + public void schedule(FulfillmentCommand command) { + commands.add(command); + } + + public boolean drain() { + boolean successful = true; + for (FulfillmentCommand command : commands) { + if (successful) { + successful = command.run(); + } + } + commands.clear(); + return successful; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCoordinator.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCoordinator.java new file mode 100644 index 00000000..cf0b60ed --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentCoordinator.java @@ -0,0 +1,39 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class FulfillmentCoordinator { + private final RoutePlanner routePlanner; + private final CarrierGateway carrierGateway; + private final FulfillmentCommandQueue commandQueue; + private final ShipmentEventPublisher eventPublisher; + private final InventoryLedger inventory; + private final ShipmentRepository shipments; + + public FulfillmentCoordinator( + RoutePlanner routePlanner, + CarrierGateway carrierGateway, + FulfillmentCommandQueue commandQueue, + ShipmentEventPublisher eventPublisher, + InventoryLedger inventory, + ShipmentRepository shipments) { + this.routePlanner = routePlanner; + this.carrierGateway = carrierGateway; + this.commandQueue = commandQueue; + this.eventPublisher = eventPublisher; + this.inventory = inventory; + this.shipments = shipments; + } + + public boolean fulfill(DeliveryOrder order) { + RoutePolicy selectedPolicy = + order.isSustainable() ? new SustainableRoutePolicy() : new FastestRoutePolicy(); + routePlanner.usePolicy(selectedPolicy); + String route = routePlanner.planRoute(order); + + BookCarrierCommand booking = new BookCarrierCommand(carrierGateway, order, route); + commandQueue.schedule(new ReserveInventoryCommand(inventory, order)); + commandQueue.schedule(booking); + commandQueue.schedule( + new RecordShipmentCommand(order, route, booking, shipments, eventPublisher)); + return commandQueue.drain(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentScenario.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentScenario.java new file mode 100644 index 00000000..cd9bc2c3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/FulfillmentScenario.java @@ -0,0 +1,87 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +import java.util.List; + +public final class FulfillmentScenario { + private FulfillmentScenario() { + } + + public static boolean run() { + DeliveryOrder order = + new DeliveryOrder( + "ORDER-2026-017", + "customer@example.test", + List.of(new DeliveryItem("ROBOT-ARM", 2), new DeliveryItem("SENSOR-KIT", 3)), + false, + true); + + InventoryLedger inventory = new InventoryLedger(); + inventory.stock("ROBOT-ARM", 5); + inventory.stock("SENSOR-KIT", 10); + ShipmentRepository shipments = new ShipmentRepository(); + AuditLog auditLog = new AuditLog(); + CustomerNotifier notifier = new CustomerNotifier(); + + ShipmentEventPublisher publisher = new ShipmentEventPublisher(); + OperationsDashboardListener dashboard = new OperationsDashboardListener(); + publisher.subscribe(new AuditTrailListener(auditLog)); + publisher.subscribe(new CustomerNotificationListener(notifier, order.getCustomerEmail())); + publisher.subscribe(dashboard); + + RoutePlanner planner = new RoutePlanner(new FastestRoutePolicy()); + CarrierGateway gateway = + new CarrierGatewayFactory().createGateway(new LegacyCarrierApi("FULFILLMENT-EU")); + FulfillmentCoordinator coordinator = + new FulfillmentCoordinator( + planner, + gateway, + new FulfillmentCommandQueue(), + publisher, + inventory, + shipments); + + boolean completed = coordinator.fulfill(order); + return completed + && inventory.reservedUnits("ROBOT-ARM") == 2 + && inventory.reservedUnits("SENSOR-KIT") == 3 + && shipments.containsOrder(order.getOrderId()) + && auditLog.containsOrder(order.getOrderId()) + && notifier.notified(order.getCustomerEmail()) + && dashboard.getProcessedShipments() == 1 + && dashboard.getLatestOrderId().equals(order.getOrderId()); + } + + /** + * Proves that an adapted command queue stops after failure, clears the failed batch, and can be + * reused without replaying old commands. + */ + public static boolean runQueueFailureAndReuse() { + int[] executions = {0}; + FulfillmentCommandQueue queue = new FulfillmentCommandQueue(); + queue.schedule(new ProbeCommand(executions, 1, false)); + queue.schedule(new ProbeCommand(executions, 100, true)); + boolean firstResult = queue.drain(); + + queue.schedule(new ProbeCommand(executions, 10, true)); + boolean secondResult = queue.drain(); + return !firstResult && secondResult && executions[0] == 11; + } + + private static final class ProbeCommand implements FulfillmentCommand { + private final int[] executions; + private final int weight; + private final boolean result; + + private ProbeCommand(int[] executions, int weight, boolean result) { + this.executions = executions; + this.weight = weight; + this.result = result; + } + + @Override + public boolean run() { + executions[0] += weight; + return result; + } + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/InventoryLedger.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/InventoryLedger.java new file mode 100644 index 00000000..3e3eee4b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/InventoryLedger.java @@ -0,0 +1,32 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +import java.util.HashMap; +import java.util.Map; + +public class InventoryLedger { + private final Map available = new HashMap<>(); + private final Map reserved = new HashMap<>(); + + public void stock(String sku, int quantity) { + available.put(sku, available.getOrDefault(sku, 0) + quantity); + } + + public boolean reserve(DeliveryOrder order) { + for (DeliveryItem item : order.getItems()) { + if (available.getOrDefault(item.getSku(), 0) < item.getQuantity()) { + return false; + } + } + for (DeliveryItem item : order.getItems()) { + available.put( + item.getSku(), available.getOrDefault(item.getSku(), 0) - item.getQuantity()); + reserved.put( + item.getSku(), reserved.getOrDefault(item.getSku(), 0) + item.getQuantity()); + } + return true; + } + + public int reservedUnits(String sku) { + return reserved.getOrDefault(sku, 0); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/LegacyCarrierApi.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/LegacyCarrierApi.java new file mode 100644 index 00000000..60af1397 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/LegacyCarrierApi.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class LegacyCarrierApi { + private final String accountCode; + private int sequence; + + public LegacyCarrierApi(String accountCode) { + this.accountCode = accountCode; + } + + public String createLegacyConsignment(DeliveryOrder order, String route) { + sequence++; + return accountCode + "-" + route + "-" + order.getOrderId() + "-" + sequence; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/LegacyCarrierGatewayAdapter.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/LegacyCarrierGatewayAdapter.java new file mode 100644 index 00000000..841d70fb --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/LegacyCarrierGatewayAdapter.java @@ -0,0 +1,7 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class LegacyCarrierGatewayAdapter implements CarrierGateway { + public LegacyCarrierGatewayAdapter(LegacyCarrierApi legacyCarrier) { + this.legacyCarrier = legacyCarrier; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/OperationsDashboardListener.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/OperationsDashboardListener.java new file mode 100644 index 00000000..1394bd91 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/OperationsDashboardListener.java @@ -0,0 +1,20 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class OperationsDashboardListener implements ShipmentEventListener { + private int processedShipments; + private String latestOrderId = ""; + + @Override + public void onShipmentEvent(ShipmentRecord shipment) { + processedShipments++; + latestOrderId = shipment.getOrderId(); + } + + public int getProcessedShipments() { + return processedShipments; + } + + public String getLatestOrderId() { + return latestOrderId; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RecordShipmentCommand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RecordShipmentCommand.java new file mode 100644 index 00000000..41ca5d4f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RecordShipmentCommand.java @@ -0,0 +1,33 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class RecordShipmentCommand implements FulfillmentCommand { + private final DeliveryOrder order; + private final String route; + private final BookCarrierCommand booking; + private final ShipmentRepository repository; + private final ShipmentEventPublisher publisher; + + public RecordShipmentCommand( + DeliveryOrder order, + String route, + BookCarrierCommand booking, + ShipmentRepository repository, + ShipmentEventPublisher publisher) { + this.order = order; + this.route = route; + this.booking = booking; + this.repository = repository; + this.publisher = publisher; + } + + @Override + public boolean run() { + if (booking.getLabel().isBlank()) { + return false; + } + ShipmentRecord shipment = new ShipmentRecord(order.getOrderId(), route, booking.getLabel()); + repository.save(shipment); + publisher.publishShipment(shipment); + return true; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ReserveInventoryCommand.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ReserveInventoryCommand.java new file mode 100644 index 00000000..04070b9e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ReserveInventoryCommand.java @@ -0,0 +1,16 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class ReserveInventoryCommand implements FulfillmentCommand { + private final InventoryLedger inventory; + private final DeliveryOrder order; + + public ReserveInventoryCommand(InventoryLedger inventory, DeliveryOrder order) { + this.inventory = inventory; + this.order = order; + } + + @Override + public boolean run() { + return inventory.reserve(order); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RoutePlanner.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RoutePlanner.java new file mode 100644 index 00000000..fcceb423 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RoutePlanner.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class RoutePlanner { + public RoutePlanner(RoutePolicy policy) { + this.policy = policy; + } + + public void usePolicy(RoutePolicy policy) { + this.policy = policy; + } + + public String planRoute(DeliveryOrder order) { + return policy.selectRoute(order); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RoutePolicy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RoutePolicy.java new file mode 100644 index 00000000..af9e3b79 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/RoutePolicy.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public interface RoutePolicy { + String selectRoute(DeliveryOrder order); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentEventListener.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentEventListener.java new file mode 100644 index 00000000..78d57e98 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentEventListener.java @@ -0,0 +1,5 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public interface ShipmentEventListener { + void onShipmentEvent(ShipmentRecord shipment); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentEventPublisher.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentEventPublisher.java new file mode 100644 index 00000000..5d883ff6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentEventPublisher.java @@ -0,0 +1,12 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +/** The observer field and publication loop are supplied by adapted reference code. */ +public class ShipmentEventPublisher { + public void subscribe(ShipmentEventListener listener) { + listeners.add(listener); + } + + public void unsubscribe(ShipmentEventListener listener) { + listeners.remove(listener); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentRecord.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentRecord.java new file mode 100644 index 00000000..e109d60d --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentRecord.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class ShipmentRecord { + private final String orderId; + private final String route; + private final String label; + + public ShipmentRecord(String orderId, String route, String label) { + this.orderId = orderId; + this.route = route; + this.label = label; + } + + public String getOrderId() { + return orderId; + } + + public String getRoute() { + return route; + } + + public String getLabel() { + return label; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentRepository.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentRepository.java new file mode 100644 index 00000000..1bae7985 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/ShipmentRepository.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +import java.util.ArrayList; +import java.util.List; + +public class ShipmentRepository { + private final List shipments = new ArrayList<>(); + + public void save(ShipmentRecord shipment) { + shipments.add(shipment); + } + + public boolean containsOrder(String orderId) { + for (ShipmentRecord shipment : shipments) { + if (shipment.getOrderId().equals(orderId)) { + return true; + } + } + return false; + } + + public int size() { + return shipments.size(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/SustainableRoutePolicy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/SustainableRoutePolicy.java new file mode 100644 index 00000000..064a5bb8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_17_fulfillment_platform/concrete/SustainableRoutePolicy.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.evaluation.testcase_17_fulfillment_platform.concrete; + +public class SustainableRoutePolicy implements RoutePolicy { + @Override + public String selectRoute(DeliveryOrder order) { + return order.totalUnits() > 8 ? "RAIL_CONSOLIDATED" : "ELECTRIC_VAN"; + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Concrete.cd index 0d27a347..67ab7cf7 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Concrete.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Concrete.cd @@ -1,4 +1,3 @@ -package de.monticore.codeAdaption.evaluation.testcase_2_cd4code; import java.lang.String; import java.util.Date; diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Reference.cd index 84d52cf2..d82008c5 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Reference.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/Reference.cd @@ -1,5 +1,5 @@ -package de.monticore.codeAdaption.evaluation.testcase_2_cd4code; import java.lang.String; +import java.util.Date; classdiagram Reference { diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/hwc/entity/User.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/hwc/entity/User.java index 41eff3c6..fde3af7c 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/hwc/entity/User.java +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_2_cd4code/hwc/entity/User.java @@ -1,4 +1,4 @@ -package de.monticore.codeAdaption.evaluation.testcase_2_cd4code.Concrete; +package Concrete; import de.monticore.codeAdaption.utils.Adapt; diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Concrete.cd new file mode 100644 index 00000000..536414f8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Concrete.cd @@ -0,0 +1,28 @@ +import java.lang.String; +import java.lang.Object; +import java.util.Date; +import java.util.List; + +classdiagram Concrete { + + <> class Person { + <> String name; + <> int age; + <> String email; + <> String phone; + <> List tags; + + <> void sX(Object x); + } + + <> class Task { + <> int id; + <> String title; + <> String description; + <> String status; + <> Date dueDate; + <> String assignee; + + <> void sX(Object x); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Reference.cd new file mode 100644 index 00000000..78994fee --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/Reference.cd @@ -0,0 +1,8 @@ + +classdiagram Reference { + class Builder { + any attribute; + void setAttribute(any attribute); + } +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/adapter/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/adapter/Builder.java new file mode 100644 index 00000000..f30cac40 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/adapter/Builder.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_6_builder_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}", genTemplate = "${}Builder") +public class Builder { + private Object attribute; + + @Adapt(ref = {"Builder.attribute"}, template = "set${cap_first}") + public Builder setAttribute(Object attribute) { + this.attribute = attribute; + return this; + } +} \ No newline at end of file diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/adapter/GenericBuilder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/adapter/GenericBuilder.java new file mode 100644 index 00000000..e69de29b diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/concrete/Person.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/concrete/Person.java new file mode 100644 index 00000000..ea0348e4 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/concrete/Person.java @@ -0,0 +1,59 @@ +package de.monticore.codeAdaption.evaluation.testcase_6_builder_pattern; + +import java.util.List; + +/** + * Person domain class with attributes + */ +public class Person { + private String name; + private int age; + private String email; + private String phone; + private List tags; + + public Person() { + } + + public Person(String name, int age, String email, String phone, List tags) { + this.name = name; + this.age = age; + this.email = email; + this.phone = phone; + this.tags = tags; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public String getEmail() { + return email; + } + + public String getPhone() { + return phone; + } + + public List getTags() { + return tags; + } + + public void sX(Object x) {} + + @Override + public String toString() { + return "Person{" + + "name='" + name + '\'' + + ", age=" + age + + ", email='" + email + '\'' + + ", phone='" + phone + '\'' + + ", tags=" + tags + + '}'; + } +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/concrete/Task.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/concrete/Task.java new file mode 100644 index 00000000..b74a4c7b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_6_builder_pattern/concrete/Task.java @@ -0,0 +1,88 @@ +package de.monticore.codeAdaption.evaluation.testcase_6_builder_pattern; + +import java.util.Date; + +/** + * Task domain class with attributes + */ +public class Task { + private int id; + private String title; + private String description; + private String status; + private Date dueDate; + private String assignee; + + public Task() { + } + + public Task(int id, String title, String description, String status, Date dueDate, String assignee) { + this.id = id; + this.title = title; + this.description = description; + this.status = status; + this.dueDate = dueDate; + this.assignee = assignee; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public Date getDueDate() { + return dueDate; + } + + public void setDueDate(Date dueDate) { + this.dueDate = dueDate; + } + + public String getAssignee() { + return assignee; + } + + public void setAssignee(String assignee) { + this.assignee = assignee; + } + + @Override + public String toString() { + return "Task{" + + "id=" + id + + ", title='" + title + '\'' + + ", description='" + description + '\'' + + ", status='" + status + '\'' + + ", dueDate=" + dueDate + + ", assignee='" + assignee + '\'' + + '}'; + } +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/Concrete.cd new file mode 100644 index 00000000..43da8d35 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/Concrete.cd @@ -0,0 +1,34 @@ +import java.lang.String; +import java.lang.Object; + +classdiagram Concrete { + + <> class Controller { + <> String id; + <> String status; + <> void sX(Object x); + } + + <> interface WorkerInterface { + void work(); + } + + <> class WorkerA implements WorkerInterface { + <> String name; + <> void sX(Object x); + void work(); + } + + <> class WorkerB implements WorkerInterface { + <> String name; + <> void sX(Object x); + void work(); + } +} + + + + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/Reference.cd new file mode 100644 index 00000000..32469955 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/Reference.cd @@ -0,0 +1,25 @@ +import java.util.List; + + +classdiagram Reference { + class Builder { + any attribute; + void setAttribute(any attribute); + } + + class Subject { + List observers; + boolean subscribe(Observer o); + boolean unsubscribe(Observer o); + void notifyAll(); + } + + class Observer { + void update(); + } +} + + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/Builder.java new file mode 100644 index 00000000..11534ac3 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/Builder.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.evaluation.testcase_7_controller_worker_observer; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}", genTemplate = "${}Builder") +public class Builder { + private Object attribute; + + @Adapt(ref = {"Builder.attribute"}, template = "set${cap_first}") + public Builder setAttribute(Object attribute) { + this.attribute = attribute; + return this; + } +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/OObserver.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/OObserver.java new file mode 100644 index 00000000..ae92e393 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/OObserver.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_7_controller_worker_observer; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Observer"}, template = "${}") +public interface OObserver { + + @Adapt(ref = {"Observer.update"}, template = "update") + void update(); +} + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/OSubject.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/OSubject.java new file mode 100644 index 00000000..f7a5839f --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/adapter/OSubject.java @@ -0,0 +1,33 @@ +package de.monticore.codeAdaption.evaluation.testcase_7_controller_worker_observer; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.List; + +@Adapt(ref = {"Subject"}, template = "${}") +public class OSubject { + + @Adapt(ref = {"Subject.observers"}, template = "${}") + private List observers; + + @Adapt(ref = {"Subject.subscribe"}, template = "subscribe") + public boolean subscribe(Observer w) { + // template method body - adapter will replace with concrete implementation + return observers != null && observers.add(w); + } + + @Adapt(ref = {"Subject.unsubscribe"}, template = "unsubscribe") + public boolean unsubscribe(Observer w) { + return observers != null && observers.remove(w); + } + + @Adapt(ref = {"Subject.notifyAll"}, template = "notifyAll") + public void notifyAll() throws Exception { + if (observers == null) return; + for (Observer w : observers) { + w.update(); + } + } +} + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/Controller.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/Controller.java new file mode 100644 index 00000000..93cc8568 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/Controller.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.evaluation.testcase_7_controller_worker_observer; + +/** + * Controller domain class used in the controller/worker observer example + */ +public class Controller { + private String id; + private String status; + + public Controller() { + } + + public Controller(String id, String status) { + this.id = id; + this.status = status; + } + + public String getId() { return id; } + public String getStatus() { return status; } + + public void sX(Object x) { } +} + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerA.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerA.java new file mode 100644 index 00000000..83799c54 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerA.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.evaluation.testcase_7_controller_worker_observer; + +/** + * WorkerA domain class (observer implementation) + */ +public class WorkerA implements WorkerInterface { + private String name; + + public WorkerA() {} + + public WorkerA(String name) { this.name = name; } + + public String getName() { return name; } + + public void update() { /* perform update handling */ } + + @Override + public void work() { /* perform work - adapter may map observer calls to this */ } + + public void sX(Object x) { } + + +} + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerB.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerB.java new file mode 100644 index 00000000..c18a8ec6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerB.java @@ -0,0 +1,26 @@ +package de.monticore.codeAdaption.evaluation.testcase_7_controller_worker_observer; + +/** + * WorkerB domain class (observer implementation) + */ +public class WorkerB implements WorkerInterface { + private String name; + + public WorkerB() {} + + public WorkerB(String name) { this.name = name; } + + public String getName() { return name; } + + + public void update() { /* perform update handling */ } + + @Override + public void work() { /* perform work - adapter may map observer calls to this */ } + + public void sX(Object x) { } +} + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerInterface.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerInterface.java new file mode 100644 index 00000000..05f82023 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_7_controller_worker_observer/concrete/WorkerInterface.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.evaluation.testcase_7_controller_worker_observer; + +public interface WorkerInterface { + void work(); +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/Concrete.cd new file mode 100644 index 00000000..7a4654b6 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/Concrete.cd @@ -0,0 +1,35 @@ +import java.lang.String; +import java.lang.Object; + + +classdiagram Concrete { + + <> class Controller { + <> String id; + <> String status; + <> void sX(Object x); + } + + interface WorkerInterface { + void work(); + } + + <> class WorkerA implements WorkerInterface { + <> String name; + <> void sX(Object x); + void work(); + } + + <> class WorkerB implements WorkerInterface { + <> String name; + <> void sX(Object x); + void work(); + } +} + + + + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/Reference.cd new file mode 100644 index 00000000..311297a5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/Reference.cd @@ -0,0 +1,25 @@ +import java.util.List; + + +classdiagram Reference { + class Builder { + any attribute; + void setAttribute(any attribute); + } + + class Subject { + List observers; + boolean subscribe(Observer o); + boolean unsubscribe(Observer o); + void notifyAll(); + } + + interface Observer { + void update(); + } +} + + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/Builder.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/Builder.java new file mode 100644 index 00000000..b3978c3c --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/Builder.java @@ -0,0 +1,15 @@ +package de.monticore.codeAdaption.evaluation.testcase_8_controller_worker_observer_reversed; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Builder"}, template = "${}", genTemplate = "${}Builder") +public class Builder { + private Object attribute; + + @Adapt(ref = {"Builder.attribute"}, template = "set${cap_first}") + public Builder setAttribute(Object attribute) { + this.attribute = attribute; + return this; + } +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/OObserver.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/OObserver.java new file mode 100644 index 00000000..92d8c731 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/OObserver.java @@ -0,0 +1,14 @@ +package de.monticore.codeAdaption.evaluation.testcase_8_controller_worker_observer_reversed; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Observer"}, template = "${}") +public interface OObserver { + + @Adapt(ref = {"Observer.update"}, template = "update") + void update(); +} + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/OSubject.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/OSubject.java new file mode 100644 index 00000000..a6b2cc75 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/adapter/OSubject.java @@ -0,0 +1,33 @@ +package de.monticore.codeAdaption.evaluation.testcase_8_controller_worker_observer_reversed; + +import de.monticore.codeAdaption.utils.Adapt; +import java.util.List; + +@Adapt(ref = {"Subject"}, template = "${}") +public class OSubject { + + @Adapt(ref = {"Subject.observers"}, template = "${}") + private List observers; + + @Adapt(ref = {"Subject.subscribe"}, template = "subscribe") + public boolean subscribe(Observer w) { + // template method body - adapter will replace with concrete implementation + return observers != null && observers.add(w); + } + + @Adapt(ref = {"Subject.unsubscribe"}, template = "unsubscribe") + public boolean unsubscribe(Observer w) { + return observers != null && observers.remove(w); + } + + @Adapt(ref = {"Subject.notifyAll"}, template = "notifyAll") + public void notifyAll() throws Exception { + if (observers == null) return; + for (Observer w : observers) { + w.update(); + } + } +} + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/Controller.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/Controller.java new file mode 100644 index 00000000..72d97d37 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/Controller.java @@ -0,0 +1,25 @@ +package de.monticore.codeAdaption.evaluation.testcase_8_controller_worker_observer_reversed; + +/** + * Controller domain class used in the controller/worker observer example + */ +public class Controller { + private String id; + private String status; + + public Controller() { + } + + public Controller(String id, String status) { + this.id = id; + this.status = status; + } + + public String getId() { return id; } + public String getStatus() { return status; } + + public void sX(Object x) { } +} + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerA.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerA.java new file mode 100644 index 00000000..f22fa81e --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerA.java @@ -0,0 +1,27 @@ +package de.monticore.codeAdaption.evaluation.testcase_8_controller_worker_observer_reversed; + +/** + * WorkerA domain class (observer implementation) + */ +public class WorkerA implements WorkerInterface { + private String name; + + public WorkerA() {} + + public WorkerA(String name) { this.name = name; } + + public String getName() { return name; } + + public void update() { /* perform update handling */ } + + @Override + public void work() { /* perform work - adapter may map observer calls to this */ } + + public void sX(Object x) { } + + +} + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerB.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerB.java new file mode 100644 index 00000000..27b17981 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerB.java @@ -0,0 +1,26 @@ +package de.monticore.codeAdaption.evaluation.testcase_8_controller_worker_observer_reversed; + +/** + * WorkerB domain class (observer implementation) + */ +public class WorkerB implements WorkerInterface { + private String name; + + public WorkerB() {} + + public WorkerB(String name) { this.name = name; } + + public String getName() { return name; } + + + public void update() { /* perform update handling */ } + + @Override + public void work() { /* perform work - adapter may map observer calls to this */ } + + public void sX(Object x) { } +} + + + + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerInterface.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerInterface.java new file mode 100644 index 00000000..fbdb9a76 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_8_controller_worker_observer_reversed/concrete/WorkerInterface.java @@ -0,0 +1,6 @@ +package de.monticore.codeAdaption.evaluation.testcase_8_controller_worker_observer_reversed; + +public interface WorkerInterface { + void work(); +} + diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/Concrete.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/Concrete.cd new file mode 100644 index 00000000..68b33ea2 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/Concrete.cd @@ -0,0 +1,28 @@ +import java.lang.String; + +classdiagram Concrete { + + <> class PaymentProcessor { + String merchantId; + <> void processPayment(double amount); + } + + <> interface PaymentStrategy { + <> void pay(double amount); + } + + class CreditCardStrategy implements PaymentStrategy { + String cardNumber; + void pay(double amount); + } + + class PayPalStrategy implements PaymentStrategy { + String email; + void pay(double amount); + } + + class BitcoinStrategy implements PaymentStrategy { + String walletAddress; + void pay(double amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/Reference.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/Reference.cd new file mode 100644 index 00000000..7d9e74f8 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/Reference.cd @@ -0,0 +1,20 @@ + +classdiagram Reference { + class Context { + Strategy strategy; + void setStrategy(Strategy strategy); + void execute(any attribute); + } + + interface Strategy { + void execute(any attribute); + } + + class ConcreteStrategyA implements Strategy { + void execute(any attribute); + } + + class ConcreteStrategyB implements Strategy { + void execute(any attribute); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/adapter/Context.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/adapter/Context.java new file mode 100644 index 00000000..c735fa80 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/adapter/Context.java @@ -0,0 +1,18 @@ +package de.monticore.codeAdaption.evaluation.testcase_9_strategy_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Context"}, template = "${}") +public class Context { + private Strategy strategy; + + @Adapt(ref = {"Context.strategy"}, template = "set${cap_first}") + public void setStrategy(Strategy strategy) { + this.strategy = strategy; + } + + @Adapt(ref = {"Context.execute"}, template = "${}") + public void execute() { + strategy.execute(); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/adapter/Strategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/adapter/Strategy.java new file mode 100644 index 00000000..f649b259 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/adapter/Strategy.java @@ -0,0 +1,10 @@ +package de.monticore.codeAdaption.evaluation.testcase_9_strategy_pattern; + +import de.monticore.codeAdaption.utils.Adapt; + +@Adapt(ref = {"Strategy"}, template = "${}") +public interface Strategy { + + @Adapt(ref = {"Strategy.execute"}, template = "${}") + void execute(); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/BitcoinStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/BitcoinStrategy.java new file mode 100644 index 00000000..43d7dbc5 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/BitcoinStrategy.java @@ -0,0 +1,21 @@ +package de.monticore.codeAdaption.evaluation.testcase_9_strategy_pattern; + +/** + * BitcoinStrategy concrete implementation of PaymentStrategy + */ +public class BitcoinStrategy implements PaymentStrategy { + private String walletAddress; + + public BitcoinStrategy() { + } + + public BitcoinStrategy(String walletAddress) { + this.walletAddress = walletAddress; + } + + public String getWalletAddress() { return walletAddress; } + + public void pay(double amount) { + System.out.println("Paid $" + amount + " using Bitcoin"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/CreditCardStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/CreditCardStrategy.java new file mode 100644 index 00000000..e5f20b20 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/CreditCardStrategy.java @@ -0,0 +1,21 @@ +package de.monticore.codeAdaption.evaluation.testcase_9_strategy_pattern; + +/** + * CreditCardStrategy concrete implementation of PaymentStrategy + */ +public class CreditCardStrategy implements PaymentStrategy { + private String cardNumber; + + public CreditCardStrategy() { + } + + public CreditCardStrategy(String cardNumber) { + this.cardNumber = cardNumber; + } + + public String getCardNumber() { return cardNumber; } + + public void pay(double amount) { + System.out.println("Paid $" + amount + " using Credit Card"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PayPalStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PayPalStrategy.java new file mode 100644 index 00000000..79620421 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PayPalStrategy.java @@ -0,0 +1,21 @@ +package de.monticore.codeAdaption.evaluation.testcase_9_strategy_pattern; + +/** + * PayPalStrategy concrete implementation of PaymentStrategy + */ +public class PayPalStrategy implements PaymentStrategy { + private String email; + + public PayPalStrategy() { + } + + public PayPalStrategy(String email) { + this.email = email; + } + + public String getEmail() { return email; } + + public void pay(double amount) { + System.out.println("Paid $" + amount + " using PayPal"); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PaymentProcessor.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PaymentProcessor.java new file mode 100644 index 00000000..64866669 --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PaymentProcessor.java @@ -0,0 +1,21 @@ +package de.monticore.codeAdaption.evaluation.testcase_9_strategy_pattern; + +/** + * PaymentProcessor domain class + */ +public class PaymentProcessor { + private String merchantId; + + public PaymentProcessor() { + } + + public PaymentProcessor(String merchantId) { + this.merchantId = merchantId; + } + + public String getMerchantId() { return merchantId; } + + public void processPayment(double amount) { + System.out.println("Processing payment of $" + amount); + } +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PaymentStrategy.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PaymentStrategy.java new file mode 100644 index 00000000..9fa45c9b --- /dev/null +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/evaluation/testcase_9_strategy_pattern/concrete/PaymentStrategy.java @@ -0,0 +1,8 @@ +package de.monticore.codeAdaption.evaluation.testcase_9_strategy_pattern; + +/** + * PaymentStrategy interface + */ +public interface PaymentStrategy { + void pay(double amount); +} diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/validator/Validator.cd b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/validator/Validator.cd index 5ebb7a38..251bed16 100644 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/validator/Validator.cd +++ b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/validator/Validator.cd @@ -1,3 +1,4 @@ +import java.lang.String; classdiagram Validator{ public class Entity { String id ; diff --git a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/validator/invalid/InvalidTemplate.java b/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/validator/invalid/InvalidTemplate.java deleted file mode 100644 index b3ab1061..00000000 --- a/ref-code-adaptation/src/test/resources/de/monticore/codeAdaption/validator/invalid/InvalidTemplate.java +++ /dev/null @@ -1,13 +0,0 @@ -import de.monticore.codeAdaption.utils.Adapt; - -@Adapt(template = "${}",ref = "Entity") -public class InvalidTemplate { - @Adapt(template = "${}",ref = "Entity") - public String someAttribut ; - - @Adapt(template = "${}",ref = "Entity") - public void someMethod(@Adapt(template = "${}",ref = "Entity")String args) { - @Adapt(template = "${}",ref = "Entity") - String someLocalvar ; - } -} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 6a86540a..073d94ee 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,6 +16,14 @@ pluginManagement { } dependencyResolutionManagement { + repositories { + mavenLocal() + maven { + credentials.username mavenUser + credentials.password mavenPassword + url repo + } + } versionCatalogs { create("corpusLibs") { from(files("./gradle/corpus.versions.toml"))