package org.jnario.feature.scoping; import java.util.Collections; import java.util.List; import org.eclipse.emf.common.util.URI; 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.jdt.annotation.NonNullByDefault; import org.eclipse.xtext.linking.impl.IllegalNodeException; import org.eclipse.xtext.linking.impl.LinkingHelper; import org.eclipse.xtext.linking.lazy.LazyURIEncoder; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.nodemodel.ILeafNode; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.xbase.XAbstractFeatureCall; import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.XFeatureCall; import org.eclipse.xtext.xbase.XMemberFeatureCall; import org.eclipse.xtext.xbase.XbasePackage; import org.eclipse.xtext.xbase.scoping.batch.IFeatureScopeSession; import org.eclipse.xtext.xbase.typesystem.IResolvedTypes; import org.eclipse.xtext.xbase.typesystem.computation.IFeatureLinkingCandidate; import org.eclipse.xtext.xbase.typesystem.internal.AbstractTypeComputationState; import org.eclipse.xtext.xbase.typesystem.internal.NullFeatureLinkingCandidate; import org.eclipse.xtext.xbase.typesystem.internal.ResolvedTypes; import org.eclipse.xtext.xbase.typesystem.internal.ScopeProviderAccess; import org.jnario.util.SourceAdapter; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.inject.Inject; public class FeatureScopeProviderAccess extends ScopeProviderAccess { @Inject private LinkingHelper linkingHelper; @Inject private IQualifiedNameConverter qualifiedNameConverter; @Inject private LazyURIEncoder encoder; @NonNullByDefault protected IFeatureLinkingCandidate getKnownFeature(XAbstractFeatureCall featureCall, AbstractTypeComputationState state, ResolvedTypes resolvedTypes) { IFeatureLinkingCandidate result = super.getKnownFeature(featureCall, state, resolvedTypes); if(result instanceof NullFeatureLinkingCandidate){ EObject source = SourceAdapter.find(featureCall); if(source == null){ return result; } return super.getKnownFeature((XAbstractFeatureCall) source, state, resolvedTypes); } return result; } public Iterable<IEObjectDescription> getCandidateDescriptions(XExpression expression, EReference reference, EObject toBeLinked, IFeatureScopeSession session, IResolvedTypes types) throws IllegalNodeException { if (toBeLinked == null) { return Collections.emptyList(); } if (!toBeLinked.eIsProxy()) { throw new IllegalStateException(expression + " was already linked to " + toBeLinked); } URI uri = EcoreUtil.getURI(toBeLinked); String fragment = uri.fragment(); if (encoder.isCrossLinkFragment(expression.eResource(), fragment)) { INode node; try{ node = encoder.getNode(expression, fragment); }catch(IllegalStateException e){ EObject source = SourceAdapter.find(expression); node = encoder.getNode(source, fragment); } final EClass requiredType = reference.getEReferenceType(); if (requiredType == null) return Collections.emptyList(); final String crossRefString = linkingHelper.getCrossRefNodeAsString(node, true); if (crossRefString != null && !crossRefString.equals("")) { final IScope scope = session.getScope(expression, reference, types); QualifiedName qualifiedLinkName = qualifiedNameConverter.toQualifiedName(crossRefString); Iterable<IEObjectDescription> descriptions = scope.getElements(qualifiedLinkName); if (Iterables.isEmpty(descriptions)) { INode errorNode = getErrorNode(expression, node); if (errorNode != node) { qualifiedLinkName = getErrorName(errorNode); } return Collections.<IEObjectDescription>singletonList(new ErrorDescription(getErrorNode(expression, node), qualifiedLinkName)); } return descriptions; } return Collections.emptyList(); } else { throw new IllegalStateException(expression + " uses unsupported uri fragment " + uri); } } /** * Returns the node that best describes the error, e.g. if there is an expression * <code>com::foo::DoesNotExist::method()</code> the error will be rooted at <code>com</code>, but * the real problem is <code>com::foo::DoesNotExist</code>. */ private INode getErrorNode(XExpression expression, INode node) { if (expression instanceof XFeatureCall) { XFeatureCall featureCall = (XFeatureCall) expression; if (!canBeTypeLiteral(featureCall)) { return node; } if (featureCall.eContainingFeature() == XbasePackage.Literals.XMEMBER_FEATURE_CALL__MEMBER_CALL_TARGET) { XMemberFeatureCall container = (XMemberFeatureCall) featureCall.eContainer(); if (canBeTypeLiteral(container)) { boolean explicitStatic = container.isExplicitStatic(); XMemberFeatureCall outerMost = getLongestTypeLiteralCandidate(container, explicitStatic); if (outerMost != null) return NodeModelUtils.getNode(outerMost); } } } return node; } private XMemberFeatureCall getLongestTypeLiteralCandidate(XMemberFeatureCall current, boolean mustBeStatic) { if (current.eContainingFeature() == XbasePackage.Literals.XMEMBER_FEATURE_CALL__MEMBER_CALL_TARGET) { XMemberFeatureCall container = (XMemberFeatureCall) current.eContainer(); if (canBeTypeLiteral(container)) { if (!mustBeStatic && !container.isExplicitStatic()) { return null; } if (mustBeStatic != container.isExplicitStatic()) { return current; } if (mustBeStatic && container.eContainingFeature() != XbasePackage.Literals.XMEMBER_FEATURE_CALL__MEMBER_CALL_TARGET) { return current; } return getLongestTypeLiteralCandidate(container, mustBeStatic); } } if (mustBeStatic) { return null; } if (!mustBeStatic && !current.isExplicitStatic()) { return null; } return current; } private boolean canBeTypeLiteral(XAbstractFeatureCall featureCall) { return !featureCall.isExplicitOperationCallOrBuilderSyntax() && featureCall.getTypeArguments().isEmpty(); } private QualifiedName getErrorName(INode errorNode) { List<String> segments = Lists.newArrayListWithCapacity(4); for(ILeafNode leaf: errorNode.getLeafNodes()) { if (!leaf.isHidden()) { String text = leaf.getText(); // XParenthesizedExpression if (text.equals("(") || text.equals(")")) { continue; } if (!text.equals(".") && !text.equals("::")) { if (text.charAt(0) == '^') segments.add(text.substring(1)); else segments.add(text); } } } return QualifiedName.create(segments); } }