The context element is used to locate the originating resource and obtain - * the appropriate {@link IModelLibraryProvider}. If no provider is available - * or the provider cannot resolve the name, this method returns {@code null}. + *
The lookup starts from the closest element in a resource: the context itself, or, for an
+ * element created outside the model, the closest of its effective containers that belongs to
+ * a resource (see {@link ElementUtil#getClosestElementInResource}). That element locates the
+ * originating resource, obtains the appropriate {@link IModelLibraryProvider} and is passed to
+ * it. If no provider is available or the provider cannot resolve the name, this method returns
+ * {@code null}.
*
* @param context the lookup context
* @param name the qualified library name to resolve
@@ -168,9 +171,17 @@ public static void setProviderLookup(ProviderLookup lookup) {
* resolved
*/
public static Element getLibraryElement(Element context, String name) {
- Resource resource = context == null ? null : context.eResource();
+ Element lookupContext = context;
+ Resource resource = null;
+ if (ElementUtil.getClosestElementInResource(context) instanceof Element attached) {
+ lookupContext = attached;
+ resource = attached.eResource();
+ }
IModelLibraryProvider provider = getInstance(resource);
- return provider == null ? null : provider.getElement(context, name);
+ if (provider == null) {
+ return null;
+ }
+ return provider.getElement(lookupContext, name);
}
/**
diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java
index ba23ba562..5af7bc9cb 100644
--- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java
+++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java
@@ -478,7 +478,9 @@ public static void insertImplicitSpecializations(Type type) {
newSpecialization.setIsImplied(true);
newSpecialization.setGeneral(general);
newSpecialization.setSpecific(type);
- if (general.getOwningRelationship() == null && !general.eIsProxy()) {
+ // Only a detached general, such as a feature chain, is adopted; a root Type of its
+ // resource stays where it is (see FeatureUtil.insertImplicitTypeFeaturings).
+ if (!general.eIsProxy() && general.getOwningRelationship() == null && general.eResource() == null) {
newSpecialization.getOwnedRelatedElement().add(general);
}
type.getOwnedRelationship().add(newSpecialization);
diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/VirtualContainer.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/VirtualContainer.java
new file mode 100644
index 000000000..1fb32628b
--- /dev/null
+++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/VirtualContainer.java
@@ -0,0 +1,107 @@
+/**
+ * SysML 2 Pilot Implementation
+ * Copyright (c) 2026 Obeo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Eclipse Public License, version 2, as published by
+ * the Eclipse Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Eclipse Public License for more details.
+ *
+ * You should have received a copy of the Eclipse Public License
+ * along with this program. If not, see
+ * The specification has no detached elements: the implied Relationship is owned by the element
+ * being constrained, and a feature chain it targets is owned by that Relationship. KerML §8.4.3.1:
+ * "In all cases, the source and owningRelatedElement of the Relationship is the Element being
+ * constrained, with the target being as given in the last column of the table." KerML §7.3.4.6:
+ * "the related element specified using the feature chain notation becomes an owned related feature
+ * of the relationship with the feature chain as notated." The implementation creates such elements
+ * before the Relationship that will own them; this adapter records their future owner so that
+ * scope lookups can reach the model from them. It is recorded when such an element is stored as
+ * the target of an implied relationship: an implicit general type, an implicit featuring type or
+ * an implicit binding connector, whose implied relationship is its owning membership.
+ *
+ * The link does not change the model. Once the element is really contained, its real container
+ * takes precedence: use {@link ElementUtil#getEffectiveContainer(EObject)} rather than this class.
+ *
+ * Specification quotations are from KerML 1.1 Beta 2.
+ */
+public final class VirtualContainer extends AdapterImpl {
+
+ private final Element container;
+
+ private VirtualContainer(Element container) {
+ this.container = container;
+ }
+
+ /**
+ * Records {@code container} as the virtual container of an element that is the root of a
+ * detached tree: it is not a proxy, has no container and is not in a resource. Any other element
+ * is left unchanged, as is an element that already has a virtual container.
+ *
+ * A model built without a resource has roots that are not in a resource either, and one of them
+ * can be stored as the target of an implied relationship owned by one of its own members, such
+ * as the featuring type of a nested feature. The link is not recorded when {@code element} is
+ * already an effective container of {@code container}, so that effective containers never form
+ * a cycle and {@link ElementUtil#getClosestElementInResource(EObject)} terminates.
+ *
+ * @param