/******************************************************************************* * 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; } }