This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit 5713180292e1612bd7f9f00482f041ee716ef54b
Author: Andrus Adamchik <[email protected]>
AuthorDate: Thu Jun 25 18:44:08 2026 -0400

    CAY-2965 MCP Cgen should not fail on an absent "<cgen>" tag
    
    unifying default config API
---
 .../org/apache/cayenne/gen/CgenConfiguration.java  | 34 +++++++++
 .../apache/cayenne/gen/CgenConfigurationTest.java  | 81 ++++++++++++++++++++++
 .../apache/cayenne/mcp/tools/cgen/CgenRunTool.java | 23 +-----
 .../editor/datadomain/cgen/DataDomainCgenTab.java  |  7 +-
 .../ui/project/editor/datamap/cgen/CgenPanel.java  | 14 +---
 5 files changed, 119 insertions(+), 40 deletions(-)

diff --git 
a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/CgenConfiguration.java 
b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/CgenConfiguration.java
index 2183201a3..6d2fd810e 100644
--- a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/CgenConfiguration.java
+++ b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/CgenConfiguration.java
@@ -20,6 +20,7 @@
 package org.apache.cayenne.gen;
 
 import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
+import org.apache.cayenne.gen.internal.Utils;
 import org.apache.cayenne.gen.xml.CgenExtension;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.Embeddable;
@@ -127,6 +128,39 @@ public class CgenConfiguration implements Serializable, 
XMLSerializable {
         this.embeddableSuperTemplate = 
TemplateType.EMBEDDABLE_SUPERCLASS.defaultTemplate();
     }
 
+    /**
+     * Builds a default configuration for a DataMap that has no stored cgen 
config, generating every
+     * non-generic entity and embeddable into the given output directory.
+     *
+     * @param outputDir directory to generate classes into; if {@code null}, 
the output path is left unset
+     * @since 5.0
+     */
+    public static CgenConfiguration createDefault(DataMap dataMap, Path 
outputDir) {
+        CgenConfiguration config = new CgenConfiguration();
+        config.setDataMap(dataMap);
+        dataMap.getObjEntities().forEach(config::loadEntity);
+        dataMap.getEmbeddables().forEach(config::loadEmbeddable);
+        if (dataMap.getConfigurationSource() != null) {
+            config.setRootPath(Utils.getRootPathForDataMap(dataMap));
+        }
+        if (outputDir != null) {
+            config.updateOutputPath(outputDir);
+        }
+        return config;
+    }
+
+    /**
+     * Returns the default output directory for a saved DataMap, derived from 
the standard Maven
+     * source layout ({@code src/main/resources} &rarr; {@code src/main/java}, 
likewise for
+     * {@code test}), falling back to the DataMap's own directory for 
non-Maven layouts.
+     *
+     * @since 5.0
+     */
+    public static Path defaultOutputDir(DataMap dataMap) {
+        Path mapDir = Utils.getRootPathForDataMap(dataMap);
+        return 
Utils.getMavenSrcPathForPath(mapDir).map(Path::of).orElse(mapDir);
+    }
+
     public void resetCollections() {
         embeddableArtifacts.clear();
         entityArtifacts.clear();
diff --git 
a/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenConfigurationTest.java 
b/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenConfigurationTest.java
index b306cb565..18d63718b 100644
--- 
a/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenConfigurationTest.java
+++ 
b/cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenConfigurationTest.java
@@ -19,22 +19,103 @@
 
 package org.apache.cayenne.gen;
 
+import org.apache.cayenne.map.DataMap;
+import org.apache.cayenne.map.Embeddable;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.resource.URLResource;
 import org.apache.cayenne.validation.ValidationException;
 import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Locale;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class CgenConfigurationTest {
 
+    @Nested
+    public class CreateDefaultTest {
+
+        private DataMap savedMap(Path mapFile) throws MalformedURLException {
+            DataMap map = unsavedMap();
+            map.setConfigurationSource(new 
URLResource(mapFile.toUri().toURL()));
+            return map;
+        }
+
+        private DataMap unsavedMap() {
+            DataMap map = new DataMap("test");
+
+            ObjEntity person = new ObjEntity("Person");
+            person.setClassName("com.example.Person");
+            map.addObjEntity(person);
+
+            // generic entity (no class name) — must be excluded from 
generation
+            map.addObjEntity(new ObjEntity("Generic"));
+
+            map.addEmbeddable(new Embeddable("com.example.Address"));
+            return map;
+        }
+
+        @Test
+        public void populatesNonGenericArtifacts(@TempDir Path tmp) throws 
IOException {
+            Path mapFile = Files.createFile(tmp.resolve("test.map.xml"));
+            CgenConfiguration config = 
CgenConfiguration.createDefault(savedMap(mapFile), tmp);
+
+            assertTrue(config.getEntities().contains("Person"));
+            assertFalse(config.getEntities().contains("Generic"), "Generic 
entity must be skipped");
+            
assertTrue(config.getEmbeddables().contains("com.example.Address"));
+        }
+
+        @Test
+        public void derivesMavenOutputDir(@TempDir Path tmp) throws 
IOException {
+            Path resources = 
Files.createDirectories(tmp.resolve("src/main/resources"));
+            Path mapFile = Files.createFile(resources.resolve("test.map.xml"));
+            DataMap map = savedMap(mapFile);
+
+            // src/main/resources -> src/main/java
+            Path outputDir = CgenConfiguration.defaultOutputDir(map);
+            assertEquals(tmp.resolve("src/main/java"), outputDir);
+
+            CgenConfiguration config = CgenConfiguration.createDefault(map, 
outputDir);
+            assertEquals(resources, config.getRootPath());
+            assertEquals(tmp.resolve("src/main/java"), 
config.buildOutputPath());
+        }
+
+        @Test
+        public void explicitOutputDir(@TempDir Path tmp) throws IOException {
+            Path mapFile = Files.createFile(tmp.resolve("test.map.xml"));
+            Path outputDir = tmp.resolve("custom/output");
+
+            CgenConfiguration config = 
CgenConfiguration.createDefault(savedMap(mapFile), outputDir);
+
+            assertEquals(outputDir, config.buildOutputPath());
+        }
+
+        @Test
+        public void unsavedMapSkipsRootPath() {
+            CgenConfiguration config = 
CgenConfiguration.createDefault(unsavedMap(), null);
+
+            assertNull(config.getRootPath());
+            assertNull(config.buildOutputPath());
+            // artifacts still populated even without a saved location
+            assertTrue(config.getEntities().contains("Person"));
+            
assertTrue(config.getEmbeddables().contains("com.example.Address"));
+        }
+    }
+
     @Nested
     public class CgenWindowsConfigurationTest {
 
diff --git 
a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/cgen/CgenRunTool.java
 
b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/cgen/CgenRunTool.java
index bbf45f51f..56017064f 100644
--- 
a/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/cgen/CgenRunTool.java
+++ 
b/cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/cgen/CgenRunTool.java
@@ -28,7 +28,6 @@ import org.apache.cayenne.gen.CgenConfigList;
 import org.apache.cayenne.gen.CgenConfiguration;
 import org.apache.cayenne.gen.MetadataUtils;
 import org.apache.cayenne.gen.ToolsUtilsFactory;
-import org.apache.cayenne.gen.internal.Utils;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.mcp.project.McpProjectLoaderModule;
 import org.apache.cayenne.mcp.log.McpLoggingHandler;
@@ -165,7 +164,7 @@ public class CgenRunTool {
         CgenConfigList configList = metaData.get(dataMap, 
CgenConfigList.class);
         boolean usedDefaultConfig = configList == null || 
configList.getAll().isEmpty();
         CgenConfiguration cgenConfig = usedDefaultConfig
-                ? defaultConfig(dataMap)
+                ? CgenConfiguration.createDefault(dataMap, 
CgenConfiguration.defaultOutputDir(dataMap))
                 : configList.getAll().getFirst();
 
         // Set the DataMap file's mtime as the timestamp so fileNeedUpdate() 
can detect DataMap changes correctly.
@@ -246,26 +245,6 @@ public class CgenRunTool {
         );
     }
 
-    /**
-     * Builds a default cgen configuration for a DataMap that has no embedded 
{@code <cgen>} block.
-     * Mirrors {@code CgenPanel.createDefaultCgenConfiguration} in 
CayenneModeler: generate every
-     * entity and embeddable, with the destination derived from the standard 
Maven source layout
-     * ({@code src/main/resources} → {@code src/main/java}, likewise for 
{@code test}). For projects
-     * that don't follow the Maven layout the destination falls back to the 
DataMap's own directory.
-     */
-    private static CgenConfiguration defaultConfig(DataMap dataMap) {
-        Path mapDir = Utils.getRootPathForDataMap(dataMap);
-        Path outputPath = 
Utils.getMavenSrcPathForPath(mapDir).map(Path::of).orElse(mapDir);
-
-        CgenConfiguration config = new CgenConfiguration();
-        config.setDataMap(dataMap);
-        dataMap.getObjEntities().forEach(config::loadEntity);
-        dataMap.getEmbeddables().forEach(config::loadEmbeddable);
-        config.setRootPath(mapDir);
-        config.updateOutputPath(outputPath);
-        return config;
-    }
-
     private static CgenRunResult validationFailed(CgenErrorCode code, String 
message, CgenValidation validation) {
         return new CgenRunResult(
                 "validation_failed",
diff --git 
a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datadomain/cgen/DataDomainCgenTab.java
 
b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datadomain/cgen/DataDomainCgenTab.java
index b9327c765..01f37b8aa 100644
--- 
a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datadomain/cgen/DataDomainCgenTab.java
+++ 
b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datadomain/cgen/DataDomainCgenTab.java
@@ -85,13 +85,8 @@ public class DataDomainCgenTab extends 
DataDomainGeneratorsTab<CgenConfiguration
     }
 
     public CgenConfiguration createConfiguration(DataMap dataMap) {
-        CgenConfiguration cgenConfiguration = new CgenConfiguration();
-        cgenConfiguration.setDataMap(dataMap);
-        cgenConfiguration.updateOutputPath(CgenOps.baseDir(session));
+        CgenConfiguration cgenConfiguration = 
CgenConfiguration.createDefault(dataMap, CgenOps.baseDir(session));
         cgenConfiguration.setEncoding(new 
GeneralPrefs(app.getPrefsLocator().appNode(GeneralPrefs.NODE)).getEncoding());
-
-        cgenConfiguration.resolveExcludedEntities();
-        cgenConfiguration.resolveExcludedEmbeddables();
         return cgenConfiguration;
     }
 
diff --git 
a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java
 
b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java
index b1b147830..3e360faaa 100644
--- 
a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java
+++ 
b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java
@@ -31,7 +31,6 @@ import org.apache.cayenne.gen.CgenConfigList;
 import org.apache.cayenne.gen.CgenConfiguration;
 import org.apache.cayenne.gen.ClassGenerationAction;
 import org.apache.cayenne.gen.ClassGenerationActionFactory;
-import org.apache.cayenne.gen.internal.Utils;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.Embeddable;
 import org.apache.cayenne.map.Entity;
@@ -437,18 +436,9 @@ public class CgenPanel extends ProjectPanel implements 
ObjEntityListener, Embedd
     }
 
     private CgenConfiguration createDefaultCgenConfiguration(DataMap map) {
-        CgenConfiguration configuration = new CgenConfiguration();
-        configuration.setName(CgenConfigList.DEFAULT_CONFIG_NAME);
+        Path basePath = map.getLocation() != null ? CgenOps.baseDir(session) : 
null;
+        CgenConfiguration configuration = CgenConfiguration.createDefault(map, 
basePath);
         configuration.setForce(true);
-        configuration.setDataMap(map);
-
-        map.getObjEntities().forEach(configuration::loadEntity);
-        map.getEmbeddables().forEach(configuration::loadEmbeddable);
-        if (map.getLocation() != null) {
-            Path basePath = CgenOps.baseDir(session);
-            configuration.setRootPath(Utils.getRootPathForDataMap(map));
-            configuration.updateOutputPath(basePath);
-        }
         configuration.setEncoding(new 
GeneralPrefs(app.getPrefsLocator().appNode(GeneralPrefs.NODE)).getEncoding());
         return configuration;
     }

Reply via email to