diff --git a/emf/codegen.maven.example/ecore.annotated/model/catalog.ecore b/emf/codegen.maven.example/ecore.annotated/model/catalog.ecore new file mode 100644 index 0000000..44bfe3b --- /dev/null +++ b/emf/codegen.maven.example/ecore.annotated/model/catalog.ecore @@ -0,0 +1,74 @@ + + + +
+
+
+
+
+ + + +
+ + + +
+ + + + +
+ + + + + +
+ + + +
+ + + + +
+ + + + +
+ + + + + +
+ + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + diff --git a/emf/codegen.maven.example/ecore.annotated/pom.xml b/emf/codegen.maven.example/ecore.annotated/pom.xml new file mode 100644 index 0000000..fcc42ad --- /dev/null +++ b/emf/codegen.maven.example/ecore.annotated/pom.xml @@ -0,0 +1,110 @@ + + + + 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.annotated + jar + + Daanse EMF Codegen Example - Ecore Annotated + Ecore model using GenModel annotations for code generation + settings + + + + 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/catalog.ecore + 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.copyright/model/vehicle.ecore b/emf/codegen.maven.example/ecore.copyright/model/vehicle.ecore new file mode 100644 index 0000000..d95ae49 --- /dev/null +++ b/emf/codegen.maven.example/ecore.copyright/model/vehicle.ecore @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/ecore.copyright/pom.xml b/emf/codegen.maven.example/ecore.copyright/pom.xml new file mode 100644 index 0000000..2e334d3 --- /dev/null +++ b/emf/codegen.maven.example/ecore.copyright/pom.xml @@ -0,0 +1,107 @@ + + + + 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/model/extended.ecore b/emf/codegen.maven.example/ecore.dependencies/model/extended.ecore new file mode 100644 index 0000000..30cb064 --- /dev/null +++ b/emf/codegen.maven.example/ecore.dependencies/model/extended.ecore @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/ecore.dependencies/pom.xml b/emf/codegen.maven.example/ecore.dependencies/pom.xml new file mode 100644 index 0000000..7c25708 --- /dev/null +++ b/emf/codegen.maven.example/ecore.dependencies/pom.xml @@ -0,0 +1,106 @@ + + + + 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.dependencies + jar + + Daanse EMF Codegen Example - Ecore with Dependencies + Ecore model with cross-module dependencies referencing another + Ecore model + + + + org.eclipse.emf + org.eclipse.emf.common + ${emf.common.version} + + + org.eclipse.emf + org.eclipse.emf.ecore + ${emf.ecore.version} + + + + org.eclipse.daanse + + org.eclipse.daanse.tooling.emf.codegen.maven.example.ecore.simple + ${project.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/extended.ecore + org.eclipse.daanse.example + 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.fileext/model/config.ecore b/emf/codegen.maven.example/ecore.fileext/model/config.ecore new file mode 100644 index 0000000..f389de0 --- /dev/null +++ b/emf/codegen.maven.example/ecore.fileext/model/config.ecore @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/ecore.fileext/pom.xml b/emf/codegen.maven.example/ecore.fileext/pom.xml new file mode 100644 index 0000000..e7e3286 --- /dev/null +++ b/emf/codegen.maven.example/ecore.fileext/pom.xml @@ -0,0 +1,105 @@ + + + + 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/model/simple.ecore b/emf/codegen.maven.example/ecore.noosgi/model/simple.ecore new file mode 100644 index 0000000..96a8b4a --- /dev/null +++ b/emf/codegen.maven.example/ecore.noosgi/model/simple.ecore @@ -0,0 +1,32 @@ + + + +
+
+
+ + + +
+ + + +
+ + + + +
+ + + + + +
+ + + + + diff --git a/emf/codegen.maven.example/ecore.noosgi/pom.xml b/emf/codegen.maven.example/ecore.noosgi/pom.xml new file mode 100644 index 0000000..243c54a --- /dev/null +++ b/emf/codegen.maven.example/ecore.noosgi/pom.xml @@ -0,0 +1,108 @@ + + + + 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.noosgi + jar + + Daanse EMF Codegen Example - Ecore No OSGi + Ecore model with osgiCompatible=false - no OSGi service + components generated + + + + 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/simple.ecore + false + 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.simple/pom.xml b/emf/codegen.maven.example/ecore.simple/pom.xml new file mode 100644 index 0000000..c4016e9 --- /dev/null +++ b/emf/codegen.maven.example/ecore.simple/pom.xml @@ -0,0 +1,100 @@ + + + + 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.simple + jar + + Daanse EMF Codegen Example - Ecore Simple + Simple standalone Ecore model with Person and Address classes + + + + 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 + + + src/main/resources/model/model.ecore + org.eclipse.daanse.example + 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.simple/src/main/resources/model/model.ecore b/emf/codegen.maven.example/ecore.simple/src/main/resources/model/model.ecore new file mode 100644 index 0000000..7891829 --- /dev/null +++ b/emf/codegen.maven.example/ecore.simple/src/main/resources/model/model.ecore @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/emf/codegen.maven.example/ecore.suppress/model/sensor.ecore b/emf/codegen.maven.example/ecore.suppress/model/sensor.ecore new file mode 100644 index 0000000..0e0f51e --- /dev/null +++ b/emf/codegen.maven.example/ecore.suppress/model/sensor.ecore @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/ecore.suppress/pom.xml b/emf/codegen.maven.example/ecore.suppress/pom.xml new file mode 100644 index 0000000..02a87d2 --- /dev/null +++ b/emf/codegen.maven.example/ecore.suppress/pom.xml @@ -0,0 +1,107 @@ + + + + 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/pom.xml b/emf/codegen.maven.example/genmodel.dependencies/pom.xml new file mode 100644 index 0000000..87a1a1a --- /dev/null +++ b/emf/codegen.maven.example/genmodel.dependencies/pom.xml @@ -0,0 +1,105 @@ + + + + 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.genmodel.dependencies + jar + + Daanse EMF Codegen Example - GenModel with Dependencies + GenModel-based code generation with cross-module references + + + + org.eclipse.emf + org.eclipse.emf.common + ${emf.common.version} + + + org.eclipse.emf + org.eclipse.emf.ecore + ${emf.ecore.version} + + + + org.eclipse.daanse + + org.eclipse.daanse.tooling.emf.codegen.maven.example.genmodel.simple + ${project.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 + + + src/main/resources/model/borrowing.genmodel + 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/main/resources/model/borrowing.ecore b/emf/codegen.maven.example/genmodel.dependencies/src/main/resources/model/borrowing.ecore new file mode 100644 index 0000000..8708129 --- /dev/null +++ b/emf/codegen.maven.example/genmodel.dependencies/src/main/resources/model/borrowing.ecore @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/genmodel.dependencies/src/main/resources/model/borrowing.genmodel b/emf/codegen.maven.example/genmodel.dependencies/src/main/resources/model/borrowing.genmodel new file mode 100644 index 0000000..15d4b76 --- /dev/null +++ b/emf/codegen.maven.example/genmodel.dependencies/src/main/resources/model/borrowing.genmodel @@ -0,0 +1,23 @@ + + + borrowing.ecore + + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/genmodel.simple/pom.xml b/emf/codegen.maven.example/genmodel.simple/pom.xml new file mode 100644 index 0000000..4c0e28a --- /dev/null +++ b/emf/codegen.maven.example/genmodel.simple/pom.xml @@ -0,0 +1,98 @@ + + + + 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.genmodel.simple + jar + + Daanse EMF Codegen Example - GenModel Simple + Simple standalone GenModel-based code generation + + + + 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 + + + src/main/resources/model/library.genmodel + 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.simple/src/main/resources/model/library.ecore b/emf/codegen.maven.example/genmodel.simple/src/main/resources/model/library.ecore new file mode 100644 index 0000000..d4e9e68 --- /dev/null +++ b/emf/codegen.maven.example/genmodel.simple/src/main/resources/model/library.ecore @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/genmodel.simple/src/main/resources/model/library.genmodel b/emf/codegen.maven.example/genmodel.simple/src/main/resources/model/library.genmodel new file mode 100644 index 0000000..35a14f0 --- /dev/null +++ b/emf/codegen.maven.example/genmodel.simple/src/main/resources/model/library.genmodel @@ -0,0 +1,28 @@ + + + library.ecore + + + + + + + + + + + + + + + + + + + diff --git a/emf/codegen.maven.example/pom.xml b/emf/codegen.maven.example/pom.xml new file mode 100644 index 0000000..e2ec31d --- /dev/null +++ b/emf/codegen.maven.example/pom.xml @@ -0,0 +1,62 @@ + + + + 4.0.0 + + + org.eclipse.daanse + org.eclipse.daanse.tooling.emf + 0.0.1-SNAPSHOT + + + org.eclipse.daanse.tooling.emf.codegen.maven.example + pom + + Eclipse Daanse EMF Code Generator Examples (Ecore) + Example projects demonstrating EMF code generation from ecore + files + + + ecore.simple + ecore.dependencies + ecore.annotated + ecore.noosgi + genmodel.simple + genmodel.dependencies + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + org.sonatype.central + central-publishing-maven-plugin + true + + true + + + + + + diff --git a/emf/codegen.maven/pom.xml b/emf/codegen.maven/pom.xml new file mode 100644 index 0000000..d03a32f --- /dev/null +++ b/emf/codegen.maven/pom.xml @@ -0,0 +1,139 @@ + + + + 4.0.0 + + + org.eclipse.daanse + org.eclipse.daanse.tooling.emf + 0.0.1-SNAPSHOT + + + org.eclipse.daanse.tooling.emf.codegen.maven + maven-plugin + + Eclipse Daanse EMF Code Generator Maven Plugin + Maven plugin for generating OSGi-compatible EMF model code from + ecore and genmodel files + + + 3.9.6 + 3.11.0 + + + + + + org.apache.maven + maven-plugin-api + ${maven.version} + provided + + + org.apache.maven + maven-core + ${maven.version} + provided + + + org.apache.maven.plugin-tools + maven-plugin-annotations + ${maven-plugin-tools.version} + provided + + + + + org.eclipse.emf + org.eclipse.emf.common + ${emf.common.version} + + + org.eclipse.emf + org.eclipse.emf.ecore + ${emf.ecore.version} + + + org.eclipse.emf + org.eclipse.emf.ecore.xmi + ${emf.xmi.version} + + + org.eclipse.emf + org.eclipse.emf.codegen + ${emf.codegen.version} + + + org.eclipse.emf + org.eclipse.emf.codegen.ecore + ${emf.codegen.ecore.version} + + + + + org.eclipse.platform + org.eclipse.core.runtime + 3.34.100 + + + + + org.eclipse.fennec.emf + org.eclipse.fennec.emf.osgi.codegen + 1.0.0-SNAPSHOT + + + org.eclipse.uml2 + org.eclipse.uml2.codegen.ecore + + + + + + + biz.aQute.bnd + biz.aQute.bndlib + 7.1.0 + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + ${maven-plugin-tools.version} + + emf + + + + default-descriptor + process-classes + + + help-goal + + helpmojo + + + + + + + + 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 new file mode 100644 index 0000000..857b127 --- /dev/null +++ b/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/EmfGenerateMojo.java @@ -0,0 +1,1036 @@ +/* + * 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 + * + * Contributors: + * SmartCity Jena - initial + * Stefan Bischof (bipolis.org) - initial + */ +package org.eclipse.daanse.tooling.emf.codegen; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.eclipse.emf.codegen.ecore.generator.Generator; +import org.eclipse.emf.codegen.ecore.genmodel.GenModel; +import org.eclipse.emf.codegen.ecore.genmodel.GenModelFactory; +import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage; +import org.eclipse.emf.codegen.ecore.genmodel.GenPackage; +import org.eclipse.emf.codegen.ecore.genmodel.generator.GenBaseGeneratorAdapter; +import org.eclipse.emf.codegen.ecore.genmodel.impl.GenModelFactoryImpl; +import org.eclipse.emf.codegen.ecore.genmodel.impl.GenModelPackageImpl; +import org.eclipse.emf.codegen.util.CodeGenUtil; +import org.eclipse.emf.common.util.Diagnostic; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.Diagnostician; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; +import org.eclipse.fennec.emf.osgi.codegen.adapter.BNDGeneratorAdapterFactory; + +/** + * Unified Maven Mojo for generating EMF model code from either a GenModel or + * Ecore file. + * + *

+ * The mojo automatically detects which mode to use based on which file + * parameter is set: + *

+ *
    + *
  • If {@code genmodelFile} is set: Uses the existing GenModel file
  • + *
  • If {@code ecoreFile} is set: Creates a GenModel programmatically from the + * Ecore
  • + *
  • If both are set: GenModel takes precedence
  • + *
+ * + *

+ * Example usage with GenModel: + *

+ * + *
{@code
+ * 
+ *   model/mymodel.genmodel
+ *   target/generated-sources/emf
+ * 
+ * }
+ * + *

+ * Example usage with Ecore (GenModel settings from annotations or Maven + * config): + *

+ * + *
{@code
+ * 
+ *   model/mymodel.ecore
+ *   com.example.model
+ *   target/generated-sources/emf
+ * 
+ * }
+ * + *

+ * Example usage with Ecore using GenModel annotations in the ecore file: + *

+ * + *
{@code
+ * 
+ *   model/mymodel.ecore
+ *   target/generated-sources/emf
+ *   
+ * 
+ * }
+ */ +@Mojo(name = "generate", defaultPhase = LifecyclePhase.GENERATE_SOURCES, requiresDependencyResolution = ResolutionScope.COMPILE) +public class EmfGenerateMojo extends AbstractMojo { + + public static final String ORIGINAL_GEN_MODEL_PATH = "originalGenModelPath"; + public static final String ORIGINAL_GEN_MODEL_PATHS_EXTRA = "originalGenModelPathsExtra"; + public static final String INCLUDE_GEN_MODEL_FOLDER = "includeGenModelFolder"; + public static final String GENMODEL_ANNOTATION_SOURCE = "http://www.eclipse.org/emf/2002/GenModel"; + + /** + * Registry mapping EPackage nsURI to GenPackage for dependency resolution. + */ + private final Map genPackageRegistry = new HashMap<>(); + + @Parameter(defaultValue = "${project}", readonly = true, required = true) + private MavenProject project; + + /** + * All projects in the reactor (for resolving reactor dependencies before they are packaged). + */ + @Parameter(defaultValue = "${reactorProjects}", readonly = true) + private java.util.List reactorProjects; + + // ========== Input File Parameters ========== + + /** + * Path to the genmodel file. If set, generation uses the existing GenModel. + * Takes precedence over ecoreFile if both are set. + */ + @Parameter(property = "emf.genmodelFile") + private File genmodelFile; + + /** + * Path to the ecore file. If set (and genmodelFile is not set), a GenModel is + * created programmatically. + */ + @Parameter(property = "emf.ecoreFile") + private File ecoreFile; + + // ========== Output Parameters ========== + + /** + * Output directory for generated code, relative to the project base directory. + */ + @Parameter(property = "emf.outputDirectory", defaultValue = "target/generated-sources/emf") + private String outputDirectory; + + /** + * Optional location where the genmodel will be included in the built artifact. + * Used for OSGi capability generation. + */ + @Parameter(property = "emf.genmodelIncludeLocation") + private String genmodelIncludeLocation; + + // ========== GenModel Configuration (for Ecore mode) ========== + + /** + * Base package for generated code. If not specified, derived from EPackage + * nsURI or GenModel annotation in ecore file. + */ + @Parameter(property = "emf.basePackage") + private String basePackage; + + /** + * Prefix for generated class names. If not specified, derived from EPackage + * name or GenModel annotation in ecore file. + */ + @Parameter(property = "emf.prefix") + private String prefix; + + /** + * File extension for model resources. + */ + @Parameter(property = "emf.fileExtension") + private String fileExtension; + + /** + * Whether to generate OSGi-compatible code. + */ + @Parameter(property = "emf.osgiCompatible", defaultValue = "true") + private boolean osgiCompatible; + + /** + * Whether to suppress generation of interfaces (only impl classes). + */ + @Parameter(property = "emf.suppressInterfaces", defaultValue = "false") + private boolean suppressInterfaces; + + /** + * Whether to suppress EMF model types and use Java native types. + */ + @Parameter(property = "emf.suppressEMFTypes", defaultValue = "false") + private boolean suppressEMFTypes; + + /** + * Whether to suppress EMF metadata. + */ + @Parameter(property = "emf.suppressEMFMetaData", defaultValue = "false") + private boolean suppressEMFMetaData; + + /** + * Whether to suppress GenModel annotations in generated code. + */ + @Parameter(property = "emf.suppressGenModelAnnotations", defaultValue = "false") + private boolean suppressGenModelAnnotations; + + /** + * Whether to make constructors public. + */ + @Parameter(property = "emf.publicConstructors", defaultValue = "false") + private boolean publicConstructors; + + /** + * Root class that generated model objects extend. + */ + @Parameter(property = "emf.rootExtendsClass") + private String rootExtendsClass; + + /** + * Root interface that generated model objects implement. + */ + @Parameter(property = "emf.rootExtendsInterface") + private String rootExtendsInterface; + + /** + * Copyright text to include in generated files. + */ + @Parameter(property = "emf.copyrightText") + private String copyrightText; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + // Determine which mode to use + boolean useGenmodel = genmodelFile != null && genmodelFile.exists(); + boolean useEcore = ecoreFile != null && ecoreFile.exists(); + + if (!useGenmodel && !useEcore) { + // Check if files were specified but don't exist + if (genmodelFile != null && !genmodelFile.exists()) { + throw new MojoFailureException("GenModel file not found: " + genmodelFile.getAbsolutePath()); + } + if (ecoreFile != null && !ecoreFile.exists()) { + throw new MojoFailureException("Ecore file not found: " + ecoreFile.getAbsolutePath()); + } + throw new MojoFailureException("Either 'genmodelFile' or 'ecoreFile' must be specified. " + + "Use for existing GenModel files or for Ecore files."); + } + + File baseDir = project.getBasedir(); + File outputDir = new File(baseDir, outputDirectory); + outputDir.mkdirs(); + + Optional error; + if (useGenmodel) { + getLog().info("EMF Code Generator - Using GenModel mode"); + getLog().info(" GenModel: " + genmodelFile); + error = generateFromGenmodel(baseDir); + } else { + getLog().info("EMF Code Generator - Using Ecore mode"); + getLog().info(" Ecore: " + ecoreFile); + error = generateFromEcore(baseDir); + } + + if (error.isPresent()) { + throw new MojoFailureException(error.get()); + } + + project.addCompileSourceRoot(outputDir.getAbsolutePath()); + getLog().info("Added " + outputDir.getAbsolutePath() + " to compile source roots"); + } + + // ==================== GenModel Mode ==================== + + private Optional generateFromGenmodel(File baseDir) { + String projectName = project.getArtifactId(); + getLog().info("Running for genmodel " + genmodelFile + " in " + baseDir.getAbsolutePath()); + + ResourceSet resourceSet = new ResourceSetImpl(); + try { + configureEMF(resourceSet); + setupURIMapping(resourceSet, baseDir, projectName); + setupDependencyURIMappings(resourceSet); + + URI genModelUri = URI.createFileURI(genmodelFile.getAbsolutePath()); + getLog().info("Loading " + genModelUri.toString()); + + Resource resource = resourceSet.getResource(genModelUri, true); + if (!resource.getErrors().isEmpty()) { + return Optional.of("Error loading GenModel: " + resource.getErrors().get(0).toString()); + } + + GenModel genModel = (GenModel) resource.getContents().get(0); + getLog().info("Resolving all models"); + EcoreUtil.resolveAll(genModel); + + // Add usedGenPackages from dependencies based on referenced external packages + addUsedGenPackagesFromDependencies(genModel); + + Diagnostic genModelDiagnostic = Diagnostician.INSTANCE.validate(genModel); + if (genModelDiagnostic.getSeverity() != Diagnostic.OK) { + getLog().error("GenModel is invalid"); + printDiagnostic(genModelDiagnostic, ""); + return Optional.of("GenModel validation failed"); + } + + // Override modelDirectory with configured outputDirectory + String modelDirectory = "/" + projectName + (outputDirectory.startsWith("/") ? "" : "/") + outputDirectory; + getLog().info("Setting modelDirectory: " + modelDirectory); + genModel.setModelDirectory(modelDirectory); + + // Use OSGi templates if the genmodel has OSGi compatibility enabled + boolean useOsgiTemplates = genModel.isOSGiCompatible(); + return runGenerator(genModel, genmodelFile.getAbsolutePath(), useOsgiTemplates); + } finally { + resourceSet.getResources().forEach(Resource::unload); + resourceSet.getResources().clear(); + } + } + + // ==================== Ecore Mode ==================== + + private Optional generateFromEcore(File baseDir) { + String projectName = project.getArtifactId(); + getLog().info("Running for ecore " + ecoreFile + " in " + baseDir.getAbsolutePath()); + + ResourceSet resourceSet = new ResourceSetImpl(); + try { + configureEMF(resourceSet); + setupURIMapping(resourceSet, baseDir, projectName); + + URI ecoreUri = URI.createFileURI(ecoreFile.getAbsolutePath()); + getLog().info("Loading Ecore from: " + ecoreUri); + + Resource ecoreResource = resourceSet.getResource(ecoreUri, true); + if (!ecoreResource.getErrors().isEmpty()) { + return Optional.of("Error loading Ecore: " + ecoreResource.getErrors().get(0).toString()); + } + + if (ecoreResource.getContents().isEmpty()) { + return Optional.of("Ecore file is empty"); + } + if (!(ecoreResource.getContents().get(0) instanceof EPackage)) { + return Optional.of("Ecore file does not contain an EPackage"); + } + + EPackage ePackage = (EPackage) ecoreResource.getContents().get(0); + getLog().info("Loaded EPackage: " + ePackage.getName() + " (" + ePackage.getNsURI() + ")"); + + GenModel genModel = createGenModel(ePackage, projectName, resourceSet); + + getLog().info("Resolving all models"); + EcoreUtil.resolveAll(genModel); + + Diagnostic genModelDiagnostic = Diagnostician.INSTANCE.validate(genModel); + if (genModelDiagnostic.getSeverity() == Diagnostic.ERROR) { + getLog().error("GenModel validation failed"); + printDiagnostic(genModelDiagnostic, ""); + return Optional.of("GenModel validation failed"); + } + + logGenModelInfo(genModel); + + // Use OSGi templates if OSGi compatibility is enabled + boolean useOsgiTemplates = genModel.isOSGiCompatible(); + return runGenerator(genModel, ecoreFile.getAbsolutePath(), useOsgiTemplates); + } finally { + resourceSet.getResources().forEach(Resource::unload); + resourceSet.getResources().clear(); + } + } + + // ==================== Shared Methods ==================== + + private void setupURIMapping(ResourceSet resourceSet, File baseDir, String projectName) { + URI platformResourceURI = URI.createURI("platform:/resource/" + projectName + "/"); + URI fileURI = URI.createFileURI(baseDir.getAbsolutePath() + "/"); + resourceSet.getURIConverter().getURIMap().put(platformResourceURI, fileURI); + org.eclipse.emf.ecore.resource.URIConverter.URI_MAP.put(platformResourceURI, fileURI); + getLog().info("URI mapping: " + platformResourceURI + " -> " + fileURI); + } + + private void setupDependencyURIMappings(ResourceSet resourceSet) { + // Set up URI mappings for Maven dependencies that contain model files + for (Artifact artifact : project.getArtifacts()) { + File file = artifact.getFile(); + if (file != null && file.getName().endsWith(".jar")) { + String artifactId = artifact.getArtifactId(); + try (JarFile jarFile = new JarFile(file)) { + // Check if the JAR contains model files + Enumeration entries = jarFile.entries(); + boolean hasModels = false; + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + if (name.endsWith(".genmodel") || name.endsWith(".ecore")) { + if (!name.contains("org/eclipse/emf/")) { + hasModels = true; + break; + } + } + } + + if (hasModels) { + // Set up URI mapping: platform:/resource/{artifactId}/ -> jar:file:{jarPath}!/ + URI platformURI = URI.createURI("platform:/resource/" + artifactId + "/"); + URI jarURI = URI.createURI("jar:file:" + file.getAbsolutePath() + "!/"); + resourceSet.getURIConverter().getURIMap().put(platformURI, jarURI); + org.eclipse.emf.ecore.resource.URIConverter.URI_MAP.put(platformURI, jarURI); + getLog().info("Dependency URI mapping: " + platformURI + " -> " + jarURI); + } + } catch (IOException e) { + getLog().debug("Could not scan JAR for model files: " + file.getName()); + } + } + } + } + + private Optional runGenerator(GenModel genModel, String originalPath, boolean useOsgiTemplates) { + Generator gen = new Generator(); + configureGenerator(gen, useOsgiTemplates); + gen.setInput(genModel); + + Map props = new HashMap<>(); + props.put(ORIGINAL_GEN_MODEL_PATH, originalPath); + props.put(ORIGINAL_GEN_MODEL_PATHS_EXTRA, Arrays.asList(originalPath)); + if (genmodelIncludeLocation != null) { + props.put(INCLUDE_GEN_MODEL_FOLDER, genmodelIncludeLocation); + } + gen.getOptions().data = new Object[] { props }; + + genModel.setCanGenerate(true); + genModel.setUpdateClasspath(false); + + getLog().info("Starting generator run"); + try { + Diagnostic diagnostic = gen.generate(genModel, GenBaseGeneratorAdapter.MODEL_PROJECT_TYPE, + CodeGenUtil.EclipseUtil.createMonitor(new MavenProgressMonitor(getLog()), 1)); + + getLog().info("Generation diagnostic severity: " + diagnostic.getSeverity()); + printDiagnostic(diagnostic, ""); + + if (diagnostic.getSeverity() == Diagnostic.ERROR) { + return Optional.of("Code generation failed: " + diagnostic.toString()); + } + } catch (Exception e) { + String message = "Error during code generation: " + e.getMessage(); + getLog().error(message, e); + return Optional.of(message); + } + + return Optional.empty(); + } + + private void configureEMF(ResourceSet resourceSet) { + GenModelPackageImpl.init(); + GenModelFactoryImpl.init(); + + // Clear the genPackageRegistry for a fresh run + genPackageRegistry.clear(); + + resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap().put(GenModelPackage.eCONTENT_TYPE, + new XMIResourceFactoryImpl()); + resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap().put("application/xmi", + new XMIResourceFactoryImpl()); + resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("genmodel", + new XMIResourceFactoryImpl()); + resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("ecore", + new EcoreResourceFactoryImpl()); + + // Always load models from dependencies for both GenModel and Ecore modes + loadModelsFromDependencies(resourceSet); + } + + private void configureGenerator(Generator gen, boolean useOsgiTemplates) { + // Always use the Fennec adapter factory for standalone (non-Eclipse) generation + // The OSGi-specific code generation is controlled by + // GenModel.isOSGiCompatible() + gen.getAdapterFactoryDescriptorRegistry().addDescriptor(GenModelPackage.eNS_URI, + BNDGeneratorAdapterFactory.DESCRIPTOR); + + if (useOsgiTemplates) { + getLog().info("Using Fennec templates with OSGi service components"); + } else { + getLog().info("Using Fennec templates (standard EMF output, no OSGi service components)"); + } + } + + /** + * 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 + Set ownPackageNsURIs = new HashSet<>(); + for (GenPackage genPackage : genModel.getGenPackages()) { + EPackage ePackage = genPackage.getEcorePackage(); + if (ePackage != null && ePackage.getNsURI() != null) { + ownPackageNsURIs.add(ePackage.getNsURI()); + } + } + + // Find all referenced external packages + Set referencedNsURIs = new HashSet<>(); + for (GenPackage genPackage : genModel.getGenPackages()) { + EPackage ePackage = genPackage.getEcorePackage(); + if (ePackage != null) { + collectReferencedExternalNsURIs(ePackage, ownPackageNsURIs, referencedNsURIs); + } + } + + // Add GenPackages from the registry for all referenced external packages + for (String nsURI : referencedNsURIs) { + GenPackage externalGenPackage = genPackageRegistry.get(nsURI); + if (externalGenPackage != null) { + if (!genModel.getUsedGenPackages().contains(externalGenPackage)) { + genModel.getUsedGenPackages().add(externalGenPackage); + getLog().info("Added usedGenPackage for external reference: " + externalGenPackage.getPackageName() + " (" + nsURI + ")"); + } + } else { + getLog().warn("No GenPackage found for referenced external package: " + nsURI); + } + } + } + + /** + * Collects nsURIs of external packages referenced by the given EPackage. + */ + private void collectReferencedExternalNsURIs(EPackage ePackage, Set ownPackageNsURIs, Set referencedNsURIs) { + for (EClassifier classifier : ePackage.getEClassifiers()) { + if (classifier instanceof EClass eClass) { + // Check super types + for (EClass superClass : eClass.getESuperTypes()) { + addExternalNsURI(superClass.getEPackage(), ownPackageNsURIs, referencedNsURIs); + } + + // Check structural features + for (EStructuralFeature feature : eClass.getEStructuralFeatures()) { + EClassifier featureType = feature.getEType(); + if (featureType != null) { + addExternalNsURI(featureType.getEPackage(), ownPackageNsURIs, referencedNsURIs); + } + } + } + } + } + + /** + * 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) { + String nsURI = ePackage.getNsURI(); + if (nsURI != null && !ownPackageNsURIs.contains(nsURI) && !isEMFCorePackage(nsURI)) { + referencedNsURIs.add(nsURI); + } + } + } + + // ==================== GenModel Creation (Ecore mode) ==================== + + private GenModel createGenModel(EPackage ePackage, String projectName, ResourceSet resourceSet) { + GenModel genModel = GenModelFactory.eINSTANCE.createGenModel(); + + logGenModelAnnotations(ePackage); + + String modelDirectory = "/" + projectName + (outputDirectory.startsWith("/") ? "" : "/") + outputDirectory; + genModel.setModelDirectory(modelDirectory); + genModel.setModelName(capitalize(ePackage.getName())); + genModel.setComplianceLevel(org.eclipse.emf.codegen.ecore.genmodel.GenJDKLevel.JDK170_LITERAL); + + // OSGi compatibility + boolean effectiveOsgiCompatible = osgiCompatible; + String osgiAnnotation = getGenModelAnnotation(ePackage, "oSGiCompatible"); + if (osgiAnnotation != null) { + effectiveOsgiCompatible = Boolean.parseBoolean(osgiAnnotation); + getLog().info("Using oSGiCompatible from Ecore annotation: " + effectiveOsgiCompatible); + } + if (effectiveOsgiCompatible) { + genModel.setOSGiCompatible(true); + genModel.setOperationReflection(true); + } + + // Boolean options + genModel.setSuppressInterfaces( + suppressInterfaces || getGenModelAnnotationBoolean(ePackage, "suppressInterfaces", false)); + genModel.setSuppressEMFTypes( + suppressEMFTypes || getGenModelAnnotationBoolean(ePackage, "suppressEMFTypes", false)); + genModel.setSuppressEMFMetaData( + suppressEMFMetaData || getGenModelAnnotationBoolean(ePackage, "suppressEMFMetaData", false)); + genModel.setSuppressGenModelAnnotations(suppressGenModelAnnotations + || getGenModelAnnotationBoolean(ePackage, "suppressGenModelAnnotations", false)); + genModel.setPublicConstructors( + publicConstructors || getGenModelAnnotationBoolean(ePackage, "publicConstructors", false)); + + // String options with fallback to annotations + applyStringOption(genModel, ePackage, "rootExtendsClass", rootExtendsClass, genModel::setRootExtendsClass); + applyStringOption(genModel, ePackage, "rootExtendsInterface", rootExtendsInterface, + genModel::setRootExtendsInterface); + applyStringOption(genModel, ePackage, "copyrightText", copyrightText, genModel::setCopyrightText); + + // Handle referenced 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() + ")"); + } + } + + // Create resource for main GenModel + URI genModelUri = URI + .createURI("platform:/resource/" + projectName + "/model/" + ePackage.getName() + ".genmodel"); + Resource genModelResource = resourceSet.createResource(genModelUri); + genModelResource.getContents().add(genModel); + + // Configure main GenPackage + if (!genModel.getGenPackages().isEmpty()) { + GenPackage mainGenPackage = genModel.getGenPackages().get(0); + + // basePackage + String effectiveBasePackage = getEffectiveValue(basePackage, + () -> getGenModelAnnotation(ePackage, "basePackage"), () -> deriveBasePackage(ePackage.getNsURI())); + if (effectiveBasePackage != null && !effectiveBasePackage.isEmpty()) { + mainGenPackage.setBasePackage(effectiveBasePackage); + getLog().info("Using basePackage: " + effectiveBasePackage); + } + + // prefix + String effectivePrefix = getEffectiveValue(prefix, () -> getGenModelAnnotation(ePackage, "prefix"), + () -> capitalize(ePackage.getName())); + mainGenPackage.setPrefix(effectivePrefix); + getLog().info("Using prefix: " + effectivePrefix); + + // fileExtension + String effectiveFileExtension = getEffectiveValue(fileExtension, + () -> getGenModelAnnotation(ePackage, "fileExtensions"), + () -> getGenModelAnnotation(ePackage, "fileExtension")); + if (effectiveFileExtension != null && !effectiveFileExtension.isEmpty()) { + mainGenPackage.setFileExtensions(effectiveFileExtension); + getLog().info("Using fileExtension: " + effectiveFileExtension); + } + + mainGenPackage.setEcorePackage(ePackage); + } + + return genModel; + } + + private void applyStringOption(GenModel genModel, EPackage ePackage, String key, String mavenValue, + java.util.function.Consumer setter) { + String effective = mavenValue; + if (effective == null || effective.isEmpty()) { + effective = getGenModelAnnotation(ePackage, key); + } + if (effective != null && !effective.isEmpty()) { + setter.accept(effective); + getLog().info("Using " + key + ": " + (key.contains("copyright") ? "(from annotation)" : effective)); + } + } + + @SafeVarargs + private final String getEffectiveValue(String primary, java.util.function.Supplier... fallbacks) { + if (primary != null && !primary.isEmpty()) { + return primary; + } + for (java.util.function.Supplier fallback : fallbacks) { + String value = fallback.get(); + if (value != null && !value.isEmpty()) { + return value; + } + } + return null; + } + + // ==================== Helper Methods ==================== + + private void logGenModelInfo(GenModel genModel) { + getLog().info("Created GenModel:"); + getLog().info(" Model directory: " + genModel.getModelDirectory()); + getLog().info(" Model name: " + genModel.getModelName()); + getLog().info(" GenModel resource: " + genModel.eResource().getURI()); + getLog().info(" GenPackages count: " + genModel.getGenPackages().size()); + for (GenPackage gp : genModel.getGenPackages()) { + getLog().info(" GenPackage: " + gp.getPackageName() + ", basePackage: " + gp.getBasePackage()); + } + } + + private void logGenModelAnnotations(EPackage ePackage) { + EAnnotation annotation = ePackage.getEAnnotation(GENMODEL_ANNOTATION_SOURCE); + if (annotation != null && !annotation.getDetails().isEmpty()) { + getLog().info("Found GenModel annotations in EPackage '" + ePackage.getName() + "':"); + for (Map.Entry entry : annotation.getDetails()) { + getLog().info(" " + entry.getKey() + " = " + entry.getValue()); + } + } + } + + private String getGenModelAnnotation(EPackage ePackage, String key) { + EAnnotation annotation = ePackage.getEAnnotation(GENMODEL_ANNOTATION_SOURCE); + if (annotation != null) { + String value = annotation.getDetails().get(key); + if (value != null && !value.isEmpty()) { + return value; + } + } + return null; + } + + private boolean getGenModelAnnotationBoolean(EPackage ePackage, String key, boolean defaultValue) { + String value = getGenModelAnnotation(ePackage, key); + if (value != null) { + return Boolean.parseBoolean(value); + } + return defaultValue; + } + + private Set findReferencedExternalPackages(EPackage ePackage) { + Set referenced = new HashSet<>(); + String mainNsURI = ePackage.getNsURI(); + + for (EClassifier classifier : ePackage.getEClassifiers()) { + if (classifier instanceof EClass eClass) { + for (EClass superClass : eClass.getESuperTypes()) { + EPackage superPackage = superClass.getEPackage(); + if (superPackage != null && !mainNsURI.equals(superPackage.getNsURI()) + && !isEMFCorePackage(superPackage.getNsURI())) { + referenced.add(superPackage); + } + } + + for (EStructuralFeature feature : eClass.getEStructuralFeatures()) { + EClassifier featureType = feature.getEType(); + if (featureType != null) { + EPackage featurePackage = featureType.getEPackage(); + if (featurePackage != null && !mainNsURI.equals(featurePackage.getNsURI()) + && !isEMFCorePackage(featurePackage.getNsURI())) { + referenced.add(featurePackage); + } + } + } + } + } + return referenced; + } + + private boolean isEMFCorePackage(String nsURI) { + return nsURI == null || nsURI.startsWith("http://www.eclipse.org/emf/") + || nsURI.startsWith("http://www.w3.org/") || nsURI.startsWith("http:///org/eclipse/emf/"); + } + + private String deriveBasePackage(String nsUri) { + if (nsUri == null || nsUri.isEmpty()) { + return null; + } + try { + java.net.URI uri = new java.net.URI(nsUri); + String host = uri.getHost(); + String path = uri.getPath(); + + if (host == null) { + return null; + } + + String[] hostParts = host.split("\\."); + StringBuilder sb = new StringBuilder(); + for (int i = hostParts.length - 1; i >= 0; i--) { + if (sb.length() > 0) { + sb.append("."); + } + sb.append(hostParts[i].toLowerCase()); + } + + if (path != null && !path.isEmpty()) { + String[] pathParts = path.split("/"); + for (int i = 1; i < pathParts.length - 1; i++) { + if (!pathParts[i].isEmpty()) { + sb.append(".").append(pathParts[i].toLowerCase()); + } + } + } + + return sb.toString(); + } catch (Exception e) { + getLog().warn("Could not derive base package from nsURI: " + nsUri); + return null; + } + } + + private String capitalize(String s) { + if (s == null || s.isEmpty()) { + return s; + } + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + + private void loadModelsFromDependencies(ResourceSet resourceSet) { + // Build a map of reactor projects by groupId:artifactId for quick lookup + Map reactorProjectMap = new HashMap<>(); + if (reactorProjects != null) { + for (MavenProject reactorProject : reactorProjects) { + String key = reactorProject.getGroupId() + ":" + reactorProject.getArtifactId(); + reactorProjectMap.put(key, reactorProject); + } + } + + // Collect model files from all dependencies + java.util.List ecoreFiles = new java.util.ArrayList<>(); + java.util.List genmodelFiles = new java.util.ArrayList<>(); + + for (Artifact artifact : project.getArtifacts()) { + String key = artifact.getGroupId() + ":" + artifact.getArtifactId(); + MavenProject reactorProject = reactorProjectMap.get(key); + + if (reactorProject != null) { + // Reactor dependency: scan source directories + collectModelFilesFromReactorProject(reactorProject, ecoreFiles, genmodelFiles); + } else { + // External JAR dependency + File file = artifact.getFile(); + if (file != null && file.exists() && file.getName().endsWith(".jar")) { + collectModelFilesFromJar(file, resourceSet, ecoreFiles, genmodelFiles); + } + } + } + + // Load ecore files first (to register EPackages) + for (File ecoreFile : ecoreFiles) { + loadEcoreFromFile(resourceSet, ecoreFile); + } + + // Then load genmodel files (which reference the EPackages) + for (File genmodelFile : genmodelFiles) { + loadGenModelFromFile(resourceSet, genmodelFile); + } + } + + /** + * Collect model files from a reactor project's resource directories. + */ + private void collectModelFilesFromReactorProject(MavenProject reactorProject, + java.util.List ecoreFiles, java.util.List genmodelFiles) { + for (org.apache.maven.model.Resource resource : reactorProject.getResources()) { + File resourceDir = new File(resource.getDirectory()); + if (resourceDir.exists() && resourceDir.isDirectory()) { + collectModelFilesFromDirectory(resourceDir, ecoreFiles, genmodelFiles); + } + } + } + + /** + * Collect model files from a JAR dependency. + */ + private void collectModelFilesFromJar(File jarFile, ResourceSet resourceSet, + java.util.List ecoreFiles, java.util.List genmodelFiles) { + try (JarFile jar = new JarFile(jarFile)) { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + if (isModelPath(name)) { + if (name.endsWith(".ecore")) { + loadEcoreFromJar(resourceSet, jarFile, name); + } else if (name.endsWith(".genmodel")) { + loadGenModelFromJar(resourceSet, jarFile, name); + } + } + } + } catch (IOException e) { + getLog().debug("Could not scan JAR for model files: " + jarFile.getName()); + } + } + + /** + * Recursively collect model files from a directory. + */ + private void collectModelFilesFromDirectory(File dir, + java.util.List ecoreFiles, java.util.List genmodelFiles) { + File[] files = dir.listFiles(); + if (files == null) { + return; + } + for (File file : files) { + if (file.isDirectory()) { + collectModelFilesFromDirectory(file, ecoreFiles, genmodelFiles); + } else if (isModelPath(file.getAbsolutePath())) { + if (file.getName().endsWith(".ecore")) { + ecoreFiles.add(file); + } else if (file.getName().endsWith(".genmodel")) { + genmodelFiles.add(file); + } + } + } + } + + /** + * 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/"); + } + + private void loadEcoreFromFile(ResourceSet resourceSet, File ecoreFile) { + try { + URI ecoreUri = URI.createFileURI(ecoreFile.getAbsolutePath()); + Resource ecoreResource = resourceSet.getResource(ecoreUri, true); + 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 + ")"); + } + } + } + } + } catch (Exception e) { + getLog().debug("Could not load Ecore: " + ecoreFile.getAbsolutePath() + " - " + e.getMessage()); + } + } + + private void loadGenModelFromFile(ResourceSet resourceSet, File genmodelFile) { + try { + URI genmodelUri = URI.createFileURI(genmodelFile.getAbsolutePath()); + Resource genmodelResource = resourceSet.getResource(genmodelUri, true); + if (genmodelResource != null && !genmodelResource.getContents().isEmpty()) { + for (org.eclipse.emf.ecore.EObject obj : genmodelResource.getContents()) { + if (obj instanceof GenModel genModel) { + EcoreUtil.resolveAll(genModel); + for (GenPackage genPackage : genModel.getGenPackages()) { + EPackage ePackage = genPackage.getEcorePackage(); + if (ePackage != null && ePackage.getNsURI() != null) { + genPackageRegistry.put(ePackage.getNsURI(), genPackage); + getLog().info("Registered GenPackage: " + genPackage.getPackageName() + " (" + ePackage.getNsURI() + ")"); + } + } + } + } + } + } catch (Exception e) { + getLog().debug("Could not load GenModel: " + genmodelFile.getAbsolutePath() + " - " + e.getMessage()); + } + } + + private void loadEcoreFromJar(ResourceSet resourceSet, File jarFile, String ecorePath) { + try { + URI ecoreUri = URI.createURI("jar:file:" + jarFile.getAbsolutePath() + "!/" + ecorePath); + Resource ecoreResource = resourceSet.getResource(ecoreUri, true); + 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 + ")"); + } + } + } + } + } catch (Exception e) { + getLog().debug("Could not load Ecore from JAR: " + jarFile.getName() + "!/" + ecorePath); + } + } + + private void loadGenModelFromJar(ResourceSet resourceSet, File jarFile, String genmodelPath) { + try { + URI genmodelUri = URI.createURI("jar:file:" + jarFile.getAbsolutePath() + "!/" + genmodelPath); + Resource genmodelResource = resourceSet.getResource(genmodelUri, true); + if (genmodelResource != null && !genmodelResource.getContents().isEmpty()) { + for (org.eclipse.emf.ecore.EObject obj : genmodelResource.getContents()) { + if (obj instanceof GenModel genModel) { + EcoreUtil.resolveAll(genModel); + for (GenPackage genPackage : genModel.getGenPackages()) { + EPackage ePackage = genPackage.getEcorePackage(); + if (ePackage != null && ePackage.getNsURI() != null) { + genPackageRegistry.put(ePackage.getNsURI(), genPackage); + getLog().info("Registered GenPackage: " + genPackage.getPackageName() + " (" + ePackage.getNsURI() + ")"); + } + } + } + } + } + } catch (Exception e) { + getLog().debug("Could not load GenModel from JAR: " + jarFile.getName() + "!/" + genmodelPath); + } + } + + private void printDiagnostic(Diagnostic diagnostic, String prefix) { + if (diagnostic.getSeverity() != Diagnostic.OK) { + if (diagnostic.getSeverity() == Diagnostic.ERROR) { + getLog().error(prefix + diagnostic.getMessage() + " - " + diagnostic.getSource()); + } else { + getLog().warn(prefix + diagnostic.getMessage() + " - " + diagnostic.getSource()); + } + if (diagnostic.getException() != null) { + getLog().error(prefix + "Exception: ", diagnostic.getException()); + } + } + for (Diagnostic child : diagnostic.getChildren()) { + printDiagnostic(child, prefix + " "); + } + } +} diff --git a/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/MavenProgressMonitor.java b/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/MavenProgressMonitor.java new file mode 100644 index 0000000..5191865 --- /dev/null +++ b/emf/codegen.maven/src/main/java/org/eclipse/daanse/tooling/emf/codegen/MavenProgressMonitor.java @@ -0,0 +1,75 @@ +/* + * 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 + * + * Contributors: + * SmartCity Jena - initial + * Stefan Bischof (bipolis.org) - initial + */ +package org.eclipse.daanse.tooling.emf.codegen; + +import org.apache.maven.plugin.logging.Log; +import org.eclipse.core.runtime.IProgressMonitor; + +/** + * Progress monitor implementation that logs to Maven's Log. + */ +public class MavenProgressMonitor implements IProgressMonitor { + + private final Log log; + private String taskName; + private boolean canceled = false; + + public MavenProgressMonitor(Log log) { + this.log = log; + } + + @Override + public void beginTask(String name, int totalWork) { + this.taskName = name; + log.info("Starting: " + name); + } + + @Override + public void done() { + log.info("Completed: " + taskName); + } + + @Override + public void internalWorked(double work) { + log.debug("Internal work: " + work + " on " + taskName); + } + + @Override + public boolean isCanceled() { + return canceled; + } + + @Override + public void setCanceled(boolean value) { + this.canceled = value; + if (value) { + log.warn("Canceled: " + taskName); + } + } + + @Override + public void setTaskName(String name) { + this.taskName = name; + } + + @Override + public void subTask(String name) { + log.debug("Subtask: " + name); + } + + @Override + public void worked(int work) { + log.debug("Worked: " + work + " on " + taskName); + } +} diff --git a/emf/pom.xml b/emf/pom.xml new file mode 100644 index 0000000..b9a4f63 --- /dev/null +++ b/emf/pom.xml @@ -0,0 +1,51 @@ + + + + 4.0.0 + + + org.eclipse.daanse + org.eclipse.daanse.tooling + 0.0.1-SNAPSHOT + + + org.eclipse.daanse.tooling.emf + pom + + Eclipse Daanse EMF Tooling + EMF code generation tooling for Eclipse Daanse + + + 2.41.0 + 2.44.0 + 2.39.0 + 2.27.0 + 2.44.0 + + + + codegen.maven + codegen.maven.example + + + + + eclipse-releases + https://repo.eclipse.org/content/repositories/releases/ + + + + diff --git a/pom.xml b/pom.xml index 0c4dc55..5725af9 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,7 @@ dockerjava + emf testcontainers