Commit e89f389d authored by Martti Käärik's avatar Martti Käärik
Browse files

Scoping providers and linking service (with custom lazy-linking-resource).

Change-Id: Ibb82a0d07ab50dacf79a682199f54d42d8f82478
parent 5b4622cc
Loading
Loading
Loading
Loading
+24 −0
Original line number Original line Diff line number Diff line
@@ -3,10 +3,18 @@
 */
 */
package org.etsi.mts.tdl.graphical.labels;
package org.etsi.mts.tdl.graphical.labels;


import org.eclipse.xtext.linking.ILinkingService;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.parser.IParser;
import org.eclipse.xtext.parser.IParser;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.scoping.IGlobalScopeProvider;
import org.eclipse.xtext.serializer.sequencer.IContextFinder;
import org.eclipse.xtext.serializer.sequencer.IContextFinder;
import org.eclipse.xtext.serializer.tokens.ICrossReferenceSerializer;
import org.eclipse.xtext.serializer.tokens.ICrossReferenceSerializer;
import org.etsi.mts.tdl.graphical.labels.linking.ContextAwareLinkingService;
import org.etsi.mts.tdl.graphical.labels.naming.QNameProvider;
import org.etsi.mts.tdl.graphical.labels.parser.RuleBasedDataParser;
import org.etsi.mts.tdl.graphical.labels.parser.RuleBasedDataParser;
import org.etsi.mts.tdl.graphical.labels.resource.AdaptiveLinkingResource;
import org.etsi.mts.tdl.graphical.labels.scoping.ConfigurableScopeProvider;
import org.etsi.mts.tdl.graphical.labels.serializer.DataCrossReferenceSerializer;
import org.etsi.mts.tdl.graphical.labels.serializer.DataCrossReferenceSerializer;


/**
/**
@@ -25,4 +33,20 @@ public class DataRuntimeModule extends AbstractDataRuntimeModule {
	public Class<? extends IParser> bindIParser() {
	public Class<? extends IParser> bindIParser() {
		return RuleBasedDataParser.class;
		return RuleBasedDataParser.class;
	}
	}

	public Class<? extends XtextResource> bindXtextResource() {
		return AdaptiveLinkingResource.class;
	}
	
	public Class<? extends ILinkingService> bindILinkingService() {
		return ContextAwareLinkingService.class;
	}

	public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
		return QNameProvider.class;
	}
	
	public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
		return ConfigurableScopeProvider.class;
	}
}
}
+134 −0
Original line number Original line Diff line number Diff line
package org.etsi.mts.tdl.graphical.labels.linking;

import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.linking.impl.DefaultLinkingService;
import org.eclipse.xtext.linking.impl.IllegalNodeException;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.etsi.mts.tdl.DataUse;
import org.etsi.mts.tdl.tdlPackage;

import com.google.inject.Inject;

public class ContextAwareLinkingService extends DefaultLinkingService {
	
	private Map<EClass, AlternativeContext> alternativeContexts;
	
	@Inject
	private IQualifiedNameConverter qualifiedNameConverter;
	
	@Override
	public List<EObject> getLinkedObjects(EObject context, EReference ref, INode node) throws IllegalNodeException {
		
		final EClass requiredType = ref.getEReferenceType();
		if (requiredType == null)
			return Collections.<EObject> emptyList();

		final String crossRefString = getCrossRefNodeAsString(node);
		if (crossRefString != null && !crossRefString.equals("")) {
			QualifiedName qualifiedLinkName =  qualifiedNameConverter.toQualifiedName(crossRefString);
			
			IScope scope = getScope(context, ref);
			Iterable<IEObjectDescription> eObjectDescriptions = scope.getElements(qualifiedLinkName);
			if (context instanceof DataUse && !eObjectDescriptions.iterator().hasNext()) {
				
				AlternativeContext alternative = getAlternativeContext(context);
				
				if (alternative != null) {
					//Keeps track of current object in the resource
					EObject current = context;
					for (int i = 0; i < alternative.classes.length; i++) {
						EObject alternativeContext = EcoreUtil.create(alternative.classes[i]);
						
						//Add new object to resource
						EcoreUtil.replace(current, alternativeContext);
						current = alternativeContext;
						
						IScope alternativeScope = getScope(alternativeContext, alternative.references[i]);
						Iterable<IEObjectDescription> altEObjectDescriptions = alternativeScope.getElements(qualifiedLinkName);
						if (altEObjectDescriptions.iterator().hasNext()) {
							
							alternative.copy(context, alternativeContext);
							
							context = alternativeContext;
							ref = alternative.references[i];
							scope = alternativeScope;
							eObjectDescriptions = altEObjectDescriptions;
							break;
						}
					}
					if (context != current)
						//No alternative match, restore original
						EcoreUtil.replace(current, context);
				}
			}
			
			Iterator<IEObjectDescription> iter = eObjectDescriptions.iterator();
			if (iter.hasNext()) {
				IEObjectDescription eObjectDescription = iter.next();
				EObject linkedObject = eObjectDescription.getEObjectOrProxy();
				if (!iter.hasNext() || !ref.isMany()) {
					// If we replaced the context object above then we need to
					// set this association here or it will be lost
					context.eSet(ref, linkedObject);
					return Collections.singletonList(linkedObject);
				}
			}
		}
		return Collections.emptyList();
	}

	public AlternativeContext getAlternativeContext(EObject contextObject) {
		if (alternativeContexts == null) {
			alternativeContexts = new Hashtable<EClass, AlternativeContext>();
			
			AlternativeContext dataUseContexts = new AlternativeContext() {
				@Override
				void copy(EObject from, EObject to) {
					((DataUse)to).getReduction().addAll(((DataUse)from).getReduction());
					((DataUse)to).getArgument().addAll(((DataUse)from).getArgument());
				}
			};
			dataUseContexts.classes = new EClass[]{
					tdlPackage.eINSTANCE.getDataInstanceUse(),
					tdlPackage.eINSTANCE.getFunctionCall(),
					tdlPackage.eINSTANCE.getPredefinedFunctionCall(),
					tdlPackage.eINSTANCE.getVariableUse(),
					tdlPackage.eINSTANCE.getFormalParameterUse()
			};
			dataUseContexts.references = new EReference[]{
					tdlPackage.eINSTANCE.getDataInstanceUse_DataInstance(),
					tdlPackage.eINSTANCE.getFunctionCall_Function(),
					tdlPackage.eINSTANCE.getPredefinedFunctionCall_Function(),
					tdlPackage.eINSTANCE.getVariableUse_Variable(),
					tdlPackage.eINSTANCE.getFormalParameterUse_Parameter()
			};
			alternativeContexts.put(tdlPackage.eINSTANCE.getDataUse(), dataUseContexts);
		}

		for (EClass alternativeClass: alternativeContexts.keySet())
			if (alternativeClass.isInstance(contextObject))
				return alternativeContexts.get(alternativeClass);
		
		return null;
	}

}

abstract class AlternativeContext {
	EClass[] classes;
	EReference[] references;
	abstract void copy(EObject from, EObject to);
}
 No newline at end of file
+19 −0
Original line number Original line Diff line number Diff line
package org.etsi.mts.tdl.graphical.labels.naming;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.etsi.mts.tdl.Element;
import org.etsi.mts.tdl.NamedElement;

public class QNameProvider extends DefaultDeclarativeQualifiedNameProvider  {
	@Override
	public QualifiedName getFullyQualifiedName(EObject obj) {
		if (obj instanceof NamedElement) {
			String name = ((Element) obj).getName();
			if (name != null)
				return QualifiedName.create(((Element) obj).getName());
		}
		return super.getFullyQualifiedName(obj);
	}
}
+28 −0
Original line number Original line Diff line number Diff line
package org.etsi.mts.tdl.graphical.labels.resource;

import java.util.List;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.util.Triple;

public class AdaptiveLinkingResource extends LazyLinkingResource {
	@Override
	protected EObject getEObject(String uriFragment, Triple<EObject, EReference, INode> triple) throws AssertionError {
		List<EObject> linkedObjects = getLinkingService().getLinkedObjects(
				triple.getFirst(), 
				triple.getSecond(),
				triple.getThird());
		
		if (!linkedObjects.isEmpty()) {
			EObject newObject = linkedObjects.get(0);
			if (!triple.getSecond().getEType().isInstance(newObject)) {
				//Was replaced
				return null;
			}
		}
		return super.getEObject(uriFragment, triple);
	}
}
+81 −0
Original line number Original line Diff line number Diff line
package org.etsi.mts.tdl.graphical.labels.scoping;

import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.Map;

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.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
import org.eclipse.xtext.scoping.impl.SelectableBasedScope;
import org.etsi.mts.tdl.ElementImport;
import org.etsi.mts.tdl.Package;
import org.etsi.mts.tdl.tdlPackage;

import com.google.common.base.Predicate;
import com.google.inject.Inject;

public class ConfigurableScopeProvider extends ImportUriGlobalScopeProvider {

	@Inject
	private IResourceServiceProvider resourceServiceProvider;
	
	private Map<URI, LinkedHashSet<URI>> importedUris = new Hashtable<URI, LinkedHashSet<URI>>();
	
	@Override
	protected LinkedHashSet<URI> getImportedUris(Resource resource) {
		
		URI uri = resource.getURI();

		ResourceSet rs = new ResourceSetImpl();
		Resource realResource = rs.getResource(uri, true);
		
		LinkedHashSet<URI> imports = new LinkedHashSet<URI>();
		importedUris.put(uri, imports);
		
		imports.add(uri);

		//Add resource URIs of imported packages
		for (EObject e: realResource.getContents()) {
			if (tdlPackage.eINSTANCE.getPackage().isInstance(e)) {
				Package p = (Package) e;
				addImports(p, imports);
			}
		}
		
		return imports;
	}
	
	private void addImports(Package p, LinkedHashSet<URI> imports) {
		for (ElementImport pi: p.getImport()) {
			Package ip = pi.getImportedPackage();
			URI iUri = EcoreUtil.getURI(ip).trimFragment();
			imports.add(iUri);
			addImports(ip, imports);
		}
	}
	
	@Override
	protected IScope createLazyResourceScope(IScope parent, URI uri,
			IResourceDescriptions descriptions, EClass type,
			Predicate<IEObjectDescription> filter, boolean ignoreCase) {

		ResourceSet rs = new ResourceSetImpl();
		Resource resource = rs.getResource(uri, true);
		
		IResourceDescription description = resourceServiceProvider.getResourceDescriptionManager().getResourceDescription(resource);
		IScope scope = SelectableBasedScope.createScope(parent, description, filter, type, ignoreCase);
		
		return scope;
	}
}
Loading