The linking feature allows for specification of cross-references within an Xtext grammar. The following things are needed for the linking:
declaration of a crosslink in the grammar (at least in the Ecore model)
specification of linking semantics (usually provided via the scoping API)
In the grammar a cross-reference is specified using square brackets.
CrossReference :
'[' type=ReferencedEClass ('|' terminal=CrossReferenceableTerminal)? ']'
;
Example:
ReferringType :
'ref' referencedObject=[Entity|STRING]
;
The Ecore model inference would create an EClass ReferringType with an EReference referencedObject of type Entity ( containment=false). The referenced object would be identified either by a STRING and the surrounding information in the current context (see scoping). If you do not use generate but import an existing Ecore model, the class ReferringType (or one of its super types) would need to have an EReference of type Entity (or one of its super types) declared. Also the EReference's containment and container properties needs to be set to false.
Xtext uses lazy linking by default and we encourage users to stick to this because it provides many advantages. One of which is improved performance in all scenarios where you don’t have to load the whole closure of all transitively referenced resources. Furthermore it automatically solves situations where one link relies on other links. Though cyclic linking dependencies are not supported by Xtext at all.
When parsing a given input string, say
ref Entity01
the LazyLinker first creates an EMF proxy and assigns it to the corresponding EReference. In EMF a proxy is described by a URI, which points to the real EObject. In the case of lazy linking the stored URI comprises of the context information given at parse time, which is the EObject containing the cross-reference, the actual EReference, the index (in case it’s a multi-valued cross-reference) and the string which represented the crosslink in the concrete syntax. The latter usually corresponds to the name of the referenced EObject. In EMF a URI consists of information about the resource the EObject is contained in as well as a so called fragment part, which is used to find the EObject within that resource. When an EMF proxy is resolved, the current ResourceSet is asked. The resource set uses the first part to obtain (i.e. load if it is not already loaded) the resource. Then the resource is asked to return the EObject based on the fragment in the URI. The actual cross-reference resolution is done by LazyLinkingResource.getEObject(String) which receives the fragment and delegates to the implementation of the ILinkingService. The default implementation in turn delegates to the scoping API.
A simple implementation of the linking service (the DefaultLinkingService) is shipped with Xtext and used for any grammar per default. Usually any necessary customization of the linking behavior can best be described using the scoping API.