Skip to content
Draft
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
@@ -0,0 +1,99 @@
/*******************************************************************************
* Copyright (c) 2026 Avaloq Group AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Avaloq Group AG - initial API and implementation
*******************************************************************************/
package com.avaloq.tools.ddk.xtext.export.generator;

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

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.XtextFactory;
import org.junit.jupiter.api.Test;

import com.avaloq.tools.ddk.xtext.export.export.ExportFactory;
import com.avaloq.tools.ddk.xtext.export.export.ExportModel;
import com.avaloq.tools.ddk.xtext.test.export.util.ExportTestUtil;
import com.avaloq.tools.ddk.xtext.test.jupiter.AbstractXtextTest;


/**
* Regression tests for URI-based package derivation in {@link ExportGeneratorX}.
*/
@SuppressWarnings("nls")
public class ExportGeneratorXTest extends AbstractXtextTest {

private static final String DERIVED_PACKAGE_PROVIDER = "com.avaloq.naming.fooExportedNamesProvider";
private static final String PROJECT_FALLBACK_PROVIDER = "MyProject.naming.fooExportedNamesProvider";
private static final String DEFAULT_FALLBACK_PROVIDER = "generated.naming.fooExportedNamesProvider";
private static final String NO_SIBLING_URI = "platform:/resource/TEST/NoSibling.export";

private final ExportGeneratorX exportGeneratorX = getXtextTestUtil().get(ExportGeneratorX.class);

@Override
protected ExportTestUtil getXtextTestUtil() {
return ExportTestUtil.getInstance();
}

@Override
protected String getTestSourceFileName() {
return null; // all tests fabricate synthetic resource URIs
}

@Test
public void testNestedSourceFolderUrisKeepDerivedPackage() {
assertEquals(DERIVED_PACKAGE_PROVIDER, exportedNamesProviderFor("platform:/resource/MyProject/src/com/avaloq/foo.export"));
assertEquals(DERIVED_PACKAGE_PROVIDER, exportedNamesProviderFor("platform:/resource/MyProject/src-gen/com/avaloq/foo.export"));
assertEquals("main.java.com.avaloq.naming.fooExportedNamesProvider", exportedNamesProviderFor("platform:/resource/MyProject/src/main/java/com/avaloq/foo.export"));
}

@Test
public void testShortUrisFallBackToValidPackage() {
assertEquals(PROJECT_FALLBACK_PROVIDER, exportedNamesProviderFor("platform:/resource/MyProject/foo.export"));
assertEquals(PROJECT_FALLBACK_PROVIDER, exportedNamesProviderFor("platform:/resource/MyProject/src/foo.export"));
assertEquals(DEFAULT_FALLBACK_PROVIDER, exportedNamesProviderFor("platform:/resource/1My-Project/foo.export"));
}

@Test
public void testSingleSegmentUriFallsBackToDefaultPackage() {
assertEquals(DEFAULT_FALLBACK_PROVIDER, exportedNamesProviderFor("foo.export"));
}

@Test
public void testGetGrammarReturnsNullWithoutResolvableGrammar() {
final ExportModel detached = ExportFactory.eINSTANCE.createExportModel();
assertNull(exportGeneratorX.getGrammar(detached), "model without a resource");

final ExportModel withoutResourceSet = ExportFactory.eINSTANCE.createExportModel();
new ResourceImpl(URI.createURI(NO_SIBLING_URI)).getContents().add(withoutResourceSet);
assertNull(exportGeneratorX.getGrammar(withoutResourceSet), "resource without a resource set");

final ExportModel withoutSibling = ExportFactory.eINSTANCE.createExportModel();
final Resource resource = new ResourceSetImpl().createResource(URI.createURI(NO_SIBLING_URI));
resource.getContents().add(withoutSibling);
assertNull(exportGeneratorX.getGrammar(withoutSibling), "missing sibling grammar must not throw");

final Grammar proxy = XtextFactory.eINSTANCE.createGrammar();
((InternalEObject) proxy).eSetProxyURI(URI.createURI("platform:/resource/TEST/Unresolved.xtext#/"));
withoutSibling.setTargetGrammar(proxy);
assertNull(exportGeneratorX.getGrammar(withoutSibling), "unresolved targetGrammar proxy");
}

private String exportedNamesProviderFor(final String uri) {
final Resource resource = new ResourceImpl(URI.createURI(uri));
final ExportModel model = ExportFactory.eINSTANCE.createExportModel();
resource.getContents().add(model);
return exportGeneratorX.getExportedNamesProvider(model);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;

import com.avaloq.tools.ddk.xtext.export.generator.ExportGeneratorXTest;
import com.avaloq.tools.ddk.xtext.export.exporting.ExportExportingTest;
import com.avaloq.tools.ddk.xtext.export.formatting.ExportFormattingTest;
import com.avaloq.tools.ddk.xtext.export.scoping.ExportScopingTest;
Expand All @@ -24,6 +25,6 @@
* Empty class serving only as holder for JUnit 5 suite annotations.
*/
@Suite
@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportValidationOkTest.class, ExportScopingTest.class, ExportExportingTest.class})
@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportValidationOkTest.class, ExportScopingTest.class, ExportExportingTest.class, ExportGeneratorXTest.class})
public class ExportTestSuite {
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.google.inject.Inject
import java.util.Collection
import java.util.List
import java.util.Map
import javax.lang.model.SourceVersion
import org.eclipse.emf.ecore.EAttribute
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EClassifier
Expand All @@ -42,22 +43,35 @@ class ExportGeneratorX {
}

def Grammar getGrammar(ExportModel model) {
val uri = model.eResource.URI
// Grammar should be set correctly for export extensions, not yet for normal export sources
if(model.targetGrammar !== null) {
if (model.targetGrammar !== null && !model.targetGrammar.eIsProxy) {
return model.targetGrammar;
}
val grammarResource = model.eResource.resourceSet.getResource(uri.trimSegments(1).appendSegment(uri.trimFileExtension.lastSegment + '.xtext'), true)
return grammarResource?.contents.head as Grammar
val resource = model.eResource
if (resource?.resourceSet === null) {
return null
}
val uri = resource.URI
try {
val grammarResource = resource.resourceSet.getResource(uri.trimSegments(1).appendSegment(uri.trimFileExtension.lastSegment + '.xtext'), true)
return grammarResource?.contents.head as Grammar
} catch (RuntimeException e) {
// no resolvable sibling grammar; all generators tolerate a null grammar
return null
}
}

def List<String> getPrefix(URI uri) {
// TODO we still need to add a package to the models. Extension models already have a name in contrast to cases above
if (uri.segmentsList().size > 3) {
return uri.segmentsList().subList(3, uri.segmentCount() - 1);
} else {
return uri.segmentsList().subList(uri.segmentCount() - 2, uri.segmentCount() - 1);
val segments = uri.segmentsList
if (segments.size > 4) {
return segments.subList(3, segments.size - 1)
}
// shorter shapes have no package segments: fall back to a valid package name
if (segments.size > 2 && SourceVersion.isName(segments.get(1))) {
return #[segments.get(1)]
}
return #['generated']
}

def String getExportedNamesProvider(ExportModel model) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ protected GeneratorUtil() {
*/
public static Set<EClass> allInstantiatedTypes(final Grammar grammar) {
Set<EClass> result = Sets.newLinkedHashSet();
collectInstantiatedTypes(grammar, result);
if (grammar != null) { // no grammar -> no parser-instantiated subtypes
collectInstantiatedTypes(grammar, result);
}
return result;
}

Expand Down