| /******************************************************************************* |
| * Copyright (c) 2004, 2017 Tasktop Technologies 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: |
| * Tasktop Technologies - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.dltk.internal.mylyn.search; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.DLTKLanguageManager; |
| import org.eclipse.dltk.core.IMember; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.IType; |
| import org.eclipse.dltk.core.search.IDLTKSearchConstants; |
| import org.eclipse.dltk.core.search.IDLTKSearchScope; |
| import org.eclipse.dltk.core.search.SearchEngine; |
| import org.eclipse.dltk.internal.core.ScriptProject; |
| import org.eclipse.dltk.internal.mylyn.DLTKStructureBridge; |
| import org.eclipse.dltk.internal.mylyn.DLTKUiBridgePlugin; |
| import org.eclipse.dltk.internal.ui.search.DLTKSearchQuery; |
| import org.eclipse.dltk.internal.ui.search.DLTKSearchResult; |
| import org.eclipse.dltk.ui.search.ElementQuerySpecification; |
| import org.eclipse.dltk.ui.search.QuerySpecification; |
| import org.eclipse.mylyn.commons.core.StatusHandler; |
| import org.eclipse.mylyn.context.core.AbstractContextStructureBridge; |
| import org.eclipse.mylyn.context.core.ContextCore; |
| import org.eclipse.mylyn.context.core.IInteractionElement; |
| import org.eclipse.mylyn.internal.context.core.AbstractRelationProvider; |
| import org.eclipse.mylyn.internal.context.core.ContextCorePlugin; |
| import org.eclipse.mylyn.internal.context.core.DegreeOfSeparation; |
| import org.eclipse.mylyn.internal.context.core.IActiveSearchListener; |
| import org.eclipse.mylyn.internal.context.core.IActiveSearchOperation; |
| import org.eclipse.mylyn.internal.context.core.IDegreeOfSeparation; |
| import org.eclipse.mylyn.internal.resources.ui.ResourcesUiBridgePlugin; |
| import org.eclipse.search.ui.ISearchResult; |
| import org.eclipse.search2.internal.ui.InternalSearchUI; |
| |
| /** |
| * @author Mik Kersten |
| */ |
| public abstract class AbstractJavaRelationProvider extends AbstractRelationProvider { |
| |
| public static final String ID_GENERIC = "org.eclipse.mylyn.java.relation"; //$NON-NLS-1$ |
| |
| public static final String NAME = "Java relationships"; //$NON-NLS-1$ |
| |
| private static final int DEFAULT_DEGREE = 2; |
| |
| private static final List<Job> runningJobs = new ArrayList<>(); |
| |
| @Override |
| public String getGenericId() { |
| return ID_GENERIC; |
| } |
| |
| protected AbstractJavaRelationProvider(String structureKind, String id) { |
| super(structureKind, id); |
| } |
| |
| @Override |
| public List<IDegreeOfSeparation> getDegreesOfSeparation() { |
| List<IDegreeOfSeparation> separations = new ArrayList<>(); |
| separations.add(new DegreeOfSeparation(DOS_0_LABEL, 0)); |
| separations.add(new DegreeOfSeparation(DOS_1_LABEL, 1)); |
| separations.add(new DegreeOfSeparation(DOS_2_LABEL, 2)); |
| separations.add(new DegreeOfSeparation(DOS_3_LABEL, 3)); |
| separations.add(new DegreeOfSeparation(DOS_4_LABEL, 4)); |
| separations.add(new DegreeOfSeparation(DOS_5_LABEL, 5)); |
| return separations; |
| } |
| |
| @Override |
| protected void findRelated(final IInteractionElement node, int degreeOfSeparation) { |
| if (node == null) { |
| return; |
| } |
| if (node.getContentType() == null) { |
| StatusHandler.log(new Status(IStatus.WARNING, DLTKUiBridgePlugin.ID_PLUGIN, "Null content type for: " //$NON-NLS-1$ |
| + node)); |
| return; |
| } |
| if (!node.getContentType().equals(DLTKStructureBridge.CONTENT_TYPE)) { |
| return; |
| } |
| IModelElement javaElement = DLTKCore.create(node.getHandleIdentifier()); |
| if (!acceptElement(javaElement) || !javaElement.exists()/* |
| * || |
| * javaElement |
| * instanceof |
| * IInitializer |
| */) { |
| return; |
| } |
| |
| IDLTKSearchScope scope = createJavaSearchScope(javaElement, degreeOfSeparation); |
| if (scope != null) { |
| runJob(node, degreeOfSeparation, getId()); |
| } |
| } |
| |
| private IDLTKSearchScope createJavaSearchScope(IModelElement element, int degreeOfSeparation) { |
| Set<IInteractionElement> landmarks = ContextCore.getContextManager().getActiveLandmarks(); |
| List<IInteractionElement> interestingElements = ContextCore.getContextManager() |
| .getActiveContext() |
| .getInteresting(); |
| |
| Set<IModelElement> searchElements = new HashSet<>(); |
| int includeMask = IDLTKSearchScope.SOURCES; |
| if (degreeOfSeparation == 1) { |
| for (IInteractionElement landmark : landmarks) { |
| AbstractContextStructureBridge bridge = ContextCore.getStructureBridge(landmark.getContentType()); |
| if (includeNodeInScope(landmark, bridge)) { |
| Object o = bridge.getObjectForHandle(landmark.getHandleIdentifier()); |
| if (o instanceof IModelElement) { |
| IModelElement landmarkElement = (IModelElement) o; |
| if (landmarkElement.exists()) { |
| if (landmarkElement instanceof IMember && !landmark.getInterest().isPropagated()) { |
| searchElements.add(((IMember) landmarkElement).getSourceModule()); |
| } else if (landmarkElement instanceof ISourceModule) { |
| searchElements.add(landmarkElement); |
| } |
| } |
| } |
| } |
| } |
| } else if (degreeOfSeparation == 2) { |
| for (IInteractionElement interesting : interestingElements) { |
| AbstractContextStructureBridge bridge = ContextCore.getStructureBridge(interesting.getContentType()); |
| if (includeNodeInScope(interesting, bridge)) { |
| Object object = bridge.getObjectForHandle(interesting.getHandleIdentifier()); |
| if (object instanceof IModelElement) { |
| IModelElement interestingElement = (IModelElement) object; |
| if (interestingElement.exists()) { |
| if (interestingElement instanceof IMember && !interesting.getInterest().isPropagated()) { |
| searchElements.add(((IMember) interestingElement).getSourceModule()); |
| } else if (interestingElement instanceof ISourceModule) { |
| searchElements.add(interestingElement); |
| } |
| } |
| } |
| } |
| } |
| } else if (degreeOfSeparation == 3 || degreeOfSeparation == 4) { |
| for (IInteractionElement interesting : interestingElements) { |
| AbstractContextStructureBridge bridge = ContextCore.getStructureBridge(interesting.getContentType()); |
| if (includeNodeInScope(interesting, bridge)) { |
| // TODO what to do when the element is not a java element, |
| // how determine if a javaProject? |
| IResource resource = ResourcesUiBridgePlugin.getDefault().getResourceForElement(interesting, true); |
| if (resource != null) { |
| IProject project = resource.getProject(); |
| if (project != null && ScriptProject.hasScriptNature(project) && project.exists()) { |
| IScriptProject javaProject = DLTKCore.create(project);// ((IModelElement)o).getJavaProject(); |
| if (javaProject != null && javaProject.exists()) { |
| searchElements.add(javaProject); |
| } |
| } |
| } |
| } |
| } |
| if (degreeOfSeparation == 4) { |
| |
| includeMask = IDLTKSearchScope.SOURCES | IDLTKSearchScope.APPLICATION_LIBRARIES |
| | IDLTKSearchScope.SYSTEM_LIBRARIES; |
| } |
| } else if (degreeOfSeparation == 5) { |
| return SearchEngine.createWorkspaceScope(DLTKLanguageManager.getLanguageToolkit(element)); |
| } |
| |
| if (searchElements.size() == 0) { |
| return null; |
| } else { |
| IModelElement[] elements = new IModelElement[searchElements.size()]; |
| int j = 0; |
| for (IModelElement searchElement : searchElements) { |
| elements[j] = searchElement; |
| j++; |
| } |
| return SearchEngine.createSearchScope(elements, includeMask, |
| DLTKLanguageManager.getLanguageToolkit(element)); |
| } |
| } |
| |
| /** |
| * Only include Java elements and files. |
| */ |
| private boolean includeNodeInScope(IInteractionElement interesting, AbstractContextStructureBridge bridge) { |
| if (interesting == null || bridge == null) { |
| return false; |
| } else { |
| if (interesting.getContentType() == null) { |
| // TODO: remove |
| StatusHandler.log(new Status(IStatus.WARNING, DLTKUiBridgePlugin.ID_PLUGIN, "Null content type for: " //$NON-NLS-1$ |
| + interesting.getHandleIdentifier())); |
| return false; |
| } else { |
| return interesting.getContentType().equals(DLTKStructureBridge.CONTENT_TYPE) |
| || bridge.isDocument(interesting.getHandleIdentifier()); |
| } |
| } |
| } |
| |
| protected boolean acceptResultElement(IModelElement element) { |
| return true; // !(element instanceof IImportDeclaration); |
| } |
| |
| protected boolean acceptElement(IModelElement javaElement) { |
| return javaElement != null && (javaElement instanceof IMember || javaElement instanceof IType); |
| } |
| |
| private void runJob(final IInteractionElement node, final int degreeOfSeparation, final String kind) { |
| |
| int limitTo = 0; |
| if (kind.equals(DLTKReferencesProvider.ID)) { |
| limitTo = IDLTKSearchConstants.REFERENCES; |
| // } else if (kind.equals(JavaImplementorsProvider.ID)) { |
| // limitTo = IDLTKSearchConstants.IMPLEMENTORS; |
| } else if (kind.equals(JUnitReferencesProvider.ID)) { |
| limitTo = IDLTKSearchConstants.REFERENCES; |
| } else if (kind.equals(DLTKReadAccessProvider.ID)) { |
| limitTo = IDLTKSearchConstants.REFERENCES; |
| } else if (kind.equals(DLTKWriteAccessProvider.ID)) { |
| limitTo = IDLTKSearchConstants.REFERENCES; |
| } |
| |
| final JavaSearchOperation query = (JavaSearchOperation) getSearchOperation(node, limitTo, degreeOfSeparation); |
| if (query == null) { |
| return; |
| } |
| |
| JavaSearchJob job = new JavaSearchJob(query.getLabel(), query); |
| query.addListener(new IActiveSearchListener() { |
| |
| private boolean gathered = false; |
| |
| @Override |
| public boolean resultsGathered() { |
| return gathered; |
| } |
| |
| @Override |
| public void searchCompleted(List<?> l) { |
| if (l == null) { |
| return; |
| } |
| List<IModelElement> relatedHandles = new ArrayList<>(); |
| Object[] elements = l.toArray(); |
| for (Object element : elements) { |
| if (element instanceof IModelElement) { |
| relatedHandles.add((IModelElement) element); |
| } |
| } |
| |
| for (IModelElement element : relatedHandles) { |
| if (!acceptResultElement(element)) { |
| continue; |
| } |
| incrementInterest(node, DLTKStructureBridge.CONTENT_TYPE, element.getHandleIdentifier(), |
| degreeOfSeparation); |
| } |
| gathered = true; |
| AbstractJavaRelationProvider.this.searchCompleted(node); |
| } |
| |
| }); |
| InternalSearchUI.getInstance(); |
| |
| runningJobs.add(job); |
| job.setPriority(Job.DECORATE - 10); |
| job.schedule(); |
| } |
| |
| @Override |
| public IActiveSearchOperation getSearchOperation(IInteractionElement node, int limitTo, int degreeOfSeparation) { |
| IModelElement javaElement = DLTKCore.create(node.getHandleIdentifier()); |
| if (javaElement == null || !javaElement.exists()) { |
| return null; |
| } |
| |
| IDLTKSearchScope scope = createJavaSearchScope(javaElement, degreeOfSeparation); |
| |
| if (scope == null) { |
| return null; |
| } |
| |
| QuerySpecification specs = new ElementQuerySpecification(javaElement, limitTo, scope, |
| Messages.AbstractJavaRelationProvider_Mylyn_degree_of_separation + degreeOfSeparation); |
| |
| return new JavaSearchOperation(specs); |
| } |
| |
| protected static class JavaSearchJob extends Job { |
| |
| private final JavaSearchOperation op; |
| |
| public JavaSearchJob(String name, JavaSearchOperation op) { |
| super(name); |
| this.op = op; |
| } |
| |
| /** |
| * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| return op.run(monitor); |
| } |
| |
| } |
| |
| protected static class JavaSearchOperation extends DLTKSearchQuery implements IActiveSearchOperation { |
| private ISearchResult result = null; |
| |
| @Override |
| public ISearchResult getSearchResult() { |
| if (result == null) { |
| result = new DLTKSearchResult(this); |
| } |
| new DLTKActiveSearchResultUpdater((DLTKSearchResult) result); |
| return result; |
| } |
| |
| @Override |
| public IStatus run(IProgressMonitor monitor) { |
| try { |
| IStatus runStatus = super.run(monitor); |
| ISearchResult result = getSearchResult(); |
| if (result instanceof DLTKSearchResult) { |
| // TODO make better |
| Object[] objs = ((DLTKSearchResult) result).getElements(); |
| if (objs == null) { |
| notifySearchCompleted(null); |
| } else { |
| List<Object> l = new ArrayList<>(); |
| for (Object obj : objs) { |
| l.add(obj); |
| } |
| notifySearchCompleted(l); |
| } |
| } |
| return runStatus; |
| } catch (Throwable t) { |
| StatusHandler.log(new Status(IStatus.ERROR, DLTKUiBridgePlugin.ID_PLUGIN, "Java search failed", t)); //$NON-NLS-1$ |
| } |
| |
| IStatus status = new Status(IStatus.WARNING, ContextCorePlugin.ID_PLUGIN, IStatus.OK, |
| Messages.AbstractJavaRelationProvider_could_not_run_Java_search, null); |
| notifySearchCompleted(null); |
| return status; |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param data |
| */ |
| public JavaSearchOperation(QuerySpecification data) { |
| super(data); |
| |
| } |
| |
| /** List of listeners wanting to know about the searches */ |
| private final List<IActiveSearchListener> listeners = new ArrayList<>(); |
| |
| /** |
| * Add a listener for when the bugzilla search is completed |
| * |
| * @param l |
| * The listener to add |
| */ |
| @Override |
| public void addListener(IActiveSearchListener l) { |
| // add the listener to the list |
| listeners.add(l); |
| } |
| |
| /** |
| * Remove a listener for when the bugzilla search is completed |
| * |
| * @param l |
| * The listener to remove |
| */ |
| @Override |
| public void removeListener(IActiveSearchListener l) { |
| // remove the listener from the list |
| listeners.remove(l); |
| } |
| |
| /** |
| * Notify all of the listeners that the bugzilla search is completed |
| * |
| * @param doiList |
| * A list of BugzillaSearchHitDoiInfo |
| * @param member |
| * The IMember that the search was performed on |
| */ |
| public void notifySearchCompleted(List<Object> l) { |
| // go through all of the listeners and call |
| // searchCompleted(colelctor, |
| // member) |
| for (IActiveSearchListener listener : listeners) { |
| listener.searchCompleted(l); |
| } |
| } |
| |
| } |
| |
| @Override |
| public void stopAllRunningJobs() { |
| for (Job j : runningJobs) { |
| j.cancel(); |
| } |
| runningJobs.clear(); |
| } |
| |
| @Override |
| protected int getDefaultDegreeOfSeparation() { |
| return DEFAULT_DEGREE; |
| } |
| } |