diff options
author | Christian W. Damus | 2013-08-29 21:21:04 +0000 |
---|---|---|
committer | Christian W. Damus | 2013-09-06 18:38:59 +0000 |
commit | fd811174e7210d1d1bdc1e18af8ced3ab2469d52 (patch) | |
tree | 14fa2e8b52c057b85058c5a175e440500a751023 /plugins | |
parent | bdc0bb85cd600f2496b29e1006e0dbce106181eb (diff) | |
download | org.eclipse.papyrus-fd811174e7210d1d1bdc1e18af8ced3ab2469d52.tar.gz org.eclipse.papyrus-fd811174e7210d1d1bdc1e18af8ced3ab2469d52.tar.xz org.eclipse.papyrus-fd811174e7210d1d1bdc1e18af8ced3ab2469d52.zip |
415371: [CDO] Pluggable scope providers for workspace and selection search.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=415371
Implement a new extension point for pluggable "scope providers" that determine the appropriate URIs to define the search scope based on the user's workbench selection, or implicitly everything in the workspace and/or in connected repositories.
Diffstat (limited to 'plugins')
6 files changed, 497 insertions, 80 deletions
diff --git a/plugins/views/org.eclipse.papyrus.views.search/build.properties b/plugins/views/org.eclipse.papyrus.views.search/build.properties index 1b08f841b5e..53318077600 100644 --- a/plugins/views/org.eclipse.papyrus.views.search/build.properties +++ b/plugins/views/org.eclipse.papyrus.views.search/build.properties @@ -3,5 +3,7 @@ output.. = bin/ bin.includes = META-INF/,\
.,\
about.html,\
- plugin.properties
-src.includes = about.html
+ plugin.properties,\
+ plugin.xml
+src.includes = about.html,\
+ schema/
diff --git a/plugins/views/org.eclipse.papyrus.views.search/plugin.xml b/plugins/views/org.eclipse.papyrus.views.search/plugin.xml new file mode 100644 index 00000000000..f10ef9096fa --- /dev/null +++ b/plugins/views/org.eclipse.papyrus.views.search/plugin.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.4"?> +<plugin> + <extension-point id="scopeProviders" name="Papyrus Search Scope Providers" schema="schema/scopeProviders.exsd"/> + <extension + point="org.eclipse.papyrus.views.search.scopeProviders"> + <scopeProvider + class="org.eclipse.papyrus.views.search.scope.WorkspaceScopeProvider"> + </scopeProvider> + </extension> + +</plugin> diff --git a/plugins/views/org.eclipse.papyrus.views.search/schema/scopeProviders.exsd b/plugins/views/org.eclipse.papyrus.views.search/schema/scopeProviders.exsd new file mode 100644 index 00000000000..00ee7439413 --- /dev/null +++ b/plugins/views/org.eclipse.papyrus.views.search/schema/scopeProviders.exsd @@ -0,0 +1,129 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.papyrus.views.search" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appinfo> + <meta.schema plugin="org.eclipse.papyrus.views.search" id="scopeProviders" name="Papyrus Search Scope Providers"/> + </appinfo> + <documentation> + Pluggable providers of search scopes for workspace-wide and selection-based searches. + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appinfo> + <meta.element /> + </appinfo> + </annotation> + <complexType> + <sequence minOccurs="1" maxOccurs="unbounded"> + <element ref="scopeProvider"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appinfo> + <meta.attribute translatable="true"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="scopeProvider"> + <complexType> + <attribute name="class" type="string" use="required"> + <annotation> + <documentation> + A class implementing the <tt>IScopeProvider</tt> protocol. + </documentation> + <appinfo> + <meta.attribute kind="java" basedOn=":org.eclipse.papyrus.views.search.scope.IScopeProvider"/> + </appinfo> + </annotation> + </attribute> + <attribute name="priority" type="string" use="default" value="0"> + <annotation> + <documentation> + The non-negative priority of the scope provider; the default is 0 (zero). Providers are invoked by the search framework in priority order, from highest to lowest. + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appinfo> + <meta.section type="since"/> + </appinfo> + <documentation> + 1.0.0 + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="examples"/> + </appinfo> + <documentation> + [Enter extension point usage example here.] + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="apiinfo"/> + </appinfo> + <documentation> + [Enter API information here.] + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="implementation"/> + </appinfo> + <documentation> + [Enter information about supplied implementation of this extension point.] + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + /***************************************************************************** + * Copyright (c) 2013 CEA LIST and others. + * + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * CEA LIST - Initial API and implementation + * + *****************************************************************************/ + </documentation> + </annotation> + +</schema> diff --git a/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/IScopeProvider.java b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/IScopeProvider.java new file mode 100644 index 00000000000..b3f0e914447 --- /dev/null +++ b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/IScopeProvider.java @@ -0,0 +1,57 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * CEA LIST - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.views.search.scope; + +import java.util.Collection; + +import org.eclipse.emf.common.util.URI; + + +/** + * A pluggable provider of scopes used by the default {@link IScopeCollector}. Different providers are expected to handle + * different domains in which EMF model resources can be found. Examples of such domains include + * <ul> + * <li>the Eclipse Workspace</li> + * <li>connected model repositories (e.g., CDO)</li> + * <li>library models deployed in plug-ins</li> + * <li>models discoverable on the Word-Wide Web</li> + * </ul> + * <p> + * For workspace-wide searches, all scope providers are queried for their {@linkplain #getScope() full scope} and the resulting search scope is the + * union of all provided URIs. + * </p> + * <p> + * For selection-scoped searches, providers are queried in priority order for scopes based on each object selected in the workbench. The first + * provider that returns some URIs for a selected object wins; lower-priority providers are not consulted for additional URIs. + * </p> + */ +public interface IScopeProvider { + + /** + * Gets URIs applicable to a workspace-wide search from the provider's domain. This should be whatever is appropriate to + * search all reasonably available resources in the provider's domain, whatever that means. For example, in a model + * repository scope provider, it may be reasonable to expect a search to extend into all connected repositories but not + * any that are currently disconnected (though it may be known where they are and how to connect). + * + * @return all URIs + */ + Collection<URI> getScope(); + + /** + * Gets URIs applicable to a search that is constrained to a given object {@code selected} by the user in the workbench. + * + * @param selected + * an object selected in the workbench + * @return + */ + Collection<URI> getScope(Object selected); +} diff --git a/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/ScopeCollector.java b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/ScopeCollector.java index fbcd9adff3f..74f22d98d04 100644 --- a/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/ScopeCollector.java +++ b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/ScopeCollector.java @@ -14,32 +14,28 @@ *****************************************************************************/
package org.eclipse.papyrus.views.search.scope;
+import static org.eclipse.papyrus.views.search.scope.WorkspaceScopeProvider.findPapyrusModels;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.SortedSet;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.plugin.RegistryReader;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
-import org.eclipse.papyrus.infra.core.resource.ModelMultiException;
-import org.eclipse.papyrus.infra.core.resource.ModelSet;
-import org.eclipse.papyrus.infra.core.resource.NotFoundException;
-import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel;
-import org.eclipse.papyrus.infra.emf.utils.BusinessModelResolver;
-import org.eclipse.papyrus.infra.onefile.model.IPapyrusFile;
import org.eclipse.papyrus.views.search.Activator;
-import org.eclipse.papyrus.views.search.Messages;
-import org.eclipse.papyrus.views.search.utils.ModelUtils;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.ui.IWorkingSet;
@@ -47,8 +43,12 @@ public class ScopeCollector implements IScopeCollector { private static final ScopeCollector instance = new ScopeCollector();
+ private final Iterable<? extends IScopeProvider> scopeProviders;
+
private ScopeCollector() {
super();
+
+ scopeProviders = loadScopeProviders();
}
public final static ScopeCollector getInstance() {
@@ -128,55 +128,24 @@ public class ScopeCollector implements IScopeCollector { Iterator<?> it = selection.iterator();
while(it.hasNext()) {
- Object object = (Object)it.next();
-
- if(object instanceof IPapyrusFile) {
- results.addAll(findPapyrusModels(((IPapyrusFile)object).getMainFile()));
- } else if(object instanceof IResource) {
- results.addAll(findPapyrusModels((IResource)object));
- } else {
- Object element = BusinessModelResolver.getInstance().getBusinessModel(object);
- if(element instanceof EObject) {
- // CDO resource *are* EObjects
- Resource eResource = (element instanceof Resource) ? (Resource) element : ((EObject)element).eResource();
- if(eResource != null) {
- ModelSet modelSet = null;
-
- try {
- modelSet = ModelUtils.openResource(eResource.getURI());
- SashModel sashModel = (SashModel)modelSet.getModelChecked(SashModel.MODEL_ID);
- Resource diResource = sashModel.getResource();
- if (diResource != null) {
- results.add(diResource.getURI());
- }
- } catch (ModelMultiException e) {
- //Do a workspace search instead
- results.addAll(createWorkspaceScope());
- } catch (NotFoundException e) {
- //Do a workspace search instead
- results.addAll(createWorkspaceScope());
- } finally {
- if (modelSet != null) {
- try {
- modelSet.unload();
- } catch (Exception e) {
- Activator.log.error(e);
- }
- }
- }
-
- } else {
- //Do a workspace search instead
- results.addAll(createWorkspaceScope());
- }
-
- } else {
- //Do a workspace search instead
- results.addAll(createWorkspaceScope());
+ Object next = it.next();
+
+ for (IScopeProvider provider : getScopeProviders()) {
+ Collection<URI> scope = provider.getScope(next);
+ if (!scope.isEmpty()) {
+ results.addAll(scope);
+
+ // don't consult the next provider
+ break;
}
-
}
}
+
+ if (results.isEmpty()) {
+ // search the workspace instead, then
+ results.addAll(createWorkspaceScope());
+ }
+
return results;
}
@@ -226,36 +195,162 @@ public class ScopeCollector implements IScopeCollector { }
/**
- * Find all Papyrus models from a specific root
+ * Create a scope when the container is ISearchPageContainer.WORKSPACE_SCOPE
*
- * @param res
- * the root
* @return
- * the found Papyrus models
+ * the scope
*/
- protected Collection<URI> findPapyrusModels(IResource res) {
- ResourceVisitor visitor = new ResourceVisitor();
- try {
- res.accept(visitor, IResource.DEPTH_INFINITE);
- } catch (CoreException e) {
- Activator.log.warn(Messages.ScopeCollector_0 + res);
+ protected Collection<URI> createWorkspaceScope() {
+ Collection<URI> result = new ArrayList<URI>();
+
+ for (IScopeProvider next : getScopeProviders()) {
+ result.addAll(next.getScope());
}
+
+ return result;
+ }
- return visitor.getParticipantURIs();
+ private Iterable<? extends IScopeProvider> loadScopeProviders() {
+ return new ProvidersReader().load();
+ }
+
+ final Iterable<? extends IScopeProvider> getScopeProviders() {
+ List<IScopeProvider> result = new ArrayList<IScopeProvider>();
+
+ synchronized(scopeProviders) {
+ for (IScopeProvider next : scopeProviders) {
+ result.add(next);
+ }
+ }
+
+ return result;
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class PriorityScopeProvider implements IScopeProvider, Comparable<PriorityScopeProvider> {
+ private final IScopeProvider delegate;
+
+ private final int priority;
+
+ public PriorityScopeProvider(IScopeProvider delegate, int priority) {
+ this.delegate = delegate;
+ this.priority = priority;
+ }
+
+ public int compareTo(PriorityScopeProvider o) {
+ // sort by descending priority
+ return o.priority - this.priority;
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return (obj instanceof PriorityScopeProvider) && ((PriorityScopeProvider) obj).delegate.equals(delegate);
+ }
+
+ //
+ // API delegation
+ //
+
+ public Collection<URI> getScope() {
+ return delegate.getScope();
+ }
+
+ public Collection<URI> getScope(Object selected) {
+ return delegate.getScope(selected);
+ }
}
+
+ private class ProvidersReader extends RegistryReader {
+ private static final String EXT_PT = "scopeProviders"; //$NON-NLS-1$
+
+ private static final String TAG_PROVIDER = "scopeProvider"; //$NON-NLS-1$
+
+ private static final String ATTR_CLASS = "class"; //$NON-NLS-1$
+
+ private static final String ATTR_PRIORITY = "priority"; //$NON-NLS-1$
+
+ private final SortedSet<PriorityScopeProvider> providers = new java.util.TreeSet<PriorityScopeProvider>();
- /**
- * Create a scope when the container is ISearchPageContainer.WORKSPACE_SCOPE
- *
- * @return
- * the scope
- */
- protected Collection<URI> createWorkspaceScope() {
+ ProvidersReader() {
+ super(Platform.getExtensionRegistry(), Activator.PLUGIN_ID, EXT_PT);
+ }
+
+ Iterable<? extends IScopeProvider> load() {
+ synchronized(providers) {
+ providers.clear();
+ readRegistry();
+ }
+
+ return providers;
+ }
+
+ @Override
+ protected boolean readElement(IConfigurationElement element, boolean add) {
+ boolean result = false;
- //Go through the workspace root
- IResource root = ResourcesPlugin.getWorkspace().getRoot();
+ if(TAG_PROVIDER.equals(element.getName())) {
+ result = true;
- return findPapyrusModels(root);
- }
+ String className = element.getAttribute(ATTR_CLASS);
+ if((className == null) || (className.length() == 0)) {
+ logMissingAttribute(element, ATTR_CLASS);
+ } else if(add) {
+ addProvider(element, className);
+ } else {
+ removeProvider(element, className);
+ }
+ }
+
+ return result;
+ }
+
+ private void addProvider(IConfigurationElement element, String className) {
+ try {
+ Object provider = element.createExecutableExtension(ATTR_CLASS);
+ if(!(provider instanceof IScopeProvider)) {
+ Activator.log.error("Scope provider extension does not implement IScopeProvider interface: " + className, null); //$NON-NLS-1$
+ } else {
+ String priorityString = element.getAttribute(ATTR_PRIORITY);
+ int priority = 0;
+
+ try {
+ if((priorityString) != null && (priorityString.length() > 0)) {
+ priority = Integer.parseInt(priorityString);
+ if(priority < 0) {
+ Activator.log.warn("Negative priority in scope provider " + className); //$NON-NLS-1$
+ priority = 0;
+ }
+ }
+ } catch (NumberFormatException e) {
+ Activator.log.warn("Not an integer priority in scope provider " + className); //$NON-NLS-1$
+ }
+
+ synchronized(providers) {
+ providers.add(new PriorityScopeProvider((IScopeProvider)provider, priority));
+ }
+ }
+ } catch (CoreException e) {
+ Activator.getDefault().getLog().log(e.getStatus());
+ }
+ }
+
+ private void removeProvider(IConfigurationElement element, String className) {
+ synchronized(providers) {
+ for (Iterator<PriorityScopeProvider> iter = providers.iterator(); iter.hasNext();) {
+ if (iter.next().delegate.getClass().getName().equals(className)) {
+ iter.remove();
+ }
+ }
+ }
+ }
+ }
}
diff --git a/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/WorkspaceScopeProvider.java b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/WorkspaceScopeProvider.java new file mode 100644 index 00000000000..969d8ceca89 --- /dev/null +++ b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/scope/WorkspaceScopeProvider.java @@ -0,0 +1,122 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * CEA LIST - Initial API and implementation + * Christian W. Damus (CEA LIST) - Extracted some of the ScopeCollector implementation. + * + *****************************************************************************/ +package org.eclipse.papyrus.views.search.scope; + +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.papyrus.infra.core.resource.ModelMultiException; +import org.eclipse.papyrus.infra.core.resource.ModelSet; +import org.eclipse.papyrus.infra.core.resource.NotFoundException; +import org.eclipse.papyrus.infra.core.resource.sasheditor.SashModel; +import org.eclipse.papyrus.infra.emf.utils.BusinessModelResolver; +import org.eclipse.papyrus.infra.onefile.model.IPapyrusFile; +import org.eclipse.papyrus.views.search.Activator; +import org.eclipse.papyrus.views.search.Messages; +import org.eclipse.papyrus.views.search.utils.ModelUtils; + + +/** + * The most basic scope-provider implementation, getting scopes for workspace resources. + */ +public class WorkspaceScopeProvider implements IScopeProvider { + + public WorkspaceScopeProvider() { + super(); + } + + public Collection<URI> getScope() { + //Go through the workspace root + IResource root = ResourcesPlugin.getWorkspace().getRoot(); + + return findPapyrusModels(root); + } + + public Collection<URI> getScope(Object object) { + Collection<URI> results; + + if(object instanceof IPapyrusFile) { + results = findPapyrusModels(((IPapyrusFile)object).getMainFile()); + } else if(object instanceof IResource) { + results = findPapyrusModels((IResource)object); + } else { + Object element = BusinessModelResolver.getInstance().getBusinessModel(object); + if(element instanceof EObject) { + // CDO resource *are* EObjects + Resource eResource = (element instanceof Resource) ? (Resource) element : ((EObject)element).eResource(); + if(eResource != null) { + ModelSet modelSet = null; + + try { + modelSet = ModelUtils.openResource(eResource.getURI()); + SashModel sashModel = (SashModel)modelSet.getModelChecked(SashModel.MODEL_ID); + Resource diResource = sashModel.getResource(); + if (diResource != null) { + results = Collections.singletonList(diResource.getURI()); + } else { + results = Collections.emptyList(); + } + } catch (ModelMultiException e) { + results = Collections.emptyList(); + } catch (NotFoundException e) { + results = Collections.emptyList(); + } finally { + if (modelSet != null) { + try { + modelSet.unload(); + } catch (Exception e) { + Activator.log.error(e); + } + } + } + + } else { + results = Collections.emptyList(); + } + + } else { + results = Collections.emptyList(); + } + + } + + return results; + } + + /** + * Find all Papyrus models from a specific root + * + * @param res + * the root + * @return + * the found Papyrus models + */ + static Collection<URI> findPapyrusModels(IResource res) { + ResourceVisitor visitor = new ResourceVisitor(); + try { + res.accept(visitor, IResource.DEPTH_INFINITE); + } catch (CoreException e) { + Activator.log.warn(Messages.ScopeCollector_0 + res); + } + + return visitor.getParticipantURIs(); + } + +} |