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();
+ }
}