Home » Modeling » TMF (Xtext) » ?how to mix strategies of global scoping?(?how to adapt two strategies of global scopting for one language?)
?how to mix strategies of global scoping? [message #699001] |
Wed, 20 July 2011 16:10 |
alex.ren2006 Messages: 46 Registered: June 2011 |
Member |
|
|
Trying to combine "Global Scopes Based On Explicit Imports (ImportURI Mechanism)"
and
"Global Scopes Based On External Configuration (e.g. Classpath-Based)".
I am currently building an IDE for ATS (www.ats-lang.org). I am wondering whether Xtext can support the following strategies of scoping for one language at the same time.
For clarity, the example grammar goes as follows.
====================================================
grammar org.eclipse.xtext.example.fowlerdsl.Statemachine with org.eclipse.xtext.common.Terminals
generate statemachine "http://www.eclipse.org/xtext/example/fowlerdsl/Statemachine"
Statemachine :
{Statemachine}
(imp+=Import)*
('events'
events+=Event+
'end')?
('resetEvents'
resetEvents+=[Event]+
'end')?
('commands'
commands+=Command+
'end')?
states+=State*
;
Event:
name=ID code=ID
;
Command:
name=ID code=ID
;
State:
'state' name=ID
('actions' '{' actions+=[Command]+ '}')?
transitions+=Transition*
'end'
;
Transition:
event=[Event] '=>' state=[State]
;
Import: 'import' importURI=STRING; ====================================================
One source of cross-reference is "import". File "a.statemachine" imports file "b.statemachine" to refer to the elements in "b.statemachine". A file "c.statemachine" may reside in the same folder as "a.statemachine". But "a.statemachine" cannot refer to any elements of "c.statemachine" even if "c.statemachine" has the same elements as "b.statemachine" as long as "a.statemachine" doesn't import "c.statemachine" at all.
Another source of cross-reference is default library. There exists certain library folders, any source code can refer to elements in the files inside these library folders without explicitly importing them. These libraries folders can be set in eclipse as java class path.
If two sources of cross-reference have element of the same name, then the previous source has the higher priority over the library. (Or let user chooses from multiple options.)
By juggling the following part of mwe2 file
=================================
// scoping and exporting API
// fragment = scoping.ImportURIScopingFragment {}
// fragment = exporting.SimpleNamesFragment {}
// scoping and exporting API
fragment = scoping.ImportNamespacesScopingFragment {}
fragment = exporting.QualifiedNamesFragment {}
fragment = builder.BuilderIntegrationFragment {}
// provides the necessary bindings for java types integration
fragment = types.TypesGeneratorFragment {} ====================================
I can manage to have either of these two strategies of scoping. But I just cannot have both of them at the same time. Any suggestion is highly appreciated. Thanks a lot.
[Updated on: Thu, 21 July 2011 07:40] Report message to a moderator
|
|
| |
Re: ?how to mix strategies of global scoping? [message #702343 is a reply to message #702288] |
Tue, 26 July 2011 08:45 |
alex.ren2006 Messages: 46 Registered: June 2011 |
Member |
|
|
I simply merged DefaultGlobalScopeProvider and ImportUriGlobalScopeProvider and it works. The "CustomGlobalScopeProvider.java" goes as follows
========================
package org.eclipse.xtext.example.fowlerdsl.scoping;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.resource.IContainer;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescription.Event.Source;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.containers.FilterUriContainer;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.AbstractGlobalScopeProvider;
import org.eclipse.xtext.scoping.impl.DelegatingEventSource;
import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
import org.eclipse.xtext.scoping.impl.ImportUriResolver;
import org.eclipse.xtext.scoping.impl.LoadOnDemandResourceDescriptions;
import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
import org.eclipse.xtext.util.IResourceScopeCache;
import org.eclipse.xtext.util.OnChangeEvictingCache;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
* @author Sven Efftinge - Initial contribution and API
*/
public class CustomGlobalScopeProvider extends AbstractGlobalScopeProvider {
@Inject
private IContainer.Manager containerManager;
@Inject
private IResourceDescription.Manager descriptionManager;
// for classpath
protected IScope getScope(IScope parent, final Resource context, boolean ignoreCase, EClass type, Predicate<IEObjectDescription> filter) {
System.out.println("getScope0001");
IScope result = parent;
if (context == null || context.getResourceSet() == null)
return result;
List<IContainer> containers = Lists.newArrayList(getVisibleContainers(context));
Collections.reverse(containers);
Iterator<IContainer> iter = containers.iterator();
while (iter.hasNext()) {
IContainer container = iter.next();
result = createContainerScopeWithContext(context, result, container, filter, type, ignoreCase);
}
return getScope1(result, context, ignoreCase, type, filter);
}
protected IScope getScope(final Resource context, boolean ignoreCase, EClass type, Predicate<IEObjectDescription> filter) {
System.out.println("getScope0002");
return getScope(IScope.NULLSCOPE, context, ignoreCase, type, filter);
}
// @Override
// for import
protected IScope getScope1(IScope parent, Resource resource, boolean ignoreCase, EClass type, Predicate<IEObjectDescription> filter) {
final LinkedHashSet<URI> uniqueImportURIs = getImportedUris(resource);
IResourceDescriptions descriptions = getResourceDescriptions(resource, uniqueImportURIs);
List<URI> urisAsList = Lists.newArrayList(uniqueImportURIs);
Collections.reverse(urisAsList);
// IScope scope = IScope.NULLSCOPE;
for (URI uri : urisAsList) {
parent = createLazyResourceScope(parent, uri, descriptions, type, filter, ignoreCase);
}
return parent;
}
protected List<IContainer> getVisibleContainers(Resource resource) {
System.out.println("getVisibleContainers");
IResourceDescription description = descriptionManager.getResourceDescription(resource);
IResourceDescriptions resourceDescriptions = getResourceDescriptions(resource);
String cacheKey = getCacheKey("VisibleContainers", resource.getResourceSet());
OnChangeEvictingCache.CacheAdapter cache = new OnChangeEvictingCache().getOrCreate(resource);
List<IContainer> result = null;
result = cache.get(cacheKey);
if (result == null) {
result = containerManager.getVisibleContainers(description, resourceDescriptions);
// SZ: I'ld like this dependency to be moved to the implementation of the
// container manager, but it is not aware of a CacheAdapter
if (resourceDescriptions instanceof IResourceDescription.Event.Source) {
IResourceDescription.Event.Source eventSource = (Source) resourceDescriptions;
DelegatingEventSource delegatingEventSource = new DelegatingEventSource(eventSource);
delegatingEventSource.addListeners(Lists.newArrayList(Iterables.filter(result, IResourceDescription.Event.Listener.class)));
delegatingEventSource.initialize();
cache.addCacheListener(delegatingEventSource);
}
cache.set(cacheKey, result);
}
return result;
}
protected String getCacheKey(String base, ResourceSet context) {
System.out.println("getCacheKey");
Map<Object, Object> loadOptions = context.getLoadOptions();
if (loadOptions.containsKey(ResourceDescriptionsProvider.NAMED_BUILDER_SCOPE)) {
return base + "@" + ResourceDescriptionsProvider.NAMED_BUILDER_SCOPE;
}
return base + "@DEFAULT_SCOPE";
}
protected IScope createContainerScopeWithContext(Resource eResource, IScope parent, IContainer container,
Predicate<IEObjectDescription> filter, EClass type, boolean ignoreCase) {
System.out.println("createContainerScopeWithContext");
if (eResource != null) {
URI uriToFilter = eResource.getURI();
if (container.hasResourceDescription(uriToFilter))
container = new FilterUriContainer(uriToFilter, container);
}
return createContainerScope(parent, container, filter, type, ignoreCase);
}
protected IScope createContainerScope(IScope parent, IContainer container, Predicate<IEObjectDescription> filter, EClass type, boolean ignoreCase) {
System.out.println("createContainerScope");
return SelectableBasedScope.createScope(parent, container, filter, type, ignoreCase);
}
//======================================
@Inject
private ImportUriResolver importResolver;
@Inject
private Provider<LoadOnDemandResourceDescriptions> loadOnDemandDescriptions;
@Inject
private IResourceScopeCache cache;
public ImportUriResolver getImportUriResolver() {
return importResolver;
}
public void setImportResolver(ImportUriResolver importResolver) {
this.importResolver = importResolver;
}
public void setCache(IResourceScopeCache cache) {
this.cache = cache;
}
public IResourceDescriptions getResourceDescriptions(Resource resource, Collection<URI> importUris) {
IResourceDescriptions result = getResourceDescriptions(resource);
LoadOnDemandResourceDescriptions demandResourceDescriptions = loadOnDemandDescriptions.get();
demandResourceDescriptions.initialize(result, importUris, resource);
return demandResourceDescriptions;
}
protected LinkedHashSet<URI> getImportedUris(final Resource resource) {
return cache.get(ImportUriGlobalScopeProvider.class.getName(), resource, new Provider<LinkedHashSet<URI>>(){
public LinkedHashSet<URI> get() {
TreeIterator<EObject> iterator = resource.getAllContents();
final LinkedHashSet<URI> uniqueImportURIs = new LinkedHashSet<URI>(10);
while (iterator.hasNext()) {
EObject object = iterator.next();
String uri = importResolver.apply(object);
if (uri != null) {
URI importUri = URI.createURI(uri);
uniqueImportURIs.add(importUri);
}
}
Iterator<URI> uriIter = uniqueImportURIs.iterator();
while(uriIter.hasNext()) {
if (!EcoreUtil2.isValidUri(resource, uriIter.next()))
uriIter.remove();
}
return uniqueImportURIs;
}
});
}
protected IScope createLazyResourceScope(IScope parent, final URI uri, final IResourceDescriptions descriptions,
EClass type, final Predicate<IEObjectDescription> filter, boolean ignoreCase) {
IResourceDescription description = descriptions.getResourceDescription(uri);
return SelectableBasedScope.createScope(parent, description, filter, type, ignoreCase);
}
public void setLoadOnDemandDescriptions(Provider<LoadOnDemandResourceDescriptions> loadOnDemandDescriptions) {
this.loadOnDemandDescriptions = loadOnDemandDescriptions;
}
public Provider<LoadOnDemandResourceDescriptions> getLoadOnDemandDescriptions() {
return loadOnDemandDescriptions;
}
} =============================
I did nothing but merged two "getScope" methods. (The order is important.) I am lucky it works because I still don't understand the machanism behind very well. It would be great if the documentation can have a little bit clearer explanation for beginners about this topic.
So far I modify "AbstractStatemachineRuntimeModule.java" to make "CustomGlobalScopeProvider" to be effective.
=====================
// contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment
public Class<? extends org.eclipse.xtext.scoping.IGlobalScopeProvider> bindIGlobalScopeProvider() {
// return org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider.class;
// DefaultGlobalScopeProvider.class
return org.eclipse.xtext.example.fowlerdsl.scoping.CustomGlobalScopeProvider.class;
} ===========================
Once I rerun the mwe2 file, it's gone. Should I modify the mwe2 file to add my own fragment or there is simpler way to do this? Thanks a lot.
|
|
| | |
Goto Forum:
Current Time: Wed Sep 25 22:11:32 GMT 2024
Powered by FUDForum. Page generated in 0.04303 seconds
|