Xtext relies heavily on EMF internally, but it can also be used as the serialization back-end of other EMF-based tools.
Xtext provides an implementation of EMF’s resource, the XtextResource. This does not only encapsulate the parser that converts text to an EMF model but also the serializer working the opposite direction. That way, an Xtext model just looks like any other Ecore-based model from the outside, making it amenable for the use by other EMF based tools. In fact, the Xpand templates in the generator plug-in created by the Xtext wizard do not make any assumption on the fact that the model is described in Xtext, and they would work fine with any model based on the same Ecore model of the language. So in the ideal case, you can switch the serialization format of your models to your self-defined DSL by just replacing the resource implementation used by your other modeling tools.
The generator fragment ResourceFactoryFragment registers a factory for the XtextResource to EMF’s resource factory registry, such that all tools using the default mechanism to resolve a resource implementation will automatically get that resource implementation.
Using a self-defined textual syntax as the primary storage format has a number of advantages over the default XMI serialization, e.g.
You can use well-known and easy-to-use tools and techniques for manipulation, such as text editors, regular expressions, or stream editors.
You can use the same tools for version control as you use for source code. Merging and diffing is performed in a syntax the developer is familiar with.
It is impossible to break the model such that it cannot be reopened in the editor again.
Models can be fixed using the same tools, even if they have become incompatible with a new version of the Ecore model.
Xtext targets easy to use and naturally feeling languages. It focuses on the lexical aspects of a language a bit more than on the semantic ones. As a consequence, a referenced Ecore model can contain more concepts than are actually covered by the Xtext grammar. As a result, not everything that is possibly expressed in the EMF model can be serialized back into a textual representation with regards to the grammar. So if you want to use Xtext to serialize your models as described above, it is good to have a couple of things in mind:
Prefer optional rule calls (cardinality
?
or
*
) to mandatory ones (cardinality
+
or default), such that missing references will not obstruct serialization.
You should never use an Xtext-Editor on the same model instance as a self-synchronizing other editor, e.g. a canonical GMF editor. The Xtext parser replaces re-parsed subtrees of the AST rather than modifying it, so elements will become stale. Additional information, such as graphical properties in the case of GMF, attached to these elements are likely to be lost. As the Xtext editor continuously re-parses the model on changes, this will happen rather often. It is safer to synchronize editors on file changes.
Implement an IFragmentProvider to make the XtextResource return stable fragments for its contained elements, e.g. based on composite names rather than order of appearance.
Although inter-Xtext linking is not done by URIs, you may want to be able to reference your
EObject
from non-Xtext models.
In those cases URIs are used, which are made up of a part identifying the resource. Each
EObject
contained in a resource can be identified by a so called
fragment.
A fragment is a part of an EMF URI and needs to be unique per resource.
The generic XMI resource shipped with EMF provides a generic path-like computation of fragments. With an XMI or other binary-like serialization it is also common and possible to use UUIDs.
However with a textual concrete syntax we want to be able to compute fragments out of the given information. We don’t want to force people to use UUIDs (i.e. synthetic identifiers) or relative generic paths (very fragile), in order to refer to
EObjects
.
Therefore one can contribute a so called
IFragmentProvider
per language.
public interface IFragmentProvider extends ILanguageService {
/**
* Computes the local ID of the given object.
* @param obj
* The EObject to compute the fragment for
* @return the fragment, which can be an arbitrary string but must be
* unique within a resource. Return null to use default
* implementation
*/
String getFragment(EObject obj);
/**
* Locates an EObject in a resource by its fragment.
* @return the EObject
*/
EObject getEObject(Resource resource, String fragment);
}
Note that the currently available default fragment provider does nothing (i.e. falls back to the default behavior of EMF).