How to generate a factory? [message #1467654] |
Mon, 10 November 2014 07:39 |
|
Hey,
I have a set of DSL files that contain various model elements. I'm using IGenerator to generate Java Source code out of them.
Now I need a single factory some/most of those elements. How would I go for generating a factory that has all the required elements in there (also WRT the new paralllel generator mechanism)?
I thought about leveraging JDTs model and keeping a model of the factory in memory and manipulate it in a thread save way, but how to deal with removal of elements then? I'd have to keep track of elements per resource as thats the granularity I get in doGenerate...
Another approach would be to have a generator that builds the complete factory per plugin from the index as soon as the first resource for that bundle is encountered, and does nothing for the remaining resources. However, from the code I see that the IGenerator is only instanciated once, right? So I have no idea when to actually re-generate the factory and when not. I could only regenerate the factory always, which would be quite expensive.... What I'd need here would be a hook/method that is called once whenever a generator run (top level) starts, before the first resource is fed to doGenerate or something like that...
Any Ideas on this?
Thanks in advance...
|
|
|
|
|
Re: How to generate a factory? [message #1468908 is a reply to message #1467673] |
Tue, 11 November 2014 06:27 |
|
Hey,
Thanks a lot for the starting point I did something quite similar, but a little different...
1) I use BuilderParticipant as base classe, will use ParallelBuilderParticipant as soon as we're on 2.7.2. The 2.7.2 API is also a little bit nicer in that I can determine deletions also. This seems to be impossible with 2.6.0 AFAICT from the BuilderParticipant API. But thats not a real tragedy, in the (for us) rare case of a deletion one needs to do a clean build, et voila
2) I implemented a "slim" mode, which loads resources from the current bundle, but not it's dependencies. For some use cases (like my "I want to generate a factory for some elements", the information without resolved cross references might already be enough This speeds up the resource loading from around 5000ms to 200ms. As long as it's sufficient, I'll go with that.
Heres the code I came up with, just in case you find that interesting
/**
* Allows generation of code from multiple resources in the most efficient way that is possible.
*/
public interface IBundleModelGenerator {
/**
* @return <code>true</code> if the builder should only load the resources contained in the
* context bundle directly, without dependencies (there will be unresolved proxies!),
* <code>false</code> if the contents of visible bundles should be loaded also.
*/
public boolean isSlim();
/**
* @return the file extension of model files, used to find resources.
*/
public String getFileExtension();
/**
* Generates code out of multiple resources within a bundle. Depending on the implementation of
* {@link #isSlim()}, the passed {@link ResourceSet} will contain either only resources from the
* bundle in context, or from all visible bundles also.
*
* @param context
* the build context to allow determination of the bundle in context.
* @param resources
* the rources loaded for this generator.
* @param fsa
* the {@link IFileSystemAccess} to generate files through
*/
public void doGenerate(IBuildContext context, ResourceSet resources, IFileSystemAccess fsa);
}
and the BuilderParticipant:
class BundleModelBuilderParticipant extends BuilderParticipant {
@Inject private ResourceDescriptionsProvider descriptionsProvider
@Inject private IContainer.Manager containerManager
@Inject(optional=true) private IBundleModelGenerator bundleModelGenerator
private val modelGeneratorLock = new AtomicBoolean
override build(IBuildContext context, IProgressMonitor monitor) throws CoreException {
modelGeneratorLock.set(true)
super.build(context, monitor)
}
override protected handleChangedContents(Delta delta, IBuildContext context, EclipseResourceFileSystemAccess2 fsa) throws CoreException {
super.handleChangedContents(delta, context, fsa)
if (bundleModelGenerator != null && modelGeneratorLock.compareAndSet(true, false)) {
val start = System.currentTimeMillis
val set = context.resourceSet
val resources = findModelResources(context.builtProject)
System.err.println("finding resources took " + (System.currentTimeMillis - start))
if (bundleModelGenerator.slim) {
for (r : resources) {
set.getResource(getResourcePlatformURI(r), true)
}
} else {
// obtain the containers by querying for any of the resources in the bundle
val index = descriptionsProvider.createResourceDescriptions
val anyDesc = index.getResourceDescription(getResourcePlatformURI(resources.get(0)))
val containers = containerManager.getVisibleContainers(anyDesc, index)
for(c : containers) {
for(rd : c.resourceDescriptions) {
set.getResource(rd.URI, true)
}
}
}
System.err.println("loading resources took " + (System.currentTimeMillis - start))
bundleModelGenerator.doGenerate(context, set, fsa)
System.err.println("bundle model generator took " + (System.currentTimeMillis - start) + "ms")
}
}
/**
* Retrieves the platform:/ URI for the given resource.
*/
private def getResourcePlatformURI(IResource r) {
URI.createPlatformResourceURI(r.getProject().getName() + "/" + r.getProjectRelativePath().toPortableString(),
true)
}
/**
* Find model resources in the given project. This yields only resources directly contained in the project.
*/
private def findModelResources(IProject project) {
val files = newArrayList()
project.accept(
[ resource |
if (resource.getFileExtension() != null &&
resource.getFileExtension().equalsIgnoreCase(bundleModelGenerator.fileExtension)) {
files.add(resource);
}
return true;
]);
files
}
}
Then I just bound the generator implementation in the runtime module, and the builder participant in the UI module. Works like a charm - for modifications at least
P.S.: of course the System.err.println should be removed
Cheers,
Markus
|
|
|
|
|
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.02482 seconds