Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.regnosys.rosetta.config;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.net.URL;
import java.net.URLClassLoader;
Expand Down Expand Up @@ -82,4 +84,47 @@ void configFileAndClasspathClassLoaderSurviveACustomCreateInjector(@TempDir Path
config.findSchemaConfig("parentSchema").orElseThrow().getConfigPath());
}
}

@Test
void missingProjectConfigDoesNotUseDependencyGeneratorSettings(@TempDir Path tempDir) throws Exception {
Path dependencyRoot = Files.createDirectory(tempDir.resolve("dependency-classes"));
Files.writeString(dependencyRoot.resolve("rune-config.yml"),
"model:\n name: Parent Model\ngenerators:\n namespaces:\n - parent.only\n"
+ "namespaceConfig:\n- id: parentSchema\n namespace: parent.ns\n"
+ " schemaConfig:\n schema: parentSchema\n configPath: xml-config/parent-config.json\n");

try (URLClassLoader dependencyClassLoader =
new URLClassLoader(new URL[] { dependencyRoot.toUri().toURL() }, null)) {
Injector injector = new CustomModuleSetup()
.setConfigFile(null)
.setClasspathClassLoader(dependencyClassLoader)
.createInjectorAndDoEMFRegistration();

RuneConfiguration config = injector.getInstance(RuneConfigurationHolder.class).get();

assertEquals("Just another Rosetta model", config.getModel().getName());
assertTrue(config.getGenerators().getNamespaces().isEmpty());
assertTrue(config.getGenerators().getNamespaceFilter().test("child.ns"));
assertEquals("xml-config/parent-config.json",
config.findSchemaConfig("parentSchema").orElseThrow().getConfigPath());
}
}

@Test
void unsetProjectConfigStillUsesClasspathDiscovery(@TempDir Path tempDir) throws Exception {
Files.writeString(tempDir.resolve("rune-config.yml"),
"model:\n name: Classpath Model\ngenerators:\n namespaces:\n - classpath.only\n");

try (URLClassLoader classLoader = new URLClassLoader(new URL[] { tempDir.toUri().toURL() }, null)) {
Injector injector = new CustomModuleSetup()
.setClasspathClassLoader(classLoader)
.createInjectorAndDoEMFRegistration();

RuneConfiguration config = injector.getInstance(RuneConfigurationHolder.class).get();

assertEquals("Classpath Model", config.getModel().getName());
assertTrue(config.getGenerators().getNamespaceFilter().test("classpath.only"));
assertFalse(config.getGenerators().getNamespaceFilter().test("child.ns"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
public class RosettaStandaloneSetup extends RosettaStandaloneSetupGenerated {

private String configFile;
private boolean configFileSet;
private ClassLoader classpathClassLoader;

public static void doSetup() {
Expand All @@ -27,10 +28,13 @@ public static void doSetup() {

/**
* Points the setup at an explicit configuration file, instead of discovering the project's own
* config on the classpath. Applied by {@link #createInjectorAndDoEMFRegistration()}.
* config on the classpath. Passing {@code null} explicitly declares that the project has no
* config, while not calling this method leaves classpath discovery enabled. Applied by
* {@link #createInjectorAndDoEMFRegistration()}.
*/
public RosettaStandaloneSetup setConfigFile(String configFile) {
this.configFile = configFile;
this.configFileSet = true;
return this;
}

Expand Down Expand Up @@ -85,7 +89,7 @@ public Injector createInjectorAndDoEMFRegistration() {
* use, so this is still in time.
*/
private void configureRuneConfigurationFileProvider(Injector injector) {
if (configFile == null && classpathClassLoader == null) {
if (!configFileSet && classpathClassLoader == null) {
return;
}
RuneConfigurationFileProvider fileProvider = injector.getInstance(RuneConfigurationFileProvider.class);
Expand All @@ -96,7 +100,7 @@ private void configureRuneConfigurationFileProvider(Injector injector) {
+ "configuration. Remove the non-singleton binding of "
+ RuneConfigurationFileProvider.class.getName() + " from the runtime module.");
}
if (configFile != null) {
if (configFileSet) {
fileProvider.setConfigFile(configFile);
}
if (classpathClassLoader != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ public Map<String, LanguageAccess> createLanguageAccess(ILanguageConfiguration l
+ " must implement " + RosettaStandaloneSetup.class.getName());
}
RosettaStandaloneSetup setup = (RosettaStandaloneSetup) loadClass.getDeclaredConstructor().newInstance();
if (rosettaConfig != null) {
setup.setConfigFile(rosettaConfig);
}
// A null value explicitly means this project has no config; dependency configs must not
// become the primary config and restrict this project's generators.
setup.setConfigFile(rosettaConfig);
// The thread context classloader during the build is the plugin realm, which cannot see the
// project's compile dependencies. Hand the config provider a classloader over the project
// classpath so dependency serializationConfig entries are discovered and unioned.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2026 REGnosys
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.regnosys.rosetta.maven;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Set;

import org.eclipse.xtext.builder.standalone.ILanguageConfiguration;
import org.eclipse.xtext.generator.OutputConfiguration;
import org.junit.jupiter.api.Test;

import com.regnosys.rosetta.RosettaStandaloneSetup;

class RuneLanguageAccessFactoryTest {

@Test
void nullConfigIsExplicitlyForwardedToTheLanguageSetup() {
assertConfigIsForwarded(null);
}

@Test
void explicitConfigIsForwardedToTheLanguageSetup() {
assertConfigIsForwarded("/project/rune-config.yml");
}

private void assertConfigIsForwarded(String configFile) {
RecordingSetup.configFileSet = false;
RecordingSetup.configFile = "not-set";

ILanguageConfiguration languageConfiguration = new ILanguageConfiguration() {
@Override
public String getSetup() {
return RecordingSetup.class.getName();
}

@Override
public Set<OutputConfiguration> getOutputConfigurations() {
return Set.of();
}

@Override
public boolean isJavaSupport() {
return false;
}
};

new RuneLanguageAccessFactory().createLanguageAccess(
languageConfiguration, configFile, getClass().getClassLoader(), null);

assertTrue(RecordingSetup.configFileSet);
assertEquals(configFile, RecordingSetup.configFile);
}

public static final class RecordingSetup extends RosettaStandaloneSetup {
private static boolean configFileSet;
private static String configFile;

@Override
public RosettaStandaloneSetup setConfigFile(String configFile) {
RecordingSetup.configFileSet = true;
RecordingSetup.configFile = configFile;
return super.setConfigFile(configFile);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@ public RuneConfiguration get() {
protected RuneConfiguration readConfigFromFile() {
try {
URL primaryFile = fileProvider.get();
RuneConfiguration primary;
if (primaryFile == null) {
LOGGER.warn("No configuration file was found. Falling back to the default configuration.");
return null;
LOGGER.warn("No project configuration file was found. Falling back to the default configuration.");
primary = fallback.get();
} else {
primary = configurationService.read(primaryFile);
}
RuneConfiguration primary = configurationService.read(primaryFile);

// The model and generators come from the current project's config only.
// The namespace config is the union of all configs on the classpath (the current
Expand All @@ -57,7 +59,7 @@ protected RuneConfiguration readConfigFromFile() {
Set<String> seenIds = new HashSet<>();
collectNamespaceConfig(primary, mergedNamespaceConfig, seenIds);
for (URL file : fileProvider.getResources()) {
if (file.equals(primaryFile)) {
if (primaryFile != null && file.equals(primaryFile)) {
continue;
}
collectNamespaceConfig(configurationService.read(file), mergedNamespaceConfig, seenIds);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ public static RuneConfigurationFileProvider createFromClasspath(String fileName)

/**
* Points this provider at an explicit configuration file on disk, instead of discovering the
* primary config on the classpath. Dependency configs are still discovered from the classpath
* (see {@link #getResources()}).
* primary config on the classpath. A {@code null} value explicitly means that the current
* project has no config. Dependency configs are still discovered from the classpath (see
* {@link #getResources()}).
*/
public void setConfigFile(String configFile) {
this.loadFromClasspath = false;
Expand Down Expand Up @@ -81,6 +82,9 @@ private RuneConfigurationFileProvider(boolean loadFromClasspath, String fileName

@Override
public URL get() {
if (!loadFromClasspath && fileName == null) {
return null;
}
if (fileName != null) {
// An explicit file name was requested: resolve it as-is, without any fallback.
return loadFromClasspath ? fromClasspath(fileName) : requireFile(fileName);
Expand Down