You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2026/06/25 23:15:00 UTC
(cayenne) 02/02: CAY-2965 MCP Cgen should not fail on an absent "" tag
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 <an...@objectstyle.com>
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} → {@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;
}