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,14 @@
import "http://www.avaloq.com/tools/ddk/xtext/export/Export"

interface {
InterfaceExpression=unordered;
UserData=name;
}

export InterfaceExpression as ref
{
data ex = this.getExpr().toString();
}
export UserData as name
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*******************************************************************************
* 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 org.junit.jupiter.api.Test;

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 final ExportGeneratorX exportGeneratorX = getXtextTestUtil().get(ExportGeneratorX.class);

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

@Test
public void testShallowProjectUriFallsBackToProjectPackage() {
final ExportModel model = (ExportModel) getTestSource().getModel();

assertEquals("test.naming.ExportGeneratorXTestExportedNamesProvider", exportGeneratorX.getExportedNamesProvider(model));
assertEquals("test.resource.ExportGeneratorXTestResourceDescriptionManager", exportGeneratorX.getResourceDescriptionManager(model));
assertEquals("test.resource.ExportGeneratorXTestResourceDescriptionStrategy", exportGeneratorX.getResourceDescriptionStrategy(model));
assertEquals("test.resource.ExportGeneratorXTestResourceDescriptionConstants", exportGeneratorX.getResourceDescriptionConstants(model));
assertEquals("test.resource.ExportGeneratorXTestFingerprintComputer", exportGeneratorX.getFingerprintComputer(model));
assertEquals("test.resource.ExportGeneratorXTestFragmentProvider", exportGeneratorX.getFragmentProvider(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 @@ -23,6 +24,6 @@
* Empty class serving only as holder for JUnit4 annotations.
*/
@Suite
@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportScopingTest.class, ExportExportingTest.class})
@SelectClasses({ExportFormattingTest.class, ExportValidationTest.class, ExportScopingTest.class, ExportExportingTest.class, ExportGeneratorXTest.class})
public class ExportTestSuite {
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ import org.eclipse.xtext.Grammar

class ExportGeneratorX {

static val URI_PROJECT_SEGMENT_INDEX = 1
static val URI_PACKAGE_START_INDEX = 3
static val DEFAULT_PACKAGE_SEGMENT = "generated"

@Inject
extension Naming

Expand All @@ -51,49 +55,90 @@ class ExportGeneratorX {
}

def String getExportedNamesProvider(ExportModel model) {
val uri = model.eResource().getURI();
// TODO this is a hack; to support modularization we should probably add name to export models (as with scope models)
return String.join(".", uri.segmentsList().subList(3, uri.segmentCount() - 1)) + ".naming." + getName(model) + "ExportedNamesProvider";
return model.basePackage + ".naming." + getName(model) + "ExportedNamesProvider";
}

def String getResourceDescriptionManager(ExportModel model) {
val uri = model.eResource().getURI();
// TODO this is a hack; to support modularization we should probably add name to export models (as with scope models)
return String.join(".", uri.segmentsList().subList(3, uri.segmentCount() - 1)) + ".resource." + getName(model) + "ResourceDescriptionManager";
return model.basePackage + ".resource." + getName(model) + "ResourceDescriptionManager";
}

def String getResourceDescriptionManager(Grammar grammar) {
return grammar.name.toJavaPackage + ".resource." + grammar.name.toSimpleName + "ResourceDescriptionManager";
}

def String getResourceDescriptionStrategy(ExportModel model) {
val uri = model.eResource().getURI();
// TODO this is a hack; to support modularization we should probably add name to export models (as with scope models)
return String.join(".", uri.segmentsList().subList(3, uri.segmentCount() - 1)) + ".resource." + getName(model) + "ResourceDescriptionStrategy";
return model.basePackage + ".resource." + getName(model) + "ResourceDescriptionStrategy";
}

def String getResourceDescriptionConstants(ExportModel model) {
val uri = model.eResource().getURI();
// TODO this is a hack; to support modularization we should probably add name to export models (as with scope models)
return String.join(".", uri.segmentsList().subList(3, uri.segmentCount() - 1)) + ".resource." + getName(model) + "ResourceDescriptionConstants";
return model.basePackage + ".resource." + getName(model) + "ResourceDescriptionConstants";
}

def String getFingerprintComputer(ExportModel model) {
val uri = model.eResource().getURI();
// TODO this is a hack; to support modularization we should probably add name to export models (as with scope models)
return String.join(".", uri.segmentsList().subList(3, uri.segmentCount() - 1)) + ".resource." + getName(model) + "FingerprintComputer";
return model.basePackage + ".resource." + getName(model) + "FingerprintComputer";
}

def String getFragmentProvider(ExportModel model) {
val uri = model.eResource().getURI();
// TODO this is a hack; to support modularization we should probably add name to export models (as with scope models)
return String.join(".", uri.segmentsList().subList(3, uri.segmentCount() - 1)) + ".resource." + getName(model) + "FragmentProvider";
return model.basePackage + ".resource." + getName(model) + "FragmentProvider";
}

def String getExportFeatureExtension(ExportModel model) {
val uri = model.eResource().getURI();
// TODO we still need to add a package to the models. Extension models already have a name in contrast to cases above
return String.join(".", uri.segmentsList().subList(3, uri.segmentCount() - 1)) + ".resource." + model.name + "ExportFeatureExtension";
return model.basePackage + ".resource." + model.name + "ExportFeatureExtension";
}

private def String getBasePackage(ExportModel model) {
val uri = model.eResource.URI
val packageFromUri = uri.packageFromUri
if (packageFromUri !== null) {
return packageFromUri
}
return uri.fallbackPackage
}

private def String getPackageFromUri(org.eclipse.emf.common.util.URI uri) {
val packageSegments = uri.segmentsList
if (packageSegments.size > URI_PACKAGE_START_INDEX + 1 && "src".equals(packageSegments.get(URI_PROJECT_SEGMENT_INDEX + 1))) {
return String.join(".", packageSegments.subList(URI_PACKAGE_START_INDEX, uri.segmentCount - 1))
}
return null
}

private def String getFallbackPackage(org.eclipse.emf.common.util.URI uri) {
val segments = uri.segmentsList
if (segments.size > URI_PROJECT_SEGMENT_INDEX) {
return segments.get(URI_PROJECT_SEGMENT_INDEX).safePackageSegment
}
return DEFAULT_PACKAGE_SEGMENT
}

private def String getSafePackageSegment(String segment) {
if (segment === null || segment.empty) {
return DEFAULT_PACKAGE_SEGMENT
}
val normalizedSegment = segment.toLowerCase
val builder = new StringBuilder()
for (var i = 0; i < normalizedSegment.length; i++) {
val character = normalizedSegment.charAt(i)
if (builder.length == 0) {
if (Character.isJavaIdentifierStart(character)) {
builder.append(character)
} else if (Character.isJavaIdentifierPart(character)) {
builder.append('_').append(character)
} else {
builder.append('_')
}
} else {
builder.append(if (Character.isJavaIdentifierPart(character)) character else '_')
}
}
return if (builder.length == 0) DEFAULT_PACKAGE_SEGMENT else builder.toString
}

/**
Expand Down