From 72e1e2c973046fe8d4bf14779b7ae5ccfb8da894 Mon Sep 17 00:00:00 2001 From: Stefan Bischof Date: Fri, 26 Dec 2025 11:51:27 +0100 Subject: [PATCH] rework maven plugin. improve ecore genmodel deps. - Add includeGenModelInJar parameter for saving GenModel to resources - Fix cross-package reference handling by including all packages in GenModel - Delete generated files for referenced packages after generation - Create GenPackages from Ecore annotations when loading from JARs Signed-off-by: Stefan Bischof --- .../example/catalog/CatalogModelTest.java | 42 +++ .../ecore.copyright/model/vehicle.ecore | 17 -- .../ecore.copyright/pom.xml | 107 ------- .../example/extended/ExtendedModelTest.java | 40 +++ .../ecore.fileext/model/config.ecore | 20 -- .../ecore.fileext/pom.xml | 105 ------- .../example/simple/SimpleModelTest.java | 38 +++ .../daanse/example/base/BaseModelTest.java | 42 +++ .../ecore.suppress/model/sensor.ecore | 15 - .../ecore.suppress/pom.xml | 107 ------- .../example/borrowing/BorrowingModelTest.java | 39 +++ .../example/library/LibraryModelTest.java | 45 +++ emf/codegen.maven/README.md | 223 ++++++++++++++ .../tooling/emf/codegen/EmfGenerateMojo.java | 284 ++++++++++++++---- 14 files changed, 697 insertions(+), 427 deletions(-) create mode 100644 emf/codegen.maven.example/ecore.annotated/src/test/java/org/eclipse/daanse/example/catalog/CatalogModelTest.java delete mode 100644 emf/codegen.maven.example/ecore.copyright/model/vehicle.ecore delete mode 100644 emf/codegen.maven.example/ecore.copyright/pom.xml create mode 100644 emf/codegen.maven.example/ecore.dependencies/src/test/java/org/eclipse/daanse/example/extended/ExtendedModelTest.java delete mode 100644 emf/codegen.maven.example/ecore.fileext/model/config.ecore delete mode 100644 emf/codegen.maven.example/ecore.fileext/pom.xml create mode 100644 emf/codegen.maven.example/ecore.noosgi/src/test/java/org/eclipse/daanse/example/simple/SimpleModelTest.java create mode 100644 emf/codegen.maven.example/ecore.simple/src/test/java/org/eclipse/daanse/example/base/BaseModelTest.java delete mode 100644 emf/codegen.maven.example/ecore.suppress/model/sensor.ecore delete mode 100644 emf/codegen.maven.example/ecore.suppress/pom.xml create mode 100644 emf/codegen.maven.example/genmodel.dependencies/src/test/java/org/eclipse/daanse/example/borrowing/BorrowingModelTest.java create mode 100644 emf/codegen.maven.example/genmodel.simple/src/test/java/org/eclipse/daanse/example/library/LibraryModelTest.java create mode 100644 emf/codegen.maven/README.md diff --git a/emf/codegen.maven.example/ecore.annotated/src/test/java/org/eclipse/daanse/example/catalog/CatalogModelTest.java b/emf/codegen.maven.example/ecore.annotated/src/test/java/org/eclipse/daanse/example/catalog/CatalogModelTest.java new file mode 100644 index 0000000..a4dd413 --- /dev/null +++ b/emf/codegen.maven.example/ecore.annotated/src/test/java/org/eclipse/daanse/example/catalog/CatalogModelTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.daanse.example.catalog; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.math.BigDecimal; + +import org.junit.jupiter.api.Test; + +class CatalogModelTest { + + @Test + void testPackageInitialized() { + assertNotNull(CatalogPackage.eINSTANCE); + assertNotNull(CatalogFactory.eINSTANCE); + } + + @Test + void testCreateCatalog() { + Catalog catalog = CatalogFactory.eINSTANCE.createCatalog(); + catalog.setName("Main Catalog"); + assertEquals("Main Catalog", catalog.getName()); + } + + @Test + void testCreateProduct() { + Product product = CatalogFactory.eINSTANCE.createProduct(); + product.setName("Widget"); + product.setPrice(new BigDecimal("9.99")); + assertEquals("Widget", product.getName()); + assertEquals(new BigDecimal("9.99"), product.getPrice()); + } +} diff --git a/emf/codegen.maven.example/ecore.copyright/model/vehicle.ecore b/emf/codegen.maven.example/ecore.copyright/model/vehicle.ecore deleted file mode 100644 index d95ae49..0000000 --- a/emf/codegen.maven.example/ecore.copyright/model/vehicle.ecore +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/emf/codegen.maven.example/ecore.copyright/pom.xml b/emf/codegen.maven.example/ecore.copyright/pom.xml deleted file mode 100644 index 2e334d3..0000000 --- a/emf/codegen.maven.example/ecore.copyright/pom.xml +++ /dev/null @@ -1,107 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.daanse - org.eclipse.daanse.tooling.emf.codegen.maven.example - 0.0.1-SNAPSHOT - - - - org.eclipse.daanse.tooling.emf.codegen.maven.example.ecore.copyright - jar - - Daanse EMF Codegen Example - Ecore Copyright Options - Ecore model testing copyrightText and rootExtendsClass options - - - - org.eclipse.emf - org.eclipse.emf.common - ${emf.common.version} - - - org.eclipse.emf - org.eclipse.emf.ecore - ${emf.ecore.version} - - - - org.eclipse.fennec.emf - org.eclipse.fennec.emf.osgi.api - 1.0.0-SNAPSHOT - - - - - - - org.eclipse.daanse - org.eclipse.daanse.tooling.emf.codegen.maven - ${project.version} - - - - generate - - - - model/vehicle.ecore - org.eclipse.daanse.example - Copyright (c) 2025 Eclipse Daanse Contributors. - Licensed under EPL-2.0. - - org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container - target/generated-sources/emf - - - - - - biz.aQute.bnd - biz.aQute.bndlib - 7.1.0 - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.0 - - - generate-sources - - add-source - - - - target/generated-sources/emf - - - - - - - - - diff --git a/emf/codegen.maven.example/ecore.dependencies/src/test/java/org/eclipse/daanse/example/extended/ExtendedModelTest.java b/emf/codegen.maven.example/ecore.dependencies/src/test/java/org/eclipse/daanse/example/extended/ExtendedModelTest.java new file mode 100644 index 0000000..76e45cb --- /dev/null +++ b/emf/codegen.maven.example/ecore.dependencies/src/test/java/org/eclipse/daanse/example/extended/ExtendedModelTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.daanse.example.extended; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class ExtendedModelTest { + + @Test + void testPackageInitialized() { + assertNotNull(ExtendedPackage.eINSTANCE); + assertNotNull(ExtendedFactory.eINSTANCE); + } + + @Test + void testCreateEmployee() { + Employee employee = ExtendedFactory.eINSTANCE.createEmployee(); + employee.setName("Jane"); + employee.setEmployeeId("E123"); + assertEquals("Jane", employee.getName()); + assertEquals("E123", employee.getEmployeeId()); + } + + @Test + void testCreateCompany() { + Company company = ExtendedFactory.eINSTANCE.createCompany(); + company.setName("Acme Corp"); + assertEquals("Acme Corp", company.getName()); + } +} diff --git a/emf/codegen.maven.example/ecore.fileext/model/config.ecore b/emf/codegen.maven.example/ecore.fileext/model/config.ecore deleted file mode 100644 index f389de0..0000000 --- a/emf/codegen.maven.example/ecore.fileext/model/config.ecore +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/emf/codegen.maven.example/ecore.fileext/pom.xml b/emf/codegen.maven.example/ecore.fileext/pom.xml deleted file mode 100644 index e7e3286..0000000 --- a/emf/codegen.maven.example/ecore.fileext/pom.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.daanse - org.eclipse.daanse.tooling.emf.codegen.maven.example - 0.0.1-SNAPSHOT - - - org.eclipse.daanse.tooling.emf.codegen.maven.example.ecore.fileext - jar - - Daanse EMF Codegen Example - Ecore File Extension - Ecore model testing fileExtension and prefix options - - - - org.eclipse.emf - org.eclipse.emf.common - ${emf.common.version} - - - org.eclipse.emf - org.eclipse.emf.ecore - ${emf.ecore.version} - - - - org.eclipse.fennec.emf - org.eclipse.fennec.emf.osgi.api - 1.0.0-SNAPSHOT - - - - - - - - org.eclipse.daanse - org.eclipse.daanse.tooling.emf.codegen.maven - ${project.version} - - - - generate - - - - model/config.ecore - org.eclipse.daanse.example - Cfg - cfg - target/generated-sources/emf - - - - - - biz.aQute.bnd - biz.aQute.bndlib - 7.1.0 - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.0 - - - generate-sources - - add-source - - - - target/generated-sources/emf - - - - - - - - - diff --git a/emf/codegen.maven.example/ecore.noosgi/src/test/java/org/eclipse/daanse/example/simple/SimpleModelTest.java b/emf/codegen.maven.example/ecore.noosgi/src/test/java/org/eclipse/daanse/example/simple/SimpleModelTest.java new file mode 100644 index 0000000..ba435aa --- /dev/null +++ b/emf/codegen.maven.example/ecore.noosgi/src/test/java/org/eclipse/daanse/example/simple/SimpleModelTest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.daanse.example.simple; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class SimpleModelTest { + + @Test + void testPackageInitialized() { + assertNotNull(SimplePackage.eINSTANCE); + assertNotNull(SimpleFactory.eINSTANCE); + } + + @Test + void testCreateItem() { + Item item = SimpleFactory.eINSTANCE.createItem(); + item.setName("Test Item"); + assertEquals("Test Item", item.getName()); + } + + @Test + void testCreateContainer() { + Container container = SimpleFactory.eINSTANCE.createContainer(); + container.setLabel("Box"); + assertEquals("Box", container.getLabel()); + } +} diff --git a/emf/codegen.maven.example/ecore.simple/src/test/java/org/eclipse/daanse/example/base/BaseModelTest.java b/emf/codegen.maven.example/ecore.simple/src/test/java/org/eclipse/daanse/example/base/BaseModelTest.java new file mode 100644 index 0000000..e96989b --- /dev/null +++ b/emf/codegen.maven.example/ecore.simple/src/test/java/org/eclipse/daanse/example/base/BaseModelTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.daanse.example.base; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class BaseModelTest { + + @Test + void testPackageInitialized() { + assertNotNull(BasePackage.eINSTANCE); + assertNotNull(BaseFactory.eINSTANCE); + } + + @Test + void testCreatePerson() { + Person person = BaseFactory.eINSTANCE.createPerson(); + person.setName("John"); + person.setAge(30); + assertEquals("John", person.getName()); + assertEquals(30, person.getAge()); + } + + @Test + void testCreateAddress() { + Address address = BaseFactory.eINSTANCE.createAddress(); + address.setStreet("Main St"); + address.setCity("Berlin"); + assertEquals("Main St", address.getStreet()); + assertEquals("Berlin", address.getCity()); + } +} diff --git a/emf/codegen.maven.example/ecore.suppress/model/sensor.ecore b/emf/codegen.maven.example/ecore.suppress/model/sensor.ecore deleted file mode 100644 index 0e0f51e..0000000 --- a/emf/codegen.maven.example/ecore.suppress/model/sensor.ecore +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - diff --git a/emf/codegen.maven.example/ecore.suppress/pom.xml b/emf/codegen.maven.example/ecore.suppress/pom.xml deleted file mode 100644 index 02a87d2..0000000 --- a/emf/codegen.maven.example/ecore.suppress/pom.xml +++ /dev/null @@ -1,107 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.daanse - org.eclipse.daanse.tooling.emf.codegen.maven.example - 0.0.1-SNAPSHOT - - - - org.eclipse.daanse.tooling.emf.codegen.maven.example.ecore.suppress - jar - - Daanse EMF Codegen Example - Ecore Suppress Options - Ecore model testing suppressInterfaces and suppressEMFTypes - options - - - - org.eclipse.emf - org.eclipse.emf.common - ${emf.common.version} - - - org.eclipse.emf - org.eclipse.emf.ecore - ${emf.ecore.version} - - - - org.eclipse.fennec.emf - org.eclipse.fennec.emf.osgi.api - 1.0.0-SNAPSHOT - - - - - - - - org.eclipse.daanse - org.eclipse.daanse.tooling.emf.codegen.maven - ${project.version} - - - - generate - - - - model/sensor.ecore - org.eclipse.daanse.example - true - true - target/generated-sources/emf - - - - - - biz.aQute.bnd - biz.aQute.bndlib - 7.1.0 - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.6.0 - - - generate-sources - - add-source - - - - target/generated-sources/emf - - - - - - - - - diff --git a/emf/codegen.maven.example/genmodel.dependencies/src/test/java/org/eclipse/daanse/example/borrowing/BorrowingModelTest.java b/emf/codegen.maven.example/genmodel.dependencies/src/test/java/org/eclipse/daanse/example/borrowing/BorrowingModelTest.java new file mode 100644 index 0000000..4355a44 --- /dev/null +++ b/emf/codegen.maven.example/genmodel.dependencies/src/test/java/org/eclipse/daanse/example/borrowing/BorrowingModelTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.daanse.example.borrowing; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class BorrowingModelTest { + + @Test + void testPackageInitialized() { + assertNotNull(BorrowingPackage.eINSTANCE); + assertNotNull(BorrowingFactory.eINSTANCE); + } + + @Test + void testCreateBorrower() { + Borrower borrower = BorrowingFactory.eINSTANCE.createBorrower(); + borrower.setName("Alice"); + borrower.setMemberId("M001"); + assertEquals("Alice", borrower.getName()); + assertEquals("M001", borrower.getMemberId()); + } + + @Test + void testCreateLoan() { + Loan loan = BorrowingFactory.eINSTANCE.createLoan(); + assertNotNull(loan); + } +} diff --git a/emf/codegen.maven.example/genmodel.simple/src/test/java/org/eclipse/daanse/example/library/LibraryModelTest.java b/emf/codegen.maven.example/genmodel.simple/src/test/java/org/eclipse/daanse/example/library/LibraryModelTest.java new file mode 100644 index 0000000..870236c --- /dev/null +++ b/emf/codegen.maven.example/genmodel.simple/src/test/java/org/eclipse/daanse/example/library/LibraryModelTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 Contributors to the Eclipse Foundation. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.daanse.example.library; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class LibraryModelTest { + + @Test + void testPackageInitialized() { + assertNotNull(LibraryPackage.eINSTANCE); + assertNotNull(LibraryFactory.eINSTANCE); + } + + @Test + void testCreateLibrary() { + Library library = LibraryFactory.eINSTANCE.createLibrary(); + library.setName("City Library"); + assertEquals("City Library", library.getName()); + } + + @Test + void testCreateBook() { + Book book = LibraryFactory.eINSTANCE.createBook(); + book.setTitle("EMF Guide"); + assertEquals("EMF Guide", book.getTitle()); + } + + @Test + void testCreateAuthor() { + Author author = LibraryFactory.eINSTANCE.createAuthor(); + author.setName("John Doe"); + assertEquals("John Doe", author.getName()); + } +} diff --git a/emf/codegen.maven/README.md b/emf/codegen.maven/README.md new file mode 100644 index 0000000..8daff75 --- /dev/null +++ b/emf/codegen.maven/README.md @@ -0,0 +1,223 @@ +# Eclipse Daanse EMF Code Generator Maven Plugin + +A Maven plugin for generating OSGi-compatible EMF model code from Ecore or GenModel files. + +## Features + +- Generate EMF model code from `.ecore` files (no GenModel required) +- Generate EMF model code from existing `.genmodel` files +- Automatic cross-package reference resolution from Maven dependencies +- OSGi-compatible code generation via Fennec EMF templates +- GenModel annotations support in Ecore files +- Multi-module project support with proper dependency handling + +## Usage + +Add the plugin to your `pom.xml`: + +```xml + + + + org.eclipse.daanse + org.eclipse.daanse.tooling.emf.codegen.maven + 0.0.1-SNAPSHOT + + + + generate + + + + + + + + + +``` + +## Modes of Operation + +### 1. Ecore Mode (Recommended) + +Generate code directly from an Ecore file. GenModel settings can be provided via: +- Maven plugin configuration +- GenModel annotations in the Ecore file + +```xml + + model/mymodel.ecore + target/generated-sources/emf + +``` + +### 2. GenModel Mode + +Use an existing GenModel file: + +```xml + + model/mymodel.genmodel + target/generated-sources/emf + +``` + +## Configuration Parameters + +### Input Files + +| Parameter | Property | Description | +|-----------|----------|-------------| +| `ecoreFile` | `emf.ecoreFile` | Path to the Ecore file | +| `genmodelFile` | `emf.genmodelFile` | Path to the GenModel file (takes precedence if both are set) | + +### Output + +| Parameter | Property | Default | Description | +|-----------|----------|---------|-------------| +| `outputDirectory` | `emf.outputDirectory` | `target/generated-sources/emf` | Output directory for generated code | +| `includeGenModelInJar` | `emf.includeGenModelInJar` | `false` | Include generated GenModel in JAR resources | + +### GenModel Settings (Ecore Mode) + +| Parameter | Property | Default | Description | +|-----------|----------|---------|-------------| +| `basePackage` | `emf.basePackage` | (derived) | Base package for generated code | +| `prefix` | `emf.prefix` | (derived) | Prefix for generated class names | +| `fileExtension` | `emf.fileExtension` | - | File extension for model resources | +| `osgiCompatible` | `emf.osgiCompatible` | `true` | Generate OSGi-compatible code | +| `suppressInterfaces` | `emf.suppressInterfaces` | `false` | Suppress interface generation | +| `suppressEMFTypes` | `emf.suppressEMFTypes` | `false` | Use Java native types instead of EMF types | +| `suppressEMFMetaData` | `emf.suppressEMFMetaData` | `false` | Suppress EMF metadata generation | +| `suppressGenModelAnnotations` | `emf.suppressGenModelAnnotations` | `false` | Suppress GenModel annotations in generated code | +| `publicConstructors` | `emf.publicConstructors` | `false` | Make constructors public | +| `rootExtendsClass` | `emf.rootExtendsClass` | - | Root class for generated model objects | +| `rootExtendsInterface` | `emf.rootExtendsInterface` | - | Root interface for generated model objects | +| `copyrightText` | `emf.copyrightText` | - | Copyright text for generated files | + +## GenModel Annotations in Ecore + +You can embed GenModel settings directly in your Ecore file using annotations: + +```xml + + + +
+
+
+
+ + + + +``` + +### Supported Annotation Keys + +- `basePackage` - Base package for generated code +- `prefix` - Prefix for factory and package classes +- `fileExtensions` - File extensions for resources +- `oSGiCompatible` - Enable OSGi compatibility (true/false) +- `suppressInterfaces` - Suppress interface generation +- `suppressEMFTypes` - Use Java types instead of EMF types +- `copyrightText` - Copyright header text + +## Cross-Package References + +The plugin automatically resolves cross-package references from Maven dependencies. When your Ecore model references types from another package: + +1. Add the dependency module to your `pom.xml` +2. The plugin scans JAR dependencies for `.ecore` and `.genmodel` files +3. GenPackages are automatically created and configured +4. Referenced packages are not regenerated; existing code from JARs is used + +### Example Multi-Module Setup + +```text +parent/ +├── pom.xml +├── model-base/ # Independent model +│ ├── pom.xml +│ └── model/base.ecore +├── model-ext/ # Extends base model +│ ├── pom.xml +│ └── model/ext.ecore # References base types +``` + +In `model-ext/pom.xml`: + +```xml + + + com.example + model-base + ${project.version} + + + + + + + org.eclipse.daanse + org.eclipse.daanse.tooling.emf.codegen.maven + + model/ext.ecore + + + + + +``` + +## Required Dependencies + +The plugin requires `biz.aQute.bndlib` for OSGi manifest generation +because the code uses bnd annotations for osgi support. + +```xml + + org.eclipse.daanse + org.eclipse.daanse.tooling.emf.codegen.maven + + + biz.aQute.bnd + biz.aQute.bndlib + 7.1.0 + + + +``` + +## Generated Code Structure + +For a package named `mymodel` with `basePackage=com.example`: + +```text +target/generated-sources/emf/ +└── com/example/mymodel/ + ├── MymodelPackage.java # EPackage interface + ├── MymodelFactory.java # EFactory interface + ├── MyClass.java # EClass interfaces + ├── impl/ + │ ├── MymodelPackageImpl.java # EPackage implementation + │ ├── MymodelFactoryImpl.java # EFactory implementation + │ └── MyClassImpl.java # EClass implementations + ├── util/ + │ ├── MymodelSwitch.java # Type switch + │ ├── MymodelAdapterFactory.java + │ ├── MymodelResourceImpl.java + │ ├── MymodelResourceFactoryImpl.java + │ ├── MymodelValidator.java + │ └── MymodelXMLProcessor.java + └── configuration/ # OSGi components (if osgiCompatible=true) + ├── MymodelEPackageConfigurator.java + └── MymodelConfigurationComponent.java +``` + diff --git a/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/EmfGenerateMojo.java b/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/EmfGenerateMojo.java index 857b127..ebed534 100644 --- a/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/EmfGenerateMojo.java +++ b/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/EmfGenerateMojo.java @@ -15,11 +15,13 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -128,7 +130,8 @@ public class EmfGenerateMojo extends AbstractMojo { private MavenProject project; /** - * All projects in the reactor (for resolving reactor dependencies before they are packaged). + * All projects in the reactor (for resolving reactor dependencies before they + * are packaged). */ @Parameter(defaultValue = "${reactorProjects}", readonly = true) private java.util.List reactorProjects; @@ -240,6 +243,14 @@ public class EmfGenerateMojo extends AbstractMojo { @Parameter(property = "emf.copyrightText") private String copyrightText; + /** + * Whether to include the generated GenModel file in the JAR resources. This + * allows dependent modules to load the GenPackage from the JAR. Default is + * false. + */ + @Parameter(property = "emf.includeGenModelInJar", defaultValue = "false") + private boolean includeGenModelInJar; + @Override public void execute() throws MojoExecutionException, MojoFailureException { // Determine which mode to use @@ -372,9 +383,39 @@ private Optional generateFromEcore(File baseDir) { logGenModelInfo(genModel); + // Optionally save the GenModel to resources for inclusion in JAR + if (includeGenModelInJar) { + saveGenModelToResources(genModel, ePackage); + } + + // Collect referenced package names (to delete their generated files after + // generation) + List referencedPackageNames = new ArrayList<>(); + for (int i = 1; i < genModel.getGenPackages().size(); i++) { + GenPackage refPkg = genModel.getGenPackages().get(i); + String pkgPath = refPkg.getBasePackage().replace('.', '/') + "/" + refPkg.getPackageName(); + referencedPackageNames.add(pkgPath); + getLog().info("Will delete generated files for referenced package: " + pkgPath); + } + // Use OSGi templates if OSGi compatibility is enabled boolean useOsgiTemplates = genModel.isOSGiCompatible(); - return runGenerator(genModel, ecoreFile.getAbsolutePath(), useOsgiTemplates); + Optional result = runGenerator(genModel, ecoreFile.getAbsolutePath(), useOsgiTemplates); + + // Delete generated files for referenced packages (they come from dependency + // JARs) + if (!result.isPresent()) { + File outputDir = new File(project.getBasedir(), outputDirectory); + for (String pkgPath : referencedPackageNames) { + File pkgDir = new File(outputDir, pkgPath); + if (pkgDir.exists()) { + deleteDirectory(pkgDir); + getLog().info("Deleted generated files for referenced package: " + pkgDir.getAbsolutePath()); + } + } + } + + return result; } finally { resourceSet.getResources().forEach(Resource::unload); resourceSet.getResources().clear(); @@ -498,8 +539,9 @@ private void configureGenerator(Generator gen, boolean useOsgiTemplates) { } /** - * Adds usedGenPackages to the GenModel for external packages referenced by the model. - * This enables resolution of cross-package references during code generation. + * Adds usedGenPackages to the GenModel for external packages referenced by the + * model. This enables resolution of cross-package references during code + * generation. */ private void addUsedGenPackagesFromDependencies(GenModel genModel) { // Collect all nsURIs of packages already in the GenModel @@ -526,7 +568,8 @@ private void addUsedGenPackagesFromDependencies(GenModel genModel) { if (externalGenPackage != null) { if (!genModel.getUsedGenPackages().contains(externalGenPackage)) { genModel.getUsedGenPackages().add(externalGenPackage); - getLog().info("Added usedGenPackage for external reference: " + externalGenPackage.getPackageName() + " (" + nsURI + ")"); + getLog().info("Added usedGenPackage for external reference: " + externalGenPackage.getPackageName() + + " (" + nsURI + ")"); } } else { getLog().warn("No GenPackage found for referenced external package: " + nsURI); @@ -537,7 +580,8 @@ private void addUsedGenPackagesFromDependencies(GenModel genModel) { /** * Collects nsURIs of external packages referenced by the given EPackage. */ - private void collectReferencedExternalNsURIs(EPackage ePackage, Set ownPackageNsURIs, Set referencedNsURIs) { + private void collectReferencedExternalNsURIs(EPackage ePackage, Set ownPackageNsURIs, + Set referencedNsURIs) { for (EClassifier classifier : ePackage.getEClassifiers()) { if (classifier instanceof EClass eClass) { // Check super types @@ -557,7 +601,8 @@ private void collectReferencedExternalNsURIs(EPackage ePackage, Set ownP } /** - * Adds the nsURI of the given package if it's an external (non-EMF core) package. + * Adds the nsURI of the given package if it's an external (non-EMF core) + * package. */ private void addExternalNsURI(EPackage ePackage, Set ownPackageNsURIs, Set referencedNsURIs) { if (ePackage != null) { @@ -610,33 +655,38 @@ private GenModel createGenModel(EPackage ePackage, String projectName, ResourceS genModel::setRootExtendsInterface); applyStringOption(genModel, ePackage, "copyrightText", copyrightText, genModel::setCopyrightText); - // Handle referenced packages + // Resolve all proxies in the EPackage to ensure we get the correct EPackage + // instances + // from the registry (for referenced packages like bi and cg) + EcoreUtil.resolveAll(ePackage); + + // Find referenced external packages Set referencedPackages = findReferencedExternalPackages(ePackage); - genModel.initialize(Collections.singletonList(ePackage)); - - for (EPackage refPackage : referencedPackages) { - GenModel refGenModel = GenModelFactory.eINSTANCE.createGenModel(); - refGenModel.initialize(Collections.singletonList(refPackage)); - refGenModel.setModelName(capitalize(refPackage.getName())); - - URI refGenModelUri = URI.createURI("synthetic:/" + refPackage.getName() + ".genmodel"); - Resource refGenModelResource = resourceSet.createResource(refGenModelUri); - refGenModelResource.getContents().add(refGenModel); - - if (!refGenModel.getGenPackages().isEmpty()) { - GenPackage refGenPackage = refGenModel.getGenPackages().get(0); - String derivedRefBasePackage = deriveBasePackage(refPackage.getNsURI()); - if (derivedRefBasePackage != null && !derivedRefBasePackage.isEmpty()) { - refGenPackage.setBasePackage(derivedRefBasePackage); - } - refGenPackage.setPrefix(capitalize(refPackage.getName())); - genModel.getUsedGenPackages().add(refGenPackage); - getLog().info( - "Added usedGenPackage: " + refGenPackage.getPackageName() + " (" + refPackage.getNsURI() + ")"); - } + + // Initialize GenModel with ALL packages (main + referenced) in the same + // GenModel + // This is required because findGenClassifier needs all packages in the same + // GenModel + // to correctly resolve type references for code generation + List allPackages = new ArrayList<>(); + allPackages.add(ePackage); + allPackages.addAll(referencedPackages); + genModel.initialize(allPackages); + + // Configure referenced GenPackages (indices 1+) so they don't generate code + // but are still available for findGenClassifier + for (int i = 1; i < genModel.getGenPackages().size(); i++) { + GenPackage refGenPackage = genModel.getGenPackages().get(i); + EPackage refEPackage = refGenPackage.getEcorePackage(); + configureGenPackageFromAnnotations(refGenPackage, refEPackage); + getLog().info("Configured referenced GenPackage (no code gen): " + refGenPackage.getPackageName() + " (" + + refEPackage.getNsURI() + ")"); } - // Create resource for main GenModel + // Resolve all references in the GenModel + EcoreUtil.resolveAll(genModel); + + // Create resource for GenModel URI genModelUri = URI .createURI("platform:/resource/" + projectName + "/model/" + ePackage.getName() + ".genmodel"); Resource genModelResource = resourceSet.createResource(genModelUri); @@ -822,6 +872,62 @@ private String capitalize(String s) { return Character.toUpperCase(s.charAt(0)) + s.substring(1); } + /** + * Configures a GenPackage from EPackage GenModel annotations. Sets basePackage, + * prefix, and fileExtensions based on annotations or derived values. + */ + private void configureGenPackageFromAnnotations(GenPackage genPackage, EPackage ePackage) { + // basePackage + String basePackage = getGenModelAnnotation(ePackage, "basePackage"); + if (basePackage == null || basePackage.isEmpty()) { + basePackage = deriveBasePackage(ePackage.getNsURI()); + } + if (basePackage != null && !basePackage.isEmpty()) { + genPackage.setBasePackage(basePackage); + } + + // prefix + String prefix = getGenModelAnnotation(ePackage, "prefix"); + if (prefix == null || prefix.isEmpty()) { + prefix = capitalize(ePackage.getName()); + } + genPackage.setPrefix(prefix); + + // fileExtensions + String fileExt = getGenModelAnnotation(ePackage, "fileExtensions"); + if (fileExt != null && !fileExt.isEmpty()) { + genPackage.setFileExtensions(fileExt); + } + } + + /** + * Registers an EPackage in the registries and creates a corresponding + * GenPackage. + * + * @param resourceSet the resource set + * @param ePackage the EPackage to register + * @param source description of where the package came from (for logging) + */ + private void registerEPackage(ResourceSet resourceSet, EPackage ePackage, String source) { + String nsURI = ePackage.getNsURI(); + if (nsURI == null || EPackage.Registry.INSTANCE.containsKey(nsURI)) { + return; + } + + EPackage.Registry.INSTANCE.put(nsURI, ePackage); + resourceSet.getPackageRegistry().put(nsURI, ePackage); + getLog().info("Registered EPackage" + source + ": " + ePackage.getName() + " (" + nsURI + ")"); + + // Also create and register a GenPackage if GenModel annotations are present + if (!genPackageRegistry.containsKey(nsURI)) { + GenPackage genPackage = createGenPackageFromEcore(resourceSet, ePackage); + if (genPackage != null) { + genPackageRegistry.put(nsURI, genPackage); + getLog().info("Created GenPackage" + source + ": " + genPackage.getPackageName() + " (" + nsURI + ")"); + } + } + } + private void loadModelsFromDependencies(ResourceSet resourceSet) { // Build a map of reactor projects by groupId:artifactId for quick lookup Map reactorProjectMap = new HashMap<>(); @@ -833,8 +939,8 @@ private void loadModelsFromDependencies(ResourceSet resourceSet) { } // Collect model files from all dependencies - java.util.List ecoreFiles = new java.util.ArrayList<>(); - java.util.List genmodelFiles = new java.util.ArrayList<>(); + List ecoreFiles = new ArrayList<>(); + List genmodelFiles = new ArrayList<>(); for (Artifact artifact : project.getArtifacts()) { String key = artifact.getGroupId() + ":" + artifact.getArtifactId(); @@ -866,8 +972,8 @@ private void loadModelsFromDependencies(ResourceSet resourceSet) { /** * Collect model files from a reactor project's resource directories. */ - private void collectModelFilesFromReactorProject(MavenProject reactorProject, - java.util.List ecoreFiles, java.util.List genmodelFiles) { + private void collectModelFilesFromReactorProject(MavenProject reactorProject, List ecoreFiles, + List genmodelFiles) { for (org.apache.maven.model.Resource resource : reactorProject.getResources()) { File resourceDir = new File(resource.getDirectory()); if (resourceDir.exists() && resourceDir.isDirectory()) { @@ -879,8 +985,8 @@ private void collectModelFilesFromReactorProject(MavenProject reactorProject, /** * Collect model files from a JAR dependency. */ - private void collectModelFilesFromJar(File jarFile, ResourceSet resourceSet, - java.util.List ecoreFiles, java.util.List genmodelFiles) { + private void collectModelFilesFromJar(File jarFile, ResourceSet resourceSet, List ecoreFiles, + List genmodelFiles) { try (JarFile jar = new JarFile(jarFile)) { Enumeration entries = jar.entries(); while (entries.hasMoreElements()) { @@ -902,8 +1008,7 @@ private void collectModelFilesFromJar(File jarFile, ResourceSet resourceSet, /** * Recursively collect model files from a directory. */ - private void collectModelFilesFromDirectory(File dir, - java.util.List ecoreFiles, java.util.List genmodelFiles) { + private void collectModelFilesFromDirectory(File dir, List ecoreFiles, List genmodelFiles) { File[] files = dir.listFiles(); if (files == null) { return; @@ -925,9 +1030,8 @@ private void collectModelFilesFromDirectory(File dir, * Check if a path should be processed (excludes EMF internal paths). */ private boolean isModelPath(String path) { - return !path.contains("org/eclipse/emf/ecore/") - && !path.contains("org/eclipse/emf/codegen/") - && !path.contains("META-INF/"); + return !path.contains("org/eclipse/emf/ecore/") && !path.contains("org/eclipse/emf/codegen/") + && !path.contains("META-INF/"); } private void loadEcoreFromFile(ResourceSet resourceSet, File ecoreFile) { @@ -937,12 +1041,7 @@ private void loadEcoreFromFile(ResourceSet resourceSet, File ecoreFile) { if (ecoreResource != null && !ecoreResource.getContents().isEmpty()) { for (org.eclipse.emf.ecore.EObject obj : ecoreResource.getContents()) { if (obj instanceof EPackage ePackage) { - String nsURI = ePackage.getNsURI(); - if (nsURI != null && !EPackage.Registry.INSTANCE.containsKey(nsURI)) { - EPackage.Registry.INSTANCE.put(nsURI, ePackage); - resourceSet.getPackageRegistry().put(nsURI, ePackage); - getLog().info("Registered EPackage: " + ePackage.getName() + " (" + nsURI + ")"); - } + registerEPackage(resourceSet, ePackage, ""); } } } @@ -951,6 +1050,67 @@ private void loadEcoreFromFile(ResourceSet resourceSet, File ecoreFile) { } } + /** + * Saves the GenModel to the resources directory for inclusion in the JAR. The + * genmodel will be saved alongside the ecore file in the model directory. + */ + private void saveGenModelToResources(GenModel genModel, EPackage ePackage) { + try { + // Determine the target path - alongside the ecore file + File ecoreParentDir = ecoreFile.getParentFile(); + String genModelFileName = ePackage.getName() + ".genmodel"; + File genModelFile = new File(ecoreParentDir, genModelFileName); + + // Create the resource and save + URI genModelUri = URI.createFileURI(genModelFile.getAbsolutePath()); + Resource genModelResource = genModel.eResource(); + + // If the genmodel is in a synthetic resource, we need to move it to the file + // resource + if (genModelResource.getURI().toString().startsWith("synthetic:") + || genModelResource.getURI().toString().startsWith("platform:")) { + ResourceSet resourceSet = genModelResource.getResourceSet(); + Resource fileResource = resourceSet.createResource(genModelUri); + fileResource.getContents().add(genModel); + genModelResource = fileResource; + } else { + genModelResource.setURI(genModelUri); + } + + genModelResource.save(Collections.emptyMap()); + getLog().info("Saved GenModel for JAR inclusion: " + genModelFile.getAbsolutePath()); + } catch (IOException e) { + getLog().warn("Could not save GenModel to resources: " + e.getMessage()); + } + } + + /** + * Creates a GenPackage from an EPackage using GenModel annotations. + */ + private GenPackage createGenPackageFromEcore(ResourceSet resourceSet, EPackage ePackage) { + try { + GenModel genModel = GenModelFactory.eINSTANCE.createGenModel(); + genModel.initialize(Collections.singletonList(ePackage)); + genModel.setModelName(capitalize(ePackage.getName())); + + // Create a synthetic resource for the GenModel + URI genModelUri = URI.createURI("synthetic:/" + ePackage.getName() + ".genmodel"); + Resource genModelResource = resourceSet.createResource(genModelUri); + genModelResource.getContents().add(genModel); + + if (!genModel.getGenPackages().isEmpty()) { + GenPackage genPackage = genModel.getGenPackages().get(0); + configureGenPackageFromAnnotations(genPackage, ePackage); + genPackage.setEcorePackage(ePackage); + EcoreUtil.resolveAll(genModel); + return genPackage; + } + } catch (Exception e) { + getLog().debug("Could not create GenPackage from Ecore: " + ePackage.getName() + " - " + e.getMessage()); + } + return null; + } + private void loadGenModelFromFile(ResourceSet resourceSet, File genmodelFile) { try { URI genmodelUri = URI.createFileURI(genmodelFile.getAbsolutePath()); @@ -963,7 +1123,8 @@ private void loadGenModelFromFile(ResourceSet resourceSet, File genmodelFile) { EPackage ePackage = genPackage.getEcorePackage(); if (ePackage != null && ePackage.getNsURI() != null) { genPackageRegistry.put(ePackage.getNsURI(), genPackage); - getLog().info("Registered GenPackage: " + genPackage.getPackageName() + " (" + ePackage.getNsURI() + ")"); + getLog().info("Registered GenPackage: " + genPackage.getPackageName() + " (" + + ePackage.getNsURI() + ")"); } } } @@ -981,12 +1142,7 @@ private void loadEcoreFromJar(ResourceSet resourceSet, File jarFile, String ecor if (ecoreResource != null && !ecoreResource.getContents().isEmpty()) { for (org.eclipse.emf.ecore.EObject obj : ecoreResource.getContents()) { if (obj instanceof EPackage ePackage) { - String nsURI = ePackage.getNsURI(); - if (nsURI != null && !EPackage.Registry.INSTANCE.containsKey(nsURI)) { - EPackage.Registry.INSTANCE.put(nsURI, ePackage); - resourceSet.getPackageRegistry().put(nsURI, ePackage); - getLog().info("Registered EPackage: " + ePackage.getName() + " (" + nsURI + ")"); - } + registerEPackage(resourceSet, ePackage, " from JAR"); } } } @@ -1007,7 +1163,8 @@ private void loadGenModelFromJar(ResourceSet resourceSet, File jarFile, String g EPackage ePackage = genPackage.getEcorePackage(); if (ePackage != null && ePackage.getNsURI() != null) { genPackageRegistry.put(ePackage.getNsURI(), genPackage); - getLog().info("Registered GenPackage: " + genPackage.getPackageName() + " (" + ePackage.getNsURI() + ")"); + getLog().info("Registered GenPackage: " + genPackage.getPackageName() + " (" + + ePackage.getNsURI() + ")"); } } } @@ -1033,4 +1190,19 @@ private void printDiagnostic(Diagnostic diagnostic, String prefix) { printDiagnostic(child, prefix + " "); } } + + /** + * Recursively deletes a directory and all its contents. + */ + private void deleteDirectory(File dir) { + if (dir.isDirectory()) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + deleteDirectory(file); + } + } + } + dir.delete(); + } }