Create new object when can't resolve reference [message #1647720] |
Tue, 03 March 2015 10:27  |
Eclipse User |
|
|
|
Hi,
I would like to allow users to make cross-references to non-existent objects. Instead of producing an error, I would like to create a new object, based on the name used in the reference.
For example, with the following grammar,
Model:
elements+=Element*;
Element:
Person | Greeting;
Person:
'person' name=ID;
Greeting:
'Hello' person=[Person|ID] '!';
and the following input file,
Hello World!
a new Person object with name="World" should be created, because no such object is within scope.
Any suggestions on the cleanest way to achieve this behavior?
Thanks,
Orlando
|
|
|
|
Re: Create new object when can't resolve reference [message #1649926 is a reply to message #1648164] |
Wed, 04 March 2015 10:27   |
Eclipse User |
|
|
|
Hi Christian,
Thanks, for your help. I hadn't seen those posts.
I now have the feature working.
I created a class that extends DefaultLinkingService,
package org.example.dsl;
import java.util.Collections;
import java.util.List;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.linking.impl.DefaultLinkingService;
import org.eclipse.xtext.linking.impl.IllegalNodeException;
import org.eclipse.xtext.nodemodel.INode;
import org.example.dsl.greeting.GreetingFactory;
import org.example.dsl.greeting.Person;
public class GreetingLinkingService extends DefaultLinkingService {
@Override
public List<EObject> getLinkedObjects(EObject context, EReference ref, INode node)
throws IllegalNodeException {
List<EObject> list = super.getLinkedObjects(context, ref, node);
if (!list.isEmpty()) {
return list;
}
String name = node.getText();
// create a dummy URI with the DSL's file extension
URI uri = URI.createURI("dummy:/" + name + ".greeting");
ResourceSet resourceSet = context.eResource().getResourceSet();
Resource resource = resourceSet.getResource(uri, false);
Person person;
if (resource == null) {
person = GreetingFactory.eINSTANCE.createPerson();
person.setName(name);
resource = resourceSet.createResource(uri);
List<EObject> contents = resource.getContents();
contents.add(person);
} else {
person = (Person) resource.getContents().get(0);
}
return Collections.singletonList((EObject)person);
}
}
and I use that class in the RuntimeModule.
package org.example.dsl;
import org.eclipse.xtext.linking.ILinkingService;
public class GreetingRuntimeModule extends org.example.dsl.AbstractGreetingRuntimeModule {
@Override
public Class<? extends ILinkingService> bindILinkingService() {
return GreetingLinkingService.class;
}
}
One small issue is that value conversion has not been run at that stage. In my real application, I use a STRING terminal, and need to manually convert to get the name of the element.
import org.eclipse.xtext.conversion.impl.STRINGValueConverter;
...
String name = new STRINGValueConverter().toValue(node.getText(), node);
Thanks, again,
Orlando
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.21911 seconds