From 76b8609dc25774b804addfbe13be96065d0e15a6 Mon Sep 17 00:00:00 2001 From: Juergen Haug Date: Wed, 10 Jul 2019 12:41:00 +0200 Subject: Bug 549132 - [room.ui] Add hyperlink to namespace imports Change-Id: Iab7eda3d6658163cb23e93cd7f81071f799c77ba --- .../ui/linking/ImportAwareHyperlinkHelper.java | 151 +++++---------------- .../core/common/base/util/ImportHelpers.java | 66 +++++++-- .../core/common/validation/BaseJavaValidator.java | 33 ++--- .../RoomSemanticHighlightingCalculator.java | 5 +- .../etrice/core/validation/RoomJavaValidator.java | 9 +- 5 files changed, 107 insertions(+), 157 deletions(-) diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java index a96071feb..c349db409 100644 --- a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java @@ -16,153 +16,76 @@ package org.eclipse.etrice.core.common.ui.linking; import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EcoreFactory; -import org.eclipse.emf.ecore.EcorePackage; -import org.eclipse.etrice.core.common.base.Import; -import org.eclipse.etrice.core.common.scoping.ModelLocatorUriResolver; +import org.eclipse.etrice.core.common.base.util.ImportHelpers; import org.eclipse.jface.text.Region; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.RuleCall; -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.parser.IParseResult; -import org.eclipse.xtext.resource.EObjectAtOffsetHelper; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.IGlobalScopeProvider; import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.ui.editor.hyperlinking.HyperlinkHelper; import org.eclipse.xtext.ui.editor.hyperlinking.IHyperlinkAcceptor; import org.eclipse.xtext.ui.editor.hyperlinking.XtextHyperlink; -import com.google.common.base.Predicates; import com.google.inject.Inject; import com.google.inject.Provider; -/** - * @author Henrik Rentz-Reichert (initial contribution) - * - */ public class ImportAwareHyperlinkHelper extends HyperlinkHelper { - @Inject - protected Provider hyperlinkProvider; - - @Inject - protected ModelLocatorUriResolver uriResolver; + @Inject ImportHelpers importHelpers; + @Inject Provider hyperlinkProvider; - @Inject - protected EObjectAtOffsetHelper eObjectAtOffsetHelper; - - @Inject - protected IGlobalScopeProvider globalScopeProvider; - - @Inject - protected IQualifiedNameConverter nameConverter; - - /* - * (non-Javadoc) - * - * @see org.eclipse.xtext.ui.editor.hyperlinking.HyperlinkHelper# - * createHyperlinksByOffset(org.eclipse.xtext.resource.XtextResource, int, - * org.eclipse.xtext.ui.editor.hyperlinking.IHyperlinkAcceptor) - */ + @SuppressWarnings("deprecation") @Override public void createHyperlinksByOffset(XtextResource resource, int offset, IHyperlinkAcceptor acceptor) { - XtextHyperlink link = createByImportObject(resource, offset); - if (link == null) - link = createByImportNode(resource, offset); - if (link != null) - acceptor.accept(link); - - super.createHyperlinksByOffset(resource, offset, acceptor); - } - - private XtextHyperlink createByImportObject(XtextResource resource, int offset) { - EObject eObject = eObjectAtOffsetHelper.resolveElementAt(resource, offset); - if (eObject == null || !(eObject instanceof Import)) - return null; - - Import importObj = (Import) eObject; - - // Create hyperlink based on the qualified name of the import using the global scope provider - String name = importObj.getImportedNamespace(); - if(name != null) { - QualifiedName qualifiedName = nameConverter.toQualifiedName(name); - if(!qualifiedName.getLastSegment().equals("*")) { - EReference reference = EcoreFactory.eINSTANCE.createEReference(); - reference.setEType(EcorePackage.eINSTANCE.getEObject()); - IScope scope = globalScopeProvider.getScope(resource, reference, Predicates.alwaysTrue()); - IEObjectDescription eod = scope.getSingleElement(qualifiedName); - if(eod != null) { - URI uri = eod.getEObjectURI(); - INode node = NodeModelUtils.getNode(importObj); - XtextHyperlink result = hyperlinkProvider.get(); - result.setHyperlinkText(eod.getName().toString()); - result.setURI(uri); - result.setHyperlinkRegion(new Region(node.getOffset(), node.getLength())); // whole import statement - return result; - } - } - } - - // Create hyperlink using the import uri - if (importObj.getImportURI() == null) - return null; - - String uritext = uriResolver.resolve(importObj.getImportURI(), resource); - if (uritext == null) - return null; - - XtextHyperlink result = hyperlinkProvider.get(); - result.setHyperlinkText(uritext); // ? - try { - result.setURI(URI.createURI(uritext)); - } - catch (IllegalArgumentException e) { - return null; - } - - INode node = NodeModelUtils.getNode(importObj); - result.setHyperlinkRegion(new Region(node.getOffset(), node.getLength())); // whole import statement - - return result; - } - - private XtextHyperlink createByImportNode(XtextResource resource, int offset) { IParseResult parseResult = resource.getParseResult(); if (parseResult != null && parseResult.getRootNode() != null) { ILeafNode leaf = NodeModelUtils.findLeafNodeAtOffset(parseResult.getRootNode(), offset); - EObject grammarElement = leaf.getParent().getGrammarElement(); - if (grammarElement instanceof RuleCall) { - RuleCall rc = (RuleCall) grammarElement; - AbstractRule rule = rc.getRule(); - if (rule.getName().equals("Import")) { - String text = leaf.getText().substring(1, leaf.getText().length() - 1); - - String uritext = uriResolver.resolve(text, resource); - if (uritext == null) - return null; + Region region = new Region(leaf.getOffset(), leaf.getLength()); + + AbstractRule leafRule = null; + if(leaf.getGrammarElement() instanceof RuleCall) { + leafRule = ((RuleCall) leaf.getGrammarElement()).getRule(); + } + + AbstractRule parentRule = null; + if(leaf.getParent().getGrammarElement() instanceof RuleCall) { + parentRule = ((RuleCall) leaf.getParent().getGrammarElement()).getRule(); + } + + // FQN and ImportedFQN => create link on fqn + if (parentRule.getName().equals("FQN") || parentRule.getName().equals("ImportedFQN")) { + QualifiedName fqn = importHelpers.toFQN(leaf.getParent().getText().trim()); + // query global scope, that is how elements are actually resolved + IScope scope = importHelpers.getVisibleScope(resource, null); + IEObjectDescription candidate = scope.getSingleElement(fqn); // take first + if(candidate != null) { + createHyperlinksTo(resource, region, candidate.getEObjectOrProxy(), acceptor); + } + } + // Import-importURI => create link on uri text + else if(parentRule.getName().equals("Import") && leafRule.getName().equals("STRING")) { + String text = leaf.getText().substring(1, leaf.getText().length() - 1); - XtextHyperlink result = hyperlinkProvider.get(); - result.setHyperlinkText(uritext); - result.setHyperlinkRegion(new Region(leaf.getOffset() + 1, leaf.getLength() - 2)); // omit "" + String uritext = importHelpers.getUriResolver().resolve(text, resource); + if(uritext != null) { try { + XtextHyperlink result = hyperlinkProvider.get(); + result.setHyperlinkText(uritext); + result.setHyperlinkRegion(region); // ignore: deprecated since Xtext 2.18 result.setURI(URI.createURI(uritext)); - - return result; + acceptor.accept(result); } catch (IllegalArgumentException e) { } } } } - - return null; + + super.createHyperlinksByOffset(resource, offset, acceptor); } } diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/base/util/ImportHelpers.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/base/util/ImportHelpers.java index ec3bab4e0..010ab8e4d 100644 --- a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/base/util/ImportHelpers.java +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/base/util/ImportHelpers.java @@ -19,28 +19,71 @@ import java.util.List; import java.util.Optional; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.etrice.core.common.base.Import; +import org.eclipse.etrice.core.common.scoping.ModelLocatorUriResolver; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.QualifiedName; 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.impl.ImportUriResolver; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; +import org.eclipse.xtext.scoping.IScope; import com.google.common.base.Predicate; +import com.google.common.base.Predicates; import com.google.common.collect.Lists; +import com.google.inject.Inject; public class ImportHelpers { + private final static EClass EOBJECT = EcorePackage.eINSTANCE.getEObject(); + + @Inject IQualifiedNameConverter nameConverter; + @Inject ModelLocatorUriResolver importUriResolver; + @Inject IGlobalScopeProvider globalScope; // visible/imported scope + @Inject IResourceDescriptions resourceDescriptions; // world scope + + public ModelLocatorUriResolver getUriResolver() { + return importUriResolver; + } + +// /** +// * Returns elements from workspace. +// */ +// public Iterable findInWorskpace(QualifiedName fqn) { +// return resourceDescriptions.getExportedObjects(EOBJECT, fqn, false); +// } + + /** + * Returns current visible/imported scope of given resource. + */ + public IScope getVisibleScope(Resource context) { + return getVisibleScope(context, null); + } + + /** + * Returns current visible/imported scope of given resource. + */ + public IScope getVisibleScope(Resource context, EClass type) { + EReference reference = EcoreFactory.eINSTANCE.createEReference(); + reference.setEType((type != null) ? type : EOBJECT); + + return globalScope.getScope(context, reference, Predicates.alwaysTrue()); + } + /** * Returns a list of imported target eObjects for an import or absent if import is not computable. - * The list contains either a single exact match (name and quickFixCandidateMatcher) or candidates (name - * or quickFixCandidateMatcher) or empty. + * The list contains either a single exact match (imported namespace && quickFixCandidateMatcher) + * or candidates only (imported namespace || quickFixCandidateMatcher) or is optional empty. */ - public static Optional> getImportedObjectsFor(Import imp, ImportUriResolver importUriResolver, - Predicate quickFixCandidateMatcher) { + public Optional> getImportedObjectsFor(Import imp, Predicate quickFixCandidateMatcher) { QualifiedName importedFQN = toFQN(imp); if (importedFQN == null) @@ -89,16 +132,19 @@ public class ImportHelpers { return Optional.of(candidates); } - - public static QualifiedName toFQN(Import imp) { - IQualifiedNameConverter nameConverter = new IQualifiedNameConverter.DefaultImpl(); + + public QualifiedName toFQN(String fqn) { boolean isWildcard = false; try { - QualifiedName orig = nameConverter.toQualifiedName(imp.getImportedNamespace()); + QualifiedName orig = nameConverter.toQualifiedName(fqn); isWildcard = orig.getLastSegment().equals("*"); return (isWildcard) ? orig.skipLast(1) : orig; } catch(IllegalArgumentException e){ return null; - } + } + } + + public QualifiedName toFQN(Import imp) { + return toFQN(imp.getImportedNamespace()); } } diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java index 892a124c0..5a1838102 100644 --- a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java @@ -18,8 +18,6 @@ import java.util.HashSet; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.etrice.core.common.base.Annotation; @@ -35,17 +33,13 @@ import org.eclipse.etrice.core.common.base.KeyValue; import org.eclipse.etrice.core.common.base.RealLiteral; import org.eclipse.etrice.core.common.base.SimpleAnnotationAttribute; import org.eclipse.etrice.core.common.base.StringLiteral; -import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.etrice.core.common.base.util.ImportHelpers; import org.eclipse.etrice.generator.base.io.IModelPathProvider; -import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.resource.IEObjectDescription; -import org.eclipse.xtext.scoping.IGlobalScopeProvider; -import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.impl.ImportUriResolver; import org.eclipse.xtext.validation.Check; -import com.google.common.base.Predicates; import com.google.inject.Inject; /** @@ -65,8 +59,7 @@ public class BaseJavaValidator extends org.eclipse.etrice.core.common.validation public static final String IMPORTED_NAMESPACE_MISSING = "BaseJavaValidator.ImportedNamespaceMissing"; @Inject ImportUriResolver importUriResolver; - @Inject IGlobalScopeProvider globalScopeProvider; - @Inject IQualifiedNameConverter nameConverter; + @Inject ImportHelpers importHelpers; @Inject IModelPathProvider modelPathProvider; @Check @@ -246,30 +239,20 @@ public class BaseJavaValidator extends org.eclipse.etrice.core.common.validation */ @Check public void checkImportedNamespace(Import imp) { - if(imp.getImportURI() != null) { + if(imp.getImportURI() != null || imp.getImportedNamespace() == null) { return; } - String name = imp.getImportedNamespace(); - if(name == null) { - return; - } + QualifiedName fqn = importHelpers.toFQN(imp); + Resource resource = imp.eResource(); if(modelPathProvider.get(resource).isEmpty()) { error("no modelpath definition present", BasePackage.Literals.IMPORT__IMPORTED_NAMESPACE, MODELPATH_DESCRIPTION_MISSING); return; - } - QualifiedName importedNamespace = nameConverter.toQualifiedName(name); - if(importedNamespace.getLastSegment().equals("*")) { - return; - } - - EReference reference = EcoreFactory.eINSTANCE.createEReference(); - reference.setEType(EcorePackage.eINSTANCE.getEObject()); - IScope scope = globalScopeProvider.getScope(resource, reference, Predicates.alwaysTrue()); - IEObjectDescription eod = scope.getSingleElement(importedNamespace); + } + IEObjectDescription eod = importHelpers.getVisibleScope(resource).getSingleElement(fqn); if(eod == null) { - error("could not find imported namespace " + importedNamespace, BasePackage.Literals.IMPORT__IMPORTED_NAMESPACE, IMPORTED_NAMESPACE_MISSING); + error("could not find imported namespace " + fqn, BasePackage.Literals.IMPORT__IMPORTED_NAMESPACE, IMPORTED_NAMESPACE_MISSING); } } } diff --git a/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/highlight/RoomSemanticHighlightingCalculator.java b/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/highlight/RoomSemanticHighlightingCalculator.java index 75bf907b7..b3a885c4d 100644 --- a/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/highlight/RoomSemanticHighlightingCalculator.java +++ b/plugins/org.eclipse.etrice.core.room.ui/src/org/eclipse/etrice/core/ui/highlight/RoomSemanticHighlightingCalculator.java @@ -52,7 +52,6 @@ import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.XtextResource; -import org.eclipse.xtext.scoping.impl.ImportUriResolver; import org.eclipse.xtext.util.CancelIndicator; import com.google.common.base.Predicate; @@ -69,7 +68,7 @@ public class RoomSemanticHighlightingCalculator extends BaseSemanticHighlighter @Inject RoomGrammarAccess grammar; @Inject RoomValueConverterService converterService; @Inject RoomHelpers roomHelpers; - @Inject ImportUriResolver importUriResolver; + @Inject ImportHelpers importHelpers; @Override public void provideHighlightingFor(XtextResource resource, IHighlightedPositionAcceptor acceptor, CancelIndicator cancelIndicator) { @@ -139,7 +138,7 @@ public class RoomSemanticHighlightingCalculator extends BaseSemanticHighlighter Predicate nameMatcher = (input) -> { return importElement.getImportedNamespace().equals(input.getQualifiedName().toString()); }; - Optional> matches = ImportHelpers.getImportedObjectsFor(importElement, importUriResolver, nameMatcher); + Optional> matches = importHelpers.getImportedObjectsFor(importElement, nameMatcher); if(matches.isPresent() && matches.get().size() == 1) { EObject annotatedElement = matches.get().get(0).getEObjectOrProxy(); if(annotatedElement instanceof RoomElement) { diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java index 3542a62b1..2f9f12545 100644 --- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java +++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/validation/RoomJavaValidator.java @@ -86,7 +86,6 @@ import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.resource.IEObjectDescription; -import org.eclipse.xtext.scoping.impl.ImportUriResolver; import org.eclipse.xtext.validation.Check; import org.eclipse.xtext.validation.CheckType; @@ -109,11 +108,11 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { @Inject protected IQualifiedNameConverter nameConverter; - @Inject ImportUriResolver importUriResolver; - @Inject private IModelPathProvider modelPathProvider; @Inject InterfaceContractHelpers contractMonitorHelpers; + + @Inject ImportHelpers importHelpers; /* message strings */ public static final String OPTIONAL_REFS_HAVE_TO_HAVE_MULTIPLICITY_ANY = "optional refs have to have multiplicity any [*]"; @@ -153,7 +152,7 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { @Check public void checkRoomImportedNamespace(Import imp) { - QualifiedName importedFQN = ImportHelpers.toFQN(imp); + QualifiedName importedFQN = importHelpers.toFQN(imp); if(importedFQN == null) return; @@ -182,7 +181,7 @@ public class RoomJavaValidator extends AbstractRoomJavaValidator { } }; - Optional> importCandidates = ImportHelpers.getImportedObjectsFor(imp, importUriResolver, candidateMatcher); + Optional> importCandidates = importHelpers.getImportedObjectsFor(imp, candidateMatcher); if(!importCandidates.isPresent()) { return; } -- cgit v1.2.3