Skip to content

Commit 5713180

Browse files
committed
CAY-2965 MCP Cgen should not fail on an absent "<cgen>" tag
unifying default config API
1 parent f5c97f3 commit 5713180

5 files changed

Lines changed: 119 additions & 40 deletions

File tree

cayenne-cgen/src/main/java/org/apache/cayenne/gen/CgenConfiguration.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.apache.cayenne.gen;
2121

2222
import org.apache.cayenne.configuration.ConfigurationNodeVisitor;
23+
import org.apache.cayenne.gen.internal.Utils;
2324
import org.apache.cayenne.gen.xml.CgenExtension;
2425
import org.apache.cayenne.map.DataMap;
2526
import org.apache.cayenne.map.Embeddable;
@@ -127,6 +128,39 @@ public CgenConfiguration() {
127128
this.embeddableSuperTemplate = TemplateType.EMBEDDABLE_SUPERCLASS.defaultTemplate();
128129
}
129130

131+
/**
132+
* Builds a default configuration for a DataMap that has no stored cgen config, generating every
133+
* non-generic entity and embeddable into the given output directory.
134+
*
135+
* @param outputDir directory to generate classes into; if {@code null}, the output path is left unset
136+
* @since 5.0
137+
*/
138+
public static CgenConfiguration createDefault(DataMap dataMap, Path outputDir) {
139+
CgenConfiguration config = new CgenConfiguration();
140+
config.setDataMap(dataMap);
141+
dataMap.getObjEntities().forEach(config::loadEntity);
142+
dataMap.getEmbeddables().forEach(config::loadEmbeddable);
143+
if (dataMap.getConfigurationSource() != null) {
144+
config.setRootPath(Utils.getRootPathForDataMap(dataMap));
145+
}
146+
if (outputDir != null) {
147+
config.updateOutputPath(outputDir);
148+
}
149+
return config;
150+
}
151+
152+
/**
153+
* Returns the default output directory for a saved DataMap, derived from the standard Maven
154+
* source layout ({@code src/main/resources} &rarr; {@code src/main/java}, likewise for
155+
* {@code test}), falling back to the DataMap's own directory for non-Maven layouts.
156+
*
157+
* @since 5.0
158+
*/
159+
public static Path defaultOutputDir(DataMap dataMap) {
160+
Path mapDir = Utils.getRootPathForDataMap(dataMap);
161+
return Utils.getMavenSrcPathForPath(mapDir).map(Path::of).orElse(mapDir);
162+
}
163+
130164
public void resetCollections() {
131165
embeddableArtifacts.clear();
132166
entityArtifacts.clear();

cayenne-cgen/src/test/java/org/apache/cayenne/gen/CgenConfigurationTest.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,103 @@
1919

2020
package org.apache.cayenne.gen;
2121

22+
import org.apache.cayenne.map.DataMap;
23+
import org.apache.cayenne.map.Embeddable;
24+
import org.apache.cayenne.map.ObjEntity;
25+
import org.apache.cayenne.resource.URLResource;
2226
import org.apache.cayenne.validation.ValidationException;
2327
import org.junit.jupiter.api.Assumptions;
2428
import org.junit.jupiter.api.BeforeEach;
2529
import org.junit.jupiter.api.Nested;
2630
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.api.io.TempDir;
2732

33+
import java.io.IOException;
34+
import java.net.MalformedURLException;
35+
import java.nio.file.Files;
2836
import java.nio.file.InvalidPathException;
2937
import java.nio.file.Path;
3038
import java.nio.file.Paths;
3139
import java.util.Locale;
3240

3341
import static org.junit.jupiter.api.Assertions.assertEquals;
42+
import static org.junit.jupiter.api.Assertions.assertFalse;
43+
import static org.junit.jupiter.api.Assertions.assertNull;
3444
import static org.junit.jupiter.api.Assertions.assertThrows;
45+
import static org.junit.jupiter.api.Assertions.assertTrue;
3546

3647
public class CgenConfigurationTest {
3748

49+
@Nested
50+
public class CreateDefaultTest {
51+
52+
private DataMap savedMap(Path mapFile) throws MalformedURLException {
53+
DataMap map = unsavedMap();
54+
map.setConfigurationSource(new URLResource(mapFile.toUri().toURL()));
55+
return map;
56+
}
57+
58+
private DataMap unsavedMap() {
59+
DataMap map = new DataMap("test");
60+
61+
ObjEntity person = new ObjEntity("Person");
62+
person.setClassName("com.example.Person");
63+
map.addObjEntity(person);
64+
65+
// generic entity (no class name) — must be excluded from generation
66+
map.addObjEntity(new ObjEntity("Generic"));
67+
68+
map.addEmbeddable(new Embeddable("com.example.Address"));
69+
return map;
70+
}
71+
72+
@Test
73+
public void populatesNonGenericArtifacts(@TempDir Path tmp) throws IOException {
74+
Path mapFile = Files.createFile(tmp.resolve("test.map.xml"));
75+
CgenConfiguration config = CgenConfiguration.createDefault(savedMap(mapFile), tmp);
76+
77+
assertTrue(config.getEntities().contains("Person"));
78+
assertFalse(config.getEntities().contains("Generic"), "Generic entity must be skipped");
79+
assertTrue(config.getEmbeddables().contains("com.example.Address"));
80+
}
81+
82+
@Test
83+
public void derivesMavenOutputDir(@TempDir Path tmp) throws IOException {
84+
Path resources = Files.createDirectories(tmp.resolve("src/main/resources"));
85+
Path mapFile = Files.createFile(resources.resolve("test.map.xml"));
86+
DataMap map = savedMap(mapFile);
87+
88+
// src/main/resources -> src/main/java
89+
Path outputDir = CgenConfiguration.defaultOutputDir(map);
90+
assertEquals(tmp.resolve("src/main/java"), outputDir);
91+
92+
CgenConfiguration config = CgenConfiguration.createDefault(map, outputDir);
93+
assertEquals(resources, config.getRootPath());
94+
assertEquals(tmp.resolve("src/main/java"), config.buildOutputPath());
95+
}
96+
97+
@Test
98+
public void explicitOutputDir(@TempDir Path tmp) throws IOException {
99+
Path mapFile = Files.createFile(tmp.resolve("test.map.xml"));
100+
Path outputDir = tmp.resolve("custom/output");
101+
102+
CgenConfiguration config = CgenConfiguration.createDefault(savedMap(mapFile), outputDir);
103+
104+
assertEquals(outputDir, config.buildOutputPath());
105+
}
106+
107+
@Test
108+
public void unsavedMapSkipsRootPath() {
109+
CgenConfiguration config = CgenConfiguration.createDefault(unsavedMap(), null);
110+
111+
assertNull(config.getRootPath());
112+
assertNull(config.buildOutputPath());
113+
// artifacts still populated even without a saved location
114+
assertTrue(config.getEntities().contains("Person"));
115+
assertTrue(config.getEmbeddables().contains("com.example.Address"));
116+
}
117+
}
118+
38119
@Nested
39120
public class CgenWindowsConfigurationTest {
40121

cayenne-mcp-server/src/main/java/org/apache/cayenne/mcp/tools/cgen/CgenRunTool.java

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.apache.cayenne.gen.CgenConfiguration;
2929
import org.apache.cayenne.gen.MetadataUtils;
3030
import org.apache.cayenne.gen.ToolsUtilsFactory;
31-
import org.apache.cayenne.gen.internal.Utils;
3231
import org.apache.cayenne.map.DataMap;
3332
import org.apache.cayenne.mcp.project.McpProjectLoaderModule;
3433
import org.apache.cayenne.mcp.log.McpLoggingHandler;
@@ -165,7 +164,7 @@ public CgenRunResult run(String projectPath, String dataMapName) {
165164
CgenConfigList configList = metaData.get(dataMap, CgenConfigList.class);
166165
boolean usedDefaultConfig = configList == null || configList.getAll().isEmpty();
167166
CgenConfiguration cgenConfig = usedDefaultConfig
168-
? defaultConfig(dataMap)
167+
? CgenConfiguration.createDefault(dataMap, CgenConfiguration.defaultOutputDir(dataMap))
169168
: configList.getAll().getFirst();
170169

171170
// Set the DataMap file's mtime as the timestamp so fileNeedUpdate() can detect DataMap changes correctly.
@@ -246,26 +245,6 @@ public CgenRunResult run(String projectPath, String dataMapName) {
246245
);
247246
}
248247

249-
/**
250-
* Builds a default cgen configuration for a DataMap that has no embedded {@code <cgen>} block.
251-
* Mirrors {@code CgenPanel.createDefaultCgenConfiguration} in CayenneModeler: generate every
252-
* entity and embeddable, with the destination derived from the standard Maven source layout
253-
* ({@code src/main/resources} → {@code src/main/java}, likewise for {@code test}). For projects
254-
* that don't follow the Maven layout the destination falls back to the DataMap's own directory.
255-
*/
256-
private static CgenConfiguration defaultConfig(DataMap dataMap) {
257-
Path mapDir = Utils.getRootPathForDataMap(dataMap);
258-
Path outputPath = Utils.getMavenSrcPathForPath(mapDir).map(Path::of).orElse(mapDir);
259-
260-
CgenConfiguration config = new CgenConfiguration();
261-
config.setDataMap(dataMap);
262-
dataMap.getObjEntities().forEach(config::loadEntity);
263-
dataMap.getEmbeddables().forEach(config::loadEmbeddable);
264-
config.setRootPath(mapDir);
265-
config.updateOutputPath(outputPath);
266-
return config;
267-
}
268-
269248
private static CgenRunResult validationFailed(CgenErrorCode code, String message, CgenValidation validation) {
270249
return new CgenRunResult(
271250
"validation_failed",

modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datadomain/cgen/DataDomainCgenTab.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,8 @@ public void runGenerators(Set<DataMap> dataMaps) {
8585
}
8686

8787
public CgenConfiguration createConfiguration(DataMap dataMap) {
88-
CgenConfiguration cgenConfiguration = new CgenConfiguration();
89-
cgenConfiguration.setDataMap(dataMap);
90-
cgenConfiguration.updateOutputPath(CgenOps.baseDir(session));
88+
CgenConfiguration cgenConfiguration = CgenConfiguration.createDefault(dataMap, CgenOps.baseDir(session));
9189
cgenConfiguration.setEncoding(new GeneralPrefs(app.getPrefsLocator().appNode(GeneralPrefs.NODE)).getEncoding());
92-
93-
cgenConfiguration.resolveExcludedEntities();
94-
cgenConfiguration.resolveExcludedEmbeddables();
9590
return cgenConfiguration;
9691
}
9792

modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.apache.cayenne.gen.CgenConfiguration;
3232
import org.apache.cayenne.gen.ClassGenerationAction;
3333
import org.apache.cayenne.gen.ClassGenerationActionFactory;
34-
import org.apache.cayenne.gen.internal.Utils;
3534
import org.apache.cayenne.map.DataMap;
3635
import org.apache.cayenne.map.Embeddable;
3736
import org.apache.cayenne.map.Entity;
@@ -437,18 +436,9 @@ private void prepareClasses(DataMap dataMap) {
437436
}
438437

439438
private CgenConfiguration createDefaultCgenConfiguration(DataMap map) {
440-
CgenConfiguration configuration = new CgenConfiguration();
441-
configuration.setName(CgenConfigList.DEFAULT_CONFIG_NAME);
439+
Path basePath = map.getLocation() != null ? CgenOps.baseDir(session) : null;
440+
CgenConfiguration configuration = CgenConfiguration.createDefault(map, basePath);
442441
configuration.setForce(true);
443-
configuration.setDataMap(map);
444-
445-
map.getObjEntities().forEach(configuration::loadEntity);
446-
map.getEmbeddables().forEach(configuration::loadEmbeddable);
447-
if (map.getLocation() != null) {
448-
Path basePath = CgenOps.baseDir(session);
449-
configuration.setRootPath(Utils.getRootPathForDataMap(map));
450-
configuration.updateOutputPath(basePath);
451-
}
452442
configuration.setEncoding(new GeneralPrefs(app.getPrefsLocator().appNode(GeneralPrefs.NODE)).getEncoding());
453443
return configuration;
454444
}

0 commit comments

Comments
 (0)