Skip to content

Commit efb2b59

Browse files
committed
Resolve cross-resource qualified names through a Package short name
1 parent 3208410 commit efb2b59

7 files changed

Lines changed: 189 additions & 10 deletions

File tree

org.omg.kerml.xtext/META-INF/MANIFEST.MF

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Export-Package: org.omg.kerml.xtext,
3030
org.omg.kerml.xtext.naming,
3131
org.omg.kerml.xtext.parser.antlr,
3232
org.omg.kerml.xtext.parser.antlr.internal,
33+
org.omg.kerml.xtext.resource,
3334
org.omg.kerml.xtext.scoping,
3435
org.omg.kerml.xtext.serializer,
3536
org.omg.kerml.xtext.services,

org.omg.kerml.xtext/src/org/omg/kerml/xtext/KerMLRuntimeModule.xtend

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import org.omg.kerml.xtext.library.LibraryNamespaces
1818
import org.omg.kerml.xtext.linking.KerMLLazyLinkingResource
1919
import org.omg.kerml.xtext.naming.KerMLQualifiedNameConverter
2020
import org.omg.kerml.xtext.naming.KerMLQualifiedNameProvider
21+
import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy
22+
import org.omg.kerml.xtext.resource.KerMLResourceDescriptionStrategy
2123
import org.omg.kerml.xtext.scoping.KerMLGlobalScopeProvider
2224
import org.omg.kerml.xtext.scoping.KerMLLinker
2325
import org.omg.kerml.xtext.validation.KerMLResourceValidator
@@ -76,6 +78,10 @@ class KerMLRuntimeModule extends AbstractKerMLRuntimeModule {
7678
KerMLQualifiedNameProvider
7779
}
7880

81+
def Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
82+
KerMLResourceDescriptionStrategy
83+
}
84+
7985
override Class<? extends ILinker> bindILinker() {
8086
KerMLLinker
8187
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* SysML 2 Pilot Implementation
3+
* Copyright (C) 2026 tkanov
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the Eclipse Public License, version 2, as published by
7+
* the Eclipse Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* Eclipse Public License for more details.
13+
*
14+
* You should have received a copy of the Eclipse Public License
15+
* along with this program. If not, see <https://www.eclipse.org/legal/epl-2.0/>.
16+
*
17+
* @license EPL-2.0 <http://spdx.org/licenses/EPL-2.0>
18+
*/
19+
package org.omg.kerml.xtext.resource;
20+
21+
import org.eclipse.emf.ecore.EObject;
22+
import org.eclipse.xtext.naming.IQualifiedNameConverter;
23+
import org.eclipse.xtext.naming.QualifiedName;
24+
import org.eclipse.xtext.resource.EObjectDescription;
25+
import org.eclipse.xtext.resource.IEObjectDescription;
26+
import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy;
27+
import org.eclipse.xtext.util.IAcceptor;
28+
import org.omg.sysml.lang.sysml.Element;
29+
import org.omg.sysml.lang.sysml.Namespace;
30+
import org.omg.sysml.util.ElementUtil;
31+
32+
import com.google.inject.Inject;
33+
34+
/**
35+
* A resource description strategy that exports root Elements under their
36+
* <code>declaredShortName</code>, in addition to the <code>declaredName</code>-based
37+
* qualified name exported by {@link org.omg.kerml.xtext.naming.KerMLQualifiedNameProvider}.
38+
*
39+
* <p>Local (same-resource) name resolution matches both the <code>memberName</code> and the
40+
* <code>memberShortName</code> of a Membership. Without the additional exported names, the
41+
* first segment of a cross-resource qualified name could only be resolved by
42+
* <code>declaredName</code>, so a reference such as <code>P::Foo</code>, where <code>P</code>
43+
* is the short name of a root Package in another resource, would not resolve.
44+
*
45+
* <p>Only names of root Elements (that is, names with a single segment) are exported this way,
46+
* because those are the only ones used to resolve the first segment of a cross-resource
47+
* qualified name (see
48+
* {@link org.omg.kerml.xtext.scoping.KerMLGlobalScopeProvider#getScope}). Short names of
49+
* nested Elements continue to be handled by the local scope provider.
50+
*/
51+
public class KerMLResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
52+
53+
@Inject
54+
private IQualifiedNameConverter qualifiedNameConverter;
55+
56+
@Override
57+
public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
58+
boolean result = super.createEObjectDescriptions(eObject, acceptor);
59+
QualifiedName shortQualifiedName = getShortQualifiedName(eObject);
60+
if (shortQualifiedName != null) {
61+
acceptor.accept(EObjectDescription.create(shortQualifiedName, eObject));
62+
result = true;
63+
}
64+
return result;
65+
}
66+
67+
/**
68+
* Return the qualified name to export for the <code>declaredShortName</code> of the given
69+
* object, or null if it does not have one, or if it is not a root Element.
70+
*/
71+
protected QualifiedName getShortQualifiedName(EObject eObject) {
72+
if (!(eObject instanceof Element) || getQualifiedNameProvider() == null) {
73+
return null;
74+
}
75+
String shortName = ((Element)eObject).getDeclaredShortName();
76+
if (shortName == null || shortName.isEmpty()) {
77+
return null;
78+
}
79+
// Only root Elements are exported under their short name. A root Element is owned by the
80+
// root Namespace of its resource, which is not, itself, owned by another Namespace.
81+
Namespace owningNamespace = ((Element)eObject).getOwningNamespace();
82+
if (owningNamespace == null || owningNamespace.getOwningNamespace() != null) {
83+
return null;
84+
}
85+
QualifiedName qualifiedName = getQualifiedNameProvider().getFullyQualifiedName(eObject);
86+
QualifiedName shortQualifiedName =
87+
qualifiedNameConverter.toQualifiedName("'" + ElementUtil.escapeString(shortName) + "'");
88+
return shortQualifiedName.equals(qualifiedName)? null: shortQualifiedName;
89+
}
90+
91+
}

org.omg.kerml.xtext/src/org/omg/kerml/xtext/scoping/KerMLScope.xtend

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright (c) 2018 IncQuery Labs Ltd.
44
* Copyright (c) 2018-2022, 2024, 2025 Model Driven Solutions, Inc.
55
* Copyright (c) 2018-2020 California Institute of Technology/Jet Propulsion Laboratory
6+
* Copyright (c) 2026 tkanov
67
*
78
* This program is free software: you can redistribute it and/or modify
89
* it under the terms of the Eclipse Public License as published by
@@ -51,18 +52,13 @@ import org.omg.sysml.lang.sysml.OwningMembership
5152
import org.omg.sysml.lang.sysml.NamespaceImport
5253
import org.omg.sysml.lang.sysml.MembershipImport
5354
import org.omg.sysml.lang.sysml.SysMLPackage
54-
import com.google.inject.Inject
55-
import org.eclipse.xtext.naming.IQualifiedNameConverter
5655
import org.eclipse.emf.ecore.util.EcoreUtil
5756
import org.omg.sysml.util.NamespaceUtil
5857
import org.omg.kerml.xtext.naming.QualifiedNameUtil
5958
import org.omg.sysml.lang.sysml.Redefinition
6059

61-
class KerMLScope extends AbstractScope {
62-
63-
@Inject
64-
IQualifiedNameConverter qualifiedNameConverter
65-
60+
class KerMLScope extends AbstractScope {
61+
6662
/*
6763
* The following fields are fixed on construction.
6864
*/
@@ -162,8 +158,14 @@ class KerMLScope extends AbstractScope {
162158
!resolveInScope(QualifiedName.create(input.name.firstSegment), true).isEmpty()
163159
}
164160

165-
def getElement(String name) {
166-
var obj = EcoreUtil.resolve(getSingleElement(qualifiedNameConverter.toQualifiedName(name)).EObjectOrProxy, element)
161+
def Element getElement(String name) {
162+
// Note: The qualified name converter is obtained from the scopeProvider, because a
163+
// KerMLScope is constructed directly, rather than being injected.
164+
val description = getSingleElement(scopeProvider.qualifiedNameConverter.toQualifiedName(name))
165+
if (description === null) {
166+
return null
167+
}
168+
var obj = EcoreUtil.resolve(description.EObjectOrProxy, element)
167169
if (obj instanceof Element) obj else null
168170
}
169171

org.omg.kerml.xtext/src/org/omg/kerml/xtext/scoping/KerMLScopeProvider.xtend

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright (c) 2018 IncQuery Labs Ltd.
44
* Copyright (c) 2018-2022, 2024 Model Driven Solutions, Inc.
55
* Copyright (c) 2018, 2019 California Institute of Technology/Jet Propulsion Laboratory
6+
* Copyright (c) 2026 tkanov
67
*
78
* This program is free software: you can redistribute it and/or modify
89
* it under the terms of the Eclipse Public License as published by
@@ -31,6 +32,7 @@ package org.omg.kerml.xtext.scoping
3132

3233
import com.google.common.base.Predicates
3334
import com.google.inject.Inject
35+
import org.eclipse.xtext.naming.IQualifiedNameConverter
3436
import java.util.Set
3537
import org.eclipse.emf.ecore.EObject
3638
import org.eclipse.emf.ecore.EReference
@@ -57,6 +59,13 @@ class KerMLScopeProvider extends AbstractKerMLScopeProvider {
5759
@Inject
5860
IGlobalScopeProvider globalScope
5961

62+
@Inject
63+
IQualifiedNameConverter qualifiedNameConverter
64+
65+
def getQualifiedNameConverter() {
66+
qualifiedNameConverter
67+
}
68+
6069
@Inject
6170
LibraryNamespaces libraryNamespaces
6271

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* SysML 2 Pilot Implementation
3+
* Copyright (C) 2026 tkanov
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the Eclipse Public License, version 2, as published by
7+
* the Eclipse Foundation.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* Eclipse Public License for more details.
13+
*
14+
* You should have received a copy of the Eclipse Public License
15+
* along with this program. If not, see <https://www.eclipse.org/legal/epl-2.0/>.
16+
*
17+
* @license EPL-2.0 <http://spdx.org/licenses/EPL-2.0>
18+
*/
19+
package org.omg.sysml.interactive.tests;
20+
21+
import static org.junit.Assert.assertTrue;
22+
23+
import org.junit.Test;
24+
import org.omg.sysml.interactive.SysMLInteractive;
25+
import org.omg.sysml.interactive.SysMLInteractiveResult;
26+
27+
/**
28+
* Tests for resolving qualified names whose first segment is the short name of a
29+
* Package, both within a single resource and across resources (see issue #778).
30+
*/
31+
public class ShortNameScopeTest extends SysMLInteractiveTest {
32+
33+
private void assertNoIssues(SysMLInteractive instance, String input) {
34+
SysMLInteractiveResult result = instance.process(input);
35+
assertTrue("'" + input + "':\n" + result.formatIssues(), result.getIssues().isEmpty());
36+
}
37+
38+
@Test
39+
public void testShortNameQualificationSameResource() throws Exception {
40+
SysMLInteractive instance = createSysMLInteractiveInstance();
41+
assertNoIssues(instance, "package <P> Alpha { part def Foo; } package Beta { part x : P::Foo; }");
42+
}
43+
44+
@Test
45+
public void testDeclaredNameQualificationAcrossResources() throws Exception {
46+
SysMLInteractive instance = createSysMLInteractiveInstance();
47+
assertNoIssues(instance, "package <P> Alpha { part def Foo; }");
48+
assertNoIssues(instance, "package Beta { part x : Alpha::Foo; }");
49+
}
50+
51+
@Test
52+
public void testShortNameQualificationAcrossResources() throws Exception {
53+
SysMLInteractive instance = createSysMLInteractiveInstance();
54+
assertNoIssues(instance, "package <P> Alpha { part def Foo; }");
55+
assertNoIssues(instance, "package Beta { part x : P::Foo; }");
56+
}
57+
58+
@Test
59+
public void testShortNameImportAcrossResources() throws Exception {
60+
SysMLInteractive instance = createSysMLInteractiveInstance();
61+
assertNoIssues(instance, "package <P> Alpha { part def Foo; }");
62+
assertNoIssues(instance, "package Beta { private import P::*; part x : Foo; }");
63+
}
64+
}

org.omg.sysml.xtext/src/org/omg/sysml/xtext/SysMLRuntimeModule.xtend

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import org.eclipse.xtext.validation.IResourceValidator
1616
import org.omg.kerml.xtext.linking.KerMLLazyLinkingResource
1717
import org.omg.kerml.xtext.conversion.KerMLValueConverterService
1818
import org.omg.kerml.xtext.naming.KerMLQualifiedNameProvider
19+
import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy
20+
import org.omg.kerml.xtext.resource.KerMLResourceDescriptionStrategy
1921
import org.omg.kerml.xtext.scoping.KerMLLinker
2022
import org.omg.kerml.xtext.validation.KerMLResourceValidator
2123
import org.omg.sysml.logic.api.IModelLibraryProvider
@@ -65,7 +67,11 @@ class SysMLRuntimeModule extends AbstractSysMLRuntimeModule {
6567
override Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
6668
KerMLQualifiedNameProvider
6769
}
68-
70+
71+
def Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
72+
KerMLResourceDescriptionStrategy
73+
}
74+
6975
override Class<? extends ILinker> bindILinker() {
7076
KerMLLinker
7177
}

0 commit comments

Comments
 (0)