Model-Transformation: How to resolve custom generated Proxy-Objects. [message #1832256] |
Mon, 14 September 2020 11:37  |
Jan Hermes Messages: 27 Registered: September 2020 |
Junior Member |
|
|
Hello Xtext-Experts,
I'm currently developing a language where I want to transform the generated AST-Model,
further called src-model,
into another AST-Model (defined and generated via emf-modelling tools),
called dst-model below.
After that I want to compile the transformed Model (dst-model) into the target-language.
Current state
Currently I successfully parse the source-model language and the generator gets called (the usual xtext-procedure).
After that I iterate through the AST of the Resource to be generated.
I transform every EObject from the src-model into the desired EObject
of the dst-model.
The generator then compiles the dst-model into a desired textual representation.
The only problem I encounter is, that while transforming the src-model I sometimes want
to generate ProxyObjects for the dst-model.
But I do not know how to correctly define the ProxyObjects, so that they later can be resolved
by e.g. EcoreUtil2.resolveAll.
Minimal Working Example
The example below does not have any real functionality or reason to exist,
it is just as simple as possible.
A minimal src-model-grammar
SrcSpecification:
elements+=SrcElement*
SrcElement:
SrcDeclaration | SrcReference
SrcDeclaration:
"decl" name=ID
SrcReference:
"ref" ref=[SrcDeclaration]
A minimal dst-model-grammar
DstSpecification:
elements+=DstElement*
DstElement:
DstDeclaration | DstReference
DstDeclaration:
"decl" name=ID
DstReference:
"ref" ref=[DstDeclaration]
mwe2-example only showing the additional configuration for the dst-model
// ...
Workflow {
component = XtextGenerator {
// ...
// the usual workflow configurations for an xtext-language
// ...
language = XtextGeneratorLanguage {
name = "org.example.mydsl.DstModel"
fileExtensions = "dstmodel"
fragment = ecore.EMFGeneratorFragment2 {}
fragment = resourceFactory.ResourceFactoryFragment2 {}
}
}
}
A singleton ResourceSet Provider for the dst-model
@singleton
class ResourceSetCreator {
@Inject IResourceFactory resourceFactory
@Inject IResourceServiceProvider serviceProvider
@Inject XtextResourceSet resourceSet
def void registerExtension(String ext) {
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(ext, resourceFactory)
IResourceServiceProvider.Registry.INSTANCE.getExtensionToFactoryMap().put(ext, serviceProvider)
}
def Resource createResource(URI uri) {
return resourceSet.createResource(uri)
}
def void resolve() {
EcoreUtil2.resolveAll(resourceSet)
}
}
The generator with proxy generation and the try to resolve them
//...
@Inject extension IQualifiedNameProvider
@Inject ResourceSetCreator resourceSetCreator
override void doGenerate(Resource resource, IFileSystemAccess2 fsa, IGeneratorContext context) {
resourceSetCreator.registerExtension("dstmodel")
val generatedResources = newArrayList()
for (srcSpec: resource.contents.filter(SrcSpecification)) {
val dstResource = resourceSetCreator.createResource(srcSpec.fullyQualifiedName.toString("/") + ".dstmodel")
val transformedSpec = transformSrcSpecification(srcSpec)
dstResource.contents.add(transformedSpec)
generatedResources+=dstResource
}
resourceSetCreator.resolve()
}
def DstSpecification transformSrcSpecification(SrcSpecification srcSpec) {
for (elem: srcSpec.elements) {
switch(elem) {
SrcDeclaration: transformSrcDeclaration(elem)
SrcReference: transformSrcReference(elem)
}
}
}
def DstDeclaration transformSrcDeclaration(SrcDeclaration decl) {
FACTORY.createDstDeclaration => [
name = decl.name
]
}
def DstReference transformSrcReference(SrcReference ref) {
val proxy = EcoreUtil2.create(FACTORY.createDstDeclaration.eClass) as InternalEObject
proxy.eSetProxyURI(URI.createURI(ref.fullyQualifiedName.toString(".")))
FACTORY.createDstReference => [
ref = proxy as DstDeclaration
]
}
// ...
Actual Question
So the code above tries to build the dst-model from the src-model while creating proxy-objects for
model-elements that are not directly accessable at the time of creation.
Afterwards I wanted to leverage the resolve method of the EcoreUtil for the resolution of those proxy-objects
to its actual elements in the model (if they exist)
So in the end there are some question for this approach:
- How do I specifiy the Proxy Objects, Resource URIs and Object Uris correctly?
- Is this even possible with the standard crossreference resolution mechanism or do I need an actual parsable input?
|
|
|
|
Re: Model-Transformation: How to resolve custom generated Proxy-Objects. [message #1832273 is a reply to message #1832272] |
Tue, 15 September 2020 06:41   |
Ed Merks Messages: 32823 Registered: July 2009 |
Senior Member |
|
|
So specifically the issue is in transformSrcReference where you of course want a reference that resolves to the correct dst declaration. Generally a URI that will resolve to a given EObject is created via org.eclipse.emf.ecore.util.EcoreUtil.getURI(EObject). If you look at that implementation you can see that it generally includes the URI of the resource, org.eclipse.emf.ecore.resource.Resource.getURI(), along with a fragment computed by the resource itself, org.eclipse.emf.ecore.resource.Resource.getURIFragment(EObject). In your code you've created a URI without a fragment and that cannot resolve to anything but a resource via org.eclipse.emf.ecore.resource.ResourceSet.getResource(URI, boolean), i.e., org.eclipse.emf.ecore.resource.ResourceSet.getEObject(URI, boolean) can only find an EObject if there is a fragment in the URI. If each transformed object ends up in its own resource (which appears to be how you've written it), then likely the fragment '/' will work properly to select EObject at index 0 of that resource's contents. But, I wonder that you shouldn't be getting the fully qualified name of the ref's ref rather than of the ref itself.
A completely alternative approach is to implement your transformation maintain a map from src-thing to dst-thing such that each transform* method either creates the dst-thing corresponding to the src-thing, or returns the already-created dst-thing corresponding to that src-thing. Then you could call transformSrcDeclaration from transformSrcReference to find-or-create the dst-declaration. Note that very simple one-to-one transformations can be implemented by specializing org.eclipse.emf.ecore.util.EcoreUtil.Copier and then specializing org.eclipse.emf.ecore.util.EcoreUtil.Copier.getTarget(EClass) and org.eclipse.emf.ecore.util.EcoreUtil.Copier.getTarget(EStructuralFeature). Then org.eclipse.emf.ecore.util.EcoreUtil.Copier.copyReferences() is called at the end to hook up all the cross references after the containment structure has been copied...
Ed Merks
Professional Support: https://www.macromodeling.com/
|
|
|
|
Powered by
FUDForum. Page generated in 0.01514 seconds