When you work with Sphinx as a user in an Eclipse runtime, e.g. with the Sphinx model explorer, Sphinx does a lot of work in the background to update models, provide a single shared model to all editors etc. But what to do when you want to access Sphinx models from your own code.
EcorePlatformUtil
EcorePlatformUtil is one of the important classes with a lot of methods that help you access your models. Two important methods are
- getResource(…)
- loadResource(…)
They come in a variety of parameter variations. The important thing is, getResource(…) will not load your resource if it is not yet loaded. That is a little bit different than the standard ResourceSet.getResource(…) with its loadOnDemand parameter.
On the other hand, loadResource(…) wil only load your resource if it is not loaded yet. If it is, there will be now runtime overhead. Let’s have a look at the code:
public static Resource loadResource(IFile file, Map<?, ?> options) {
TransactionalEditingDomain editingDomain = WorkspaceEditingDomainUtil.getEditingDomain(file);
if (editingDomain != null) {
return loadResource(editingDomain, file, options);
}
return null;
}
Sphinx uses its internal registries to find the TransactionalEditingDomain that the file belongs to and then calls loadResource(…)
public static Resource loadResource(final TransactionalEditingDomain editingDomain, final IFile file, final Map<?, ?> options) {
if (editingDomain != null && file != null) {
try {
return TransactionUtil.runExclusive(editingDomain, new RunnableWithResult.Impl<Resource>() {
@Override
public void run() {
URI uri = createURI(file.getFullPath());
setResult(EcoreResourceUtil.loadResource(editingDomain.getResourceSet(), uri, options));
}
});
} catch (InterruptedException ex) {
PlatformLogUtil.logAsError(Activator.getPlugin(), ex);
}
}
return null;
}
So we have to look at org.eclipse.sphinx.emf.util.EcoreResourceUtil to see what happens next. There is just a little fragment
public static Resource loadResource(ResourceSet resourceSet, URI uri, Map<?, ?> options) {
Assert.isNotNull(uri);
return loadResource(resourceSet, uri, options, true);
}
that leads us to
private static Resource loadResource(ResourceSet resourceSet, URI uri, Map<?, ?> options, boolean loadOnDemand) {
Assert.isNotNull(uri);
// Create new ResourceSet if none has been provided
if (resourceSet == null) {
resourceSet = new ScopingResourceSetImpl();
}
// Try to convert given URI to platform:/resource URI if not yet so
/*
* !! Important Note !! This is necessary in order to avoid that resources which are located inside the
* workspace get loaded multiple times just because they are referenced by URIs with different schemes. If given
* resource set were an instance of ResourceSetImpl this extra conversion wouldn't be necessary.
* org.eclipse.emf.ecore.resource.ResourceSet.getResource(URI, boolean) normalizes and compares given URI and to
* normalized copies of URIs of already present resources and thereby avoids multiple loading of same resources
* on its own. This is however not true when ExtendedResourceSetImpl or a subclass of it is used. Herein, URI
* normalization and comparison has been removed from
* org.eclipse.sphinx.emf.resource.ExtendedResourceSetImpl.getResource(URI, boolean) in order to increase
* runtime performance.
*/
if (!uri.isPlatform()) {
uri = convertToPlatformResourceURI(uri);
}
// Just get model resource if it is already loaded
Resource resource = resourceSet.getResource(uri.trimFragment().trimQuery(), false);
// Load it using specified options if not done so yet and a demand load has been requested
if ((resource == null || !resource.isLoaded()) && loadOnDemand) {
if (exists(uri)) {
if (resource == null) {
String contentType = getContentTypeId(uri);
resource = resourceSet.createResource(uri, contentType);
}
if (resource != null) {
try {
// Capture errors and warnings encountered during resource creation
/*
* !! Important note !! This is necessary because the resource's errors and warnings are
* automatically cleared when the loading begins. Therefore, if we don't retrieve them at this
* point all previously encountered errors and warnings would be lost (see
* org.eclipse.emf.ecore.resource.impl.ResourceImpl.load(InputStream, Map<?, ?>) for details)
*/
List<Resource.Diagnostic> creationErrors = new ArrayList<Resource.Diagnostic>(resource.getErrors());
List<Resource.Diagnostic> creationWarnings = new ArrayList<Resource.Diagnostic>(resource.getWarnings());
// Load resource
resource.load(options);
// Make sure that no empty resources are kept in resource set
if (resource.getContents().isEmpty()) {
unloadResource(resource, true);
}
// Restore creation time errors and warnings
resource.getErrors().addAll(creationErrors);
resource.getWarnings().addAll(creationWarnings);
} catch (Exception ex) {
// Make sure that no empty resources are kept in resource set
if (resource.getContents().isEmpty()) {
// Capture errors and warnings encountered during resource load attempt
/*
* !! Important note !! This is necessary because the resource's errors and warnings are
* automatically cleared when it gets unloaded. Therefore, if we didn't retrieve them at
* this point all errors and warnings encountered during loading would be lost (see
* org.eclipse.emf.ecore.resource.impl.ResourceImpl.doUnload() for details)
*/
List<Resource.Diagnostic> loadErrors = new ArrayList<Resource.Diagnostic>(resource.getErrors());
List<Resource.Diagnostic> loadWarnings = new ArrayList<Resource.Diagnostic>(resource.getWarnings());
// Make sure that resource gets unloaded and removed from resource set again
try {
unloadResource(resource, true);
} catch (Exception e) {
// Log unload problem in Error Log but don't let it go along as runtime exception. It is
// most likely just a consequence of the load problems encountered before and therefore
// should not prevent those from being restored as errors and warnings on resource.
PlatformLogUtil.logAsError(Activator.getPlugin(), e);
}
// Restore load time errors and warnings on resource
/*
* !! Important Note !! The main intention behind restoring recorded errors and warnings on
* the already unloaded resource is to enable these errors/warnings to be converted to
* problem markers by the resource problem handler later on (see
* org.eclipse.sphinx.emf.internal.resource.ResourceProblemHandler#resourceSetChanged(
* ResourceSetChangeEvent)) for details).
*/
resource.getErrors().addAll(loadErrors);
resource.getWarnings().addAll(loadWarnings);
}
// Record exception as error on resource
Throwable cause = ex.getCause();
Exception exception = cause instanceof Exception ? (Exception) cause : ex;
resource.getErrors().add(
new XMIException(NLS.bind(Messages.error_problemOccurredWhenLoadingResource, uri.toString()), exception, uri
.toString(), 1, 1));
// Re-throw exception
throw new WrappedException(ex);
}
}
}
}
return resource;
}
- In line 25, the standard EMF ResourceSet.getResource() is used to see if the resource is already there. Note that loadonDemand is false.
- Otherwise the resource is actually created and loaded. If it does not have any content, it is immediately removed
- Information about loading errors / warnings is stored at the resource
EcorePlatformUtil.getResource(IFile)
This method will not load the resource as can be seen from the code:
public static Resource getResource(final IFile file) {
final TransactionalEditingDomain editingDomain = WorkspaceEditingDomainUtil.getCurrentEditingDomain(file);
if (editingDomain != null) {
try {
return TransactionUtil.runExclusive(editingDomain, new RunnableWithResult.Impl<Resource>() {
@Override
public void run() {
URI uri = createURI(file.getFullPath());
setResult(editingDomain.getResourceSet().getResource(uri, false));
}
});
} catch (InterruptedException ex) {
PlatformLogUtil.logAsError(Activator.getPlugin(), ex);
}
}
return null;
}
ModelLoadManager
In addition, the ModelLoadManager provides more integration with the Workspace framework (Jobs, ProgressMonitor,etc.):
ModelLoadManager.loadFiles(Collection, IMetaModelDescriptor, boolean, IProgressMonitor) supports an “async” parameter. If that is true, the model loading will be executed within a org.eclipse.core.runtime.jobs.Job. The method in turn calls
ModelLoadManager.runDetectAndLoadModelFiles(Collection, IMetaModelDescriptor, IProgressMonitor), which sets up progress monitor and performance statistics, itself calling detectFilesToLoad and runLoadModelFiles. detectFilesToLoad will be discussed in a dedicated posting.
runLoadModelFiles sets up progress monitor and performance statistics and calls loadModelFilesInEditingDomain finally delegating down to EcorePlatformUtil.loadResource(editingDomain, file, loadOptions) that we discussed above.