hi folks,
I have an external ecore meta model for which I created two grammars.
The meta model looks like this:
System <>---*- Module <>---1- InterfaceModel <>---*- Interface
A System consists of modules. Each module has one InterfaceModel which in turn has multiple Interfaces. (A diamond (<>) denotes a containment reference.)
The creation of models is splitted up into two DSLs - a system DSL and an interface DSL. With the system DSL only the availbale modules should be listed. Its grammar looks like this:
System returns modulob::System:
'system' name=ID 'consistsOf' modules += Module (',' modules += Module)*
;
Module returns modulob::Module:
'module' name=ID
;
With the interface DSL the interfaces for a module should be listed in a different file. Its grammar (extract):
InterfaceModel returns modulob::InterfaceModel:
'interfaces for module'
module = [system::Module | QualifiedName]
(interfaces += Interface)*
;
Interface returns modulob::Interface:
'interface' name = ID
(
'requires' requiredInterfaces += [modulob::Interface | QualifiedName]
(',' requiredInterfaces += [modulob::Interface | QualifiedName])*
)?
;
Here are some sample models. A system model:
system TradingOffice consistsOf
module Journal,
module CurrencyConversion
Two interface models - one for each module:
interfaces for module TradingOffice.CurrencyConversion version "1.2"
interface Bar
interfaces for module TradingOffice.Journal version "1.2"
interface Foo requires CurrencyConversion.Bar //not possible, only 'Bar' is possible
The first challenge is that interfaces cannot be referenced by their qualified name. Only their simple name is offered in the content assist for
specifying cross references. I thought the reason is that the meta class InterfaceModel does not contain an EAttribute 'name' and thus the computation of the qualified name of DefaultDeclarativeQualifiedNameProvider does not succeed.
I implemented my own IQualifiedNameProvider:
@Override
public QualifiedName getFullyQualifiedName(final EObject obj) {
if (obj instanceof Interface) {
Interface i = (Interface) obj;
Module m = ((InterfaceModel)i.eContainer()).getModule();//throws exception
return QualifiedName.create(m.getName(), i.getName());
}
return super.getFullyQualifiedName(obj);
}
If I test it, I get this exception:
java.lang.AssertionError: Cyclic resolution of lazy links : InterfaceModel.module->InterfaceModel.module
at org.eclipse.xtext.linking.lazy.LazyLinkingResource.handleCyclicResolution(LazyLinkingResource.java:207)
at org.eclipse.xtext.linking.lazy.LazyLinkingResource.getEObject(LazyLinkingResource.java:160)
at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.getEObject(ResourceSetImpl.java:219)
at org.eclipse.emf.ecore.util.EcoreUtil.resolve(EcoreUtil.java:203)
at org.eclipse.emf.ecore.util.EcoreUtil.resolve(EcoreUtil.java:263)
at org.eclipse.emf.ecore.impl.BasicEObjectImpl.eResolveProxy(BasicEObjectImpl.java:1483)
at de.dubmas.modulob.system.impl.InterfaceModelImpl.getModule(InterfaceModelImpl.java:100)
at de.dubmas.modulob.NameProvider.getFullyQualifiedName(NameProvider.java:36)
So, I have two questions:
1)
How can I retrieve a module's name without running in a cyclic link resolution?
2)
I realized that the general challenge in my language design is that the containment reference from Modul to InterfaceModel
cannot be set by the language infrastructure because Module instances and InterfaceModel instances are created in different
resources. I tried to resolve this issue by introducing a second reference from InterfaceModel to Module and making it the eOpposite of Module-InterfaceModel-reference but then I ran into some more issues. In a code generation step I resolve this issue by appyling a model transformation that stiches together the complete
model graph. But I do not know how such a semantic can be achieved in the UI (language infrastructure).
Is there a best practice for dealing with such situations?
regards,
steven