Implicit reference to an EObject [message #1769577] |
Tue, 01 August 2017 20:59 |
Nicolas Hili Messages: 40 Registered: March 2017 |
Member |
|
|
Hi all,
I am trying to implement an action language for UML state machines in order to be able to graphically model state machines and to use a Xtext editor to write action code for transition effects and state entry/exit. It works, and I have two editors side-by-side. When a state or a transition is selected, the Xtext editor allows me to write action code with syntax highlighting, auto-completion, and so on. When I finish editing my action code using Xtext, a UML Opaque Behavior is created for the transition effect/state entry/exit, and the action code is stored as a String in the body of the Opaque Behavior. The action code is not explicitly stored in an independent Resource.
My problem is the following: I do not know how to create my scope provider (for auto completion) without having to explicitly refer in my grammar to the contextual EObject (transition or state) for which I am currently editing the action code.
The following will better illustrate my problem:
myDSL.xtext:
Action:
container=[uml::NamedElement] '/' // I want to get rid of this line
statements+=Statement*
;
Statement:
SendStatement
;
SendStatement:
'send' portRef=PortRef ':' operation=Operation ';'
;
/* ... */
The desired output:
reception/
send portOwnedByThisComponent:myMessage();
This output shows the action code of a transition named reception. The action code simply sends a message myMessage to the port named portOwnedByThisComponent. My problem is that I want to get rid of the first line. I am currently editing (in the graphical editor) the reception transition and I do not want to explicitly refer to it in the action code (as the action code is embedded as a String in the Opaque Behavior of the transition, I already know that this action code is related to this transition).
However:
myDSLScopeProvider.xtend:
override getScope(EObject context, EReference reference) {
val contextElement = ContextElementUtil.getContextElement(reference.eResource)
val rootElement = EcoreUtil2.getRootContainer(context) // Give me the Action
val scope = super.getScope(context, reference)
if (context instanceof SendStatement && reference == MyDSLPackage.Literals.PORT_REF__PORT) {
val component = getComponent((rootElement as Action).container) // my problem is here
val candidates = EcoreUtil2.getAllContentsOfType(component, Port)
for (var i = candidates.length -1; i >= 0; i--) {
candidates.remove(i);
}
return Scopes.scopeFor(candidates)
}
}
/* ... */
In my scope provider, to be able to only accept sending messages through ports owned by the component containing the state or the transition for which the action code is edited, I need to keep a reference to the state/transition. Therefore, the line:
val component = getComponent((rootElement as Action).container)
requires me to have a reference to rootElement.container (which is, according to my grammar, a NamedElement, and specifically either a UML Transition or a UML State). The function getComponent then returns the component (UML Class) containing the state/transition.
My question: how to get rid of that? How is it possible to get an implicit reference to the state/transition without explicitly referring to it in my grammar (using injection ?). My first attempt was to retrieve the currently edited UML element in the graphical editor (the one that is currently selected). But I think this is not the correct way of doing it, as I will probably need to parse my action code (for analysis purposes, M2M, M2T, ...) even if the model is not opened in the graphical editor. The action code is simply stored as a string, and it is not contained in an independent resource, so I cannot rely on the name of that resource to know to which transition/state the action code refers. My guess is to use injections, but I do not know where to start.
Hope it is clear. Let me know otherwise,
Best,
Nicolas
|
|
|
|
|
Re: Implicit reference to an EObject [message #1769721 is a reply to message #1769675] |
Wed, 02 August 2017 21:33 |
Nicolas Hili Messages: 40 Registered: March 2017 |
Member |
|
|
Hello Christian,
Thanks a lot for your proposed solution, which is I think very close to what I want to achieve. I think it is actually half of the solution I am looking for :)
Following your message and the other forum thread, I did the following:
- I modified the Xtext resource by adding the ActionUncalled rule. However, I am not sure to understand how the ActionUncalled rule affects my generated metamodel. Should it automatically create the container derived attribute in the Action EObject in the generated metamodel ? In my case, the rule does nothing (apart from creating an ActionUncalled concept in the metamodel that is never used);
- Following the previous observation, I manually edited the generated metamodel (not sure this is the correct way of doing it) to add the container derived attribute in the Action EObject;
- I edited my MyDSLRuntimeModule as following:
class MyDSLRuntimeModule extends AbstractMyDSLRuntimeModule {
def Class<? extends IDerivedStateComputer> bindIDerivedStateComputer() {
println("test")
MyDSLDerivedStateComputer
}
override bindXtextResource() {
DerivedStateAwareResource;
}
def Class<? extends IResourceDescription.Manager> bindIResourceDescriptionManager() {
DerivedStateAwareResourceDescriptionManager;
}
}
- I implemented a first prototype of MyDSLDerivedStateComputer as follow:
@Inject
SyntheticLinkingSupport sls;
@Override
public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) {
if (!resource.getContents().isEmpty()) {
// preLinkingPhase seems to always be false
EObject obj = resource.getContents().get(0); // returns the Action
sls.createAndSetProxy(obj, MyDSLPackage.Literals.ACTION__CONTAINER, "PLAYING");
}
}
@Override
public void discardDerivedState(DerivedStateAwareResource resource) {
// nothing here
}
"PLAYING" is the name of the state that I refer to. I am using a SimpleNameProvider.
- After all these steps, in my MyDSLScopeProvider,
(rootElement as Action).container returns my container! I could then get rid of the first line in my grammar!
However, I do not know now how to change the line: sls.createAndSetProxy(obj, MyDSLPackage.Literals.ACTION__CONTAINER, "PLAYING");
so "PLAYING" is the name of an EObject (provided that the ResourceDescription exists and therefore the EObject I want to refer to has been exported) that is provided to the MyDSLDerivedStateComputer from outside.
Assuming that I have the graphical editor and the Xtext editor opened side-by-side and the State or Transition to which the action code refers to, I could easily (1) get the active editor, and (2) get the selected graphical element.
But assuming that I do not have the graphical editor opened and I only have a reference to my State or my Transition, as well as a String containing the action code, how do I ask Xtext to parse the String containing the action code and provides the MyDSLDerivedStateComputer with a reference of the EObject?
I hope it is clearer,
Related questions from above:
- What is the purpose of the rule Action_Uncalled ?
- Do I really need to edit the generated metamodel to add the derived attribute ?
Thanks a lot for your time,
Nicolas
|
|
|
|
Re: Implicit reference to an EObject [message #1769803 is a reply to message #1769723] |
Thu, 03 August 2017 13:58 |
Nicolas Hili Messages: 40 Registered: March 2017 |
Member |
|
|
Hey Christian,
Thanks for your reply.
I understand a bit better now. It works using the ActionUncall rule.
For my other issue (injecting the EObject into Xtext), I managed to find a workaround. When I instantiate my Xtext editor, I get the IDerivedStateComputer from the injector:
MyDSLActivator activator = MyDSLActivator.getInstance();
Injector injector = activator.getInjector(MyDSLActivator.MYDSL_MYDSL);
MyDSLDerivedStateComputer myDSLDerivedStateComputer = (MyDSLDerivedStateComputer)injector.getInstance(IDerivedStateComputer.class);
From the class responsible for instantiating the Xtext editor, I then set the reference to the EObject to MyDSLDerivedStateComputer (which contain a field to store it). I can then use its name by using org.eclipse.xtext.linking.lazy.SyntheticLinkingSupport.createAndSetProxy(EObject, EReference, String) .
Not sure it is the best way to achieve it, but it works :)
Hope this could help others,
Thanks a lot,
Nicolas
|
|
|
|
Re: Implicit reference to an EObject [message #1770027 is a reply to message #1769805] |
Mon, 07 August 2017 15:14 |
Nicolas Hili Messages: 40 Registered: March 2017 |
Member |
|
|
Hello Christian,
Sorry for my late answer,
That's a solution, but not sure how to do it properly with Guice (I am a newbie using Guice). In my runtime module, I implemented an "input" field (typed with NamedElement) and a method with the @Provides annotation:
@Provides @Named("input")
def NamedElement getInput() {
return input;
}
And in my DerivedStateComputer, I added the field with the following annotations:
@Inject @Named("input") @Nullable
NamedElement input;
However, in my runtime module, I still have to set the value of "input" at run-time. So, whenever/wherever the selection in my graphical editor is changed, I get the newly edited input (typed with NamedElement) and set the new value to the runtime module. Is that the correct way of doing it? I feel I am missing something.
Nicolas
|
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.03071 seconds