diff --git a/emf/codegen.maven.example/ecore.dependencies.subpackages.consumer.subpackages/model/consumer.ecore b/emf/codegen.maven.example/ecore.dependencies.subpackages.consumer.subpackages/model/consumer.ecore
new file mode 100644
index 0000000..1554c80
--- /dev/null
+++ b/emf/codegen.maven.example/ecore.dependencies.subpackages.consumer.subpackages/model/consumer.ecore
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/emf/codegen.maven.example/ecore.dependencies.subpackages.consumer.subpackages/pom.xml b/emf/codegen.maven.example/ecore.dependencies.subpackages.consumer.subpackages/pom.xml
new file mode 100644
index 0000000..e279cc6
--- /dev/null
+++ b/emf/codegen.maven.example/ecore.dependencies.subpackages.consumer.subpackages/pom.xml
@@ -0,0 +1,111 @@
+
+
+
+ 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.subpackages.consumer.subpackages
+ jar
+
+ Daanse EMF Codegen Example - Consumer with Subpackages and Cross-Module
+ Nested Reference
+ Regression test for the case where the consumer ecore has its
+ own sub-packages AND references classifiers in nested sub-packages of an
+ external dependency, with sibling-subpackage cross-references inside the
+ consumer (mirrors the cwm.governance.gdpr structure).
+
+
+
+ org.eclipse.emf
+ org.eclipse.emf.common
+
+
+ org.eclipse.emf
+ org.eclipse.emf.ecore
+
+
+
+ org.eclipse.daanse
+
+ org.eclipse.daanse.tooling.emf.codegen.maven.example.ecore.simple.subpackages
+ ${project.version}
+
+
+ org.eclipse.fennec.emf
+ org.eclipse.fennec.emf.osgi.api
+ 0.1.1
+
+
+ org.eclipse.fennec.emf
+ org.eclipse.fennec.emf.osgi.component
+ runtime
+
+
+
+
+
+
+ org.eclipse.daanse
+ org.eclipse.daanse.tooling.emf.codegen.maven
+ ${project.version}
+
+
+
+ generate
+
+
+ model/consumer.ecore
+ org.eclipse.daanse.example.consumer
+ 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/pom.xml b/emf/codegen.maven.example/pom.xml
index 62207e0..99b7787 100644
--- a/emf/codegen.maven.example/pom.xml
+++ b/emf/codegen.maven.example/pom.xml
@@ -40,6 +40,7 @@
ecore.dependencies
ecore.dependencies.subpackages
+ ecore.dependencies.subpackages.consumer.subpackages
genmodel.dependencies
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 f81d6b0..694f6f5 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
@@ -40,6 +40,7 @@
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.GenClassifier;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelFactory;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage;
@@ -128,6 +129,18 @@ public class EmfGenerateMojo extends AbstractMojo {
*/
private static final String MODEL_FOLDER = "model";
+ /**
+ * Tracks every nsURI this plugin has put into {@link EPackage.Registry#INSTANCE}
+ * across mojo invocations in the same JVM. At the start of each invocation we
+ * remove these entries so that the next module starts with a clean view: its
+ * fresh ResourceSet's package registry no longer reports {@code containsKey=true}
+ * via the delegate for nsURIs registered by an earlier session, and EMF's proxy
+ * resolution can find the freshly loaded EPackages instead of stale ones.
+ * Static so it survives the per-lookup mojo instantiation strategy.
+ */
+ private static final java.util.Set pluginRegisteredNsURIs =
+ java.util.Collections.synchronizedSet(new java.util.HashSet<>());
+
/**
* Registry mapping EPackage nsURI to GenPackage for dependency resolution.
*/
@@ -429,9 +442,25 @@ private Optional generateFromEcore(File baseDir) {
}
}
+ getLog().info("Before saveGenModelToResources: getGenPackages=" + genModel.getGenPackages().size()
+ + " usedGenPackages=" + genModel.getUsedGenPackages().size());
+
// Save the GenModel to resources for inclusion in JAR
saveGenModelToResources(genModel, ePackage);
+ getLog().info("After saveGenModelToResources: getGenPackages=" + genModel.getGenPackages().size()
+ + " usedGenPackages=" + genModel.getUsedGenPackages().size());
+
+ // Verbose diagnostic — only emitted under -X (debug). Dumps the genmodel's
+ // GenPackage tree and probes every EClassifier referenced from main to
+ // verify it's resolvable. Skips EMF's protected findGenClassifier
+ // (reflective invocation has been seen to perturb XMI parser state for
+ // very large models like CWM).
+ if (getLog().isDebugEnabled()) {
+ debugDumpGenModel(genModel);
+ debugProbeFindGenClassifier(genModel, ePackage);
+ }
+
// Collect referenced package names (to delete their generated files after
// generation)
List referencedPackageNames = new ArrayList<>();
@@ -568,6 +597,24 @@ private void configureEMF(ResourceSet resourceSet) {
// Clear the genPackageRegistry for a fresh run
genPackageRegistry.clear();
+ // Drop nsURIs this plugin previously registered into the JVM-global
+ // EPackage.Registry.INSTANCE in earlier mojo invocations within the same
+ // Maven session. Without this, ResourceSetImpl#getPackageRegistry()'s
+ // delegation would report containsKey=true for those nsURIs, the local
+ // map would never be populated, and EMF cross-ref resolution would walk
+ // stale EClassifier instances detached from this module's resourceSet —
+ // surfacing as findGenClassifier() == null deep inside codegen.
+ synchronized (pluginRegisteredNsURIs) {
+ if (!pluginRegisteredNsURIs.isEmpty()) {
+ getLog().info("Removing " + pluginRegisteredNsURIs.size()
+ + " stale nsURIs from EPackage.Registry.INSTANCE (from earlier mojo invocations in this JVM)");
+ for (String nsURI : pluginRegisteredNsURIs) {
+ EPackage.Registry.INSTANCE.remove(nsURI);
+ }
+ pluginRegisteredNsURIs.clear();
+ }
+ }
+
resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap().put(GenModelPackage.eCONTENT_TYPE,
new XMIResourceFactoryImpl());
resourceSet.getResourceFactoryRegistry().getContentTypeToFactoryMap().put("application/xmi",
@@ -745,7 +792,8 @@ private GenModel createGenModel(EPackage ePackage, String projectName, ResourceS
// Find referenced external packages (transitively)
Set referencedPackages = findReferencedExternalPackages(ePackage);
for (EPackage ref : referencedPackages) {
- getLog().info("Referenced external package: " + ref.getName() + " (" + ref.getNsURI() + ")");
+ getLog().info("Referenced external package: " + ref.getName() + " (" + ref.getNsURI() + ") with "
+ + ref.getEClassifiers().size() + " EClassifiers");
}
// Initialize GenModel with ALL packages (main + referenced) — EMF still
@@ -756,6 +804,22 @@ private GenModel createGenModel(EPackage ePackage, String projectName, ResourceS
allPackages.addAll(referencedPackages);
genModel.initialize(allPackages);
+ getLog().info("After genModel.initialize, GenPackages count: " + genModel.getGenPackages().size());
+ if (getLog().isDebugEnabled()) {
+ for (GenPackage gp : genModel.getGenPackages()) {
+ EPackage ep = gp.getEcorePackage();
+ getLog().debug(" top-level GenPackage: " + gp.getPackageName() + " ("
+ + (ep != null ? ep.getNsURI() : "null") + ") classifiers="
+ + gp.getGenClassifiers().size() + " sub=" + gp.getSubGenPackages().size());
+ for (GenPackage sub : gp.getSubGenPackages()) {
+ EPackage sep = sub.getEcorePackage();
+ getLog().debug(" sub GenPackage: " + sub.getPackageName() + " ("
+ + (sep != null ? sep.getNsURI() : "null") + ") classifiers="
+ + sub.getGenClassifiers().size());
+ }
+ }
+ }
+
// Configure referenced GenPackages (indices 1+) and MOVE them from
// getGenPackages() to getUsedGenPackages() — this prevents EMF from
// (re-)generating Java code for the external types (those classes already
@@ -1142,12 +1206,24 @@ private void configureGenPackageFromAnnotations(GenPackage genPackage, EPackage
*/
private void registerEPackage(ResourceSet resourceSet, EPackage ePackage, String source) {
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" + source + ": " + ePackage.getName() + " (" + nsURI + ")");
+ if (nsURI != null) {
+ // Putting into EPackage.Registry.INSTANCE happens only the first time
+ // a session sees this nsURI. The resourceSet.getPackageRegistry() also
+ // gets the freshly loaded ePackage on first encounter. ResourceSet
+ // PackageRegistry delegates to INSTANCE for misses, so subsequent
+ // sessions would otherwise short-circuit on the stale INSTANCE entry.
+ if (!EPackage.Registry.INSTANCE.containsKey(nsURI)) {
+ EPackage.Registry.INSTANCE.put(nsURI, ePackage);
+ pluginRegisteredNsURIs.add(nsURI);
+ resourceSet.getPackageRegistry().put(nsURI, ePackage);
+ getLog().info("Registered EPackage" + source + ": " + ePackage.getName() + " (" + nsURI + ")");
+ }
- // Also create and register a GenPackage if GenModel annotations are present
+ // Populate the per-mojo GenPackage registry independently of the JVM-global
+ // EPackage.Registry. The latter persists across modules in the same Maven
+ // session, so on the second consumer module the outer guard would
+ // short-circuit and leave genPackageRegistry empty — breaking cross-package
+ // codegen lookups (manifests as NPE in getPackageFamilyTreeDependencies).
if (!genPackageRegistry.containsKey(nsURI)) {
GenPackage genPackage = createGenPackageFromEcore(resourceSet, ePackage);
if (genPackage != null) {
@@ -1607,6 +1683,126 @@ private void printDiagnostic(Diagnostic diagnostic, String prefix) {
/**
* Recursively deletes a directory and all its contents.
*/
+ private void debugDumpGenModel(GenModel genModel) {
+ getLog().debug("==== GenModel state before generation ====");
+ getLog().debug("getGenPackages():");
+ for (GenPackage gp : genModel.getGenPackages()) {
+ dumpGenPackage(gp, " ");
+ }
+ getLog().debug("getUsedGenPackages():");
+ for (GenPackage gp : genModel.getUsedGenPackages()) {
+ dumpGenPackage(gp, " ");
+ }
+ getLog().debug("==========================================");
+ }
+
+ private void dumpGenPackage(GenPackage gp, String indent) {
+ EPackage ep = gp.getEcorePackage();
+ String nsURI = ep != null ? ep.getNsURI() : "null";
+ int classifiers = gp.getGenClassifiers().size();
+ Resource res = ep != null ? ep.eResource() : null;
+ getLog().debug(indent + gp.getPackageName() + " (" + nsURI + ") classifiers=" + classifiers
+ + " eResource=" + (res != null ? res.getURI() : "null"));
+ for (GenPackage sub : gp.getSubGenPackages()) {
+ dumpGenPackage(sub, indent + " ");
+ }
+ }
+
+ private void debugProbeFindGenClassifier(GenModel genModel, EPackage mainEPackage) {
+ getLog().debug("==== Probing findGenClassifier for external EClassifier refs ====");
+ Set probed = new HashSet<>();
+ probeEPackage(genModel, mainEPackage, probed);
+ getLog().debug("==========================================");
+ }
+
+ private void probeEPackage(GenModel genModel, EPackage ePackage, Set probed) {
+ String ownNsURI = ePackage.getNsURI();
+ for (EClassifier classifier : ePackage.getEClassifiers()) {
+ if (classifier instanceof EClass eClass) {
+ for (EClass sup : eClass.getESuperTypes()) {
+ probeOne(genModel, sup, ownNsURI, "supertype of " + eClass.getName(), probed);
+ }
+ for (EStructuralFeature f : eClass.getEStructuralFeatures()) {
+ EClassifier ft = f.getEType();
+ if (ft != null) {
+ probeOne(genModel, ft, ownNsURI, eClass.getName() + "." + f.getName(),
+ probed);
+ }
+ }
+ }
+ }
+ for (EPackage sub : ePackage.getESubpackages()) {
+ probeEPackage(genModel, sub, probed);
+ }
+ }
+
+ private void probeOne(GenModel genModel, EClassifier ec, String ownRootNsURI, String origin,
+ Set probed) {
+ if (!probed.add(ec)) {
+ return;
+ }
+ EPackage ep = ec.getEPackage();
+ String nsURI = ep != null ? ep.getNsURI() : "null";
+ // Manual walk: find a GenClassifier whose getEcoreClassifier() == ec, by name
+ // fallback. This mimics what the EMF codegen does without invoking the
+ // protected findGenClassifier method directly (reflection on it can perturb
+ // the state of large generated packages like CWM during their initial parse).
+ GenPackage manualMatch = null;
+ for (GenPackage gp : genModel.getGenPackages()) {
+ manualMatch = matchClassifier(gp, ec);
+ if (manualMatch != null) {
+ break;
+ }
+ }
+ if (manualMatch == null) {
+ for (GenPackage gp : genModel.getUsedGenPackages()) {
+ manualMatch = matchClassifier(gp, ec);
+ if (manualMatch != null) {
+ break;
+ }
+ }
+ }
+ getLog().debug(" ref " + origin + " -> " + (ec.getName() == null ? "null" : ec.getName())
+ + " in " + nsURI
+ + " instanceTypeName=" + ec.getInstanceTypeName()
+ + " manualMatch=" + (manualMatch != null ? manualMatch.getPackageName() : "NONE"));
+ }
+
+ private GenPackage matchClassifier(GenPackage gp, EClassifier ec) {
+ EPackage gpEP = gp.getEcorePackage();
+ if (gpEP != null && ec.getEPackage() != null
+ && gpEP.getNsURI().equals(ec.getEPackage().getNsURI())) {
+ for (GenClassifier gc : gp.getGenClassifiers()) {
+ if (gc.getEcoreClassifier() == ec) {
+ return gp;
+ }
+ }
+ // EClassifier may be a different instance with same name
+ for (GenClassifier gc : gp.getGenClassifiers()) {
+ if (gc.getEcoreClassifier() != null
+ && ec.getName() != null
+ && ec.getName().equals(gc.getEcoreClassifier().getName())) {
+ EClassifier other = gc.getEcoreClassifier();
+ Resource ecRes = ec.eResource();
+ Resource otherRes = other.eResource();
+ getLog().warn(" IDENTITY MISMATCH for " + ec.getName()
+ + ": ec@" + System.identityHashCode(ec) + " in "
+ + (ecRes != null ? ecRes.getURI() : "null")
+ + " vs GenClassifier.ec@" + System.identityHashCode(other)
+ + " in " + (otherRes != null ? otherRes.getURI() : "null"));
+ return gp;
+ }
+ }
+ }
+ for (GenPackage sub : gp.getSubGenPackages()) {
+ GenPackage m = matchClassifier(sub, ec);
+ if (m != null) {
+ return m;
+ }
+ }
+ return null;
+ }
+
private void deleteDirectory(File dir) {
if (dir.isDirectory()) {
File[] files = dir.listFiles();