1 /******************************************************************************* 2 * Copyright (c) 2018, 2019 Angelo Zerr and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * - Angelo Zerr: initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.ui.javaeditor.codemining; 15 16 import java.text.MessageFormat; 17 import java.util.concurrent.CompletableFuture; 18 import java.util.concurrent.atomic.AtomicLong; 19 import java.util.function.Consumer; 20 21 import org.eclipse.swt.SWT; 22 import org.eclipse.swt.events.MouseEvent; 23 24 import org.eclipse.core.runtime.CoreException; 25 import org.eclipse.core.runtime.IProgressMonitor; 26 27 import org.eclipse.core.resources.ResourcesPlugin; 28 29 import org.eclipse.jface.text.BadLocationException; 30 import org.eclipse.jface.text.IDocument; 31 import org.eclipse.jface.text.ITextViewer; 32 import org.eclipse.jface.text.codemining.ICodeMiningProvider; 33 34 import org.eclipse.ui.IEditorPart; 35 36 import org.eclipse.ui.texteditor.ITextEditor; 37 38 import org.eclipse.search.ui.NewSearchUI; 39 40 import org.eclipse.jdt.core.IJavaElement; 41 import org.eclipse.jdt.core.IJavaProject; 42 import org.eclipse.jdt.core.JavaCore; 43 import org.eclipse.jdt.core.JavaModelException; 44 import org.eclipse.jdt.core.search.IJavaSearchConstants; 45 import org.eclipse.jdt.core.search.IJavaSearchScope; 46 import org.eclipse.jdt.core.search.SearchEngine; 47 import org.eclipse.jdt.core.search.SearchMatch; 48 import org.eclipse.jdt.core.search.SearchParticipant; 49 import org.eclipse.jdt.core.search.SearchPattern; 50 import org.eclipse.jdt.core.search.SearchRequestor; 51 52 import org.eclipse.jdt.ui.actions.FindReferencesAction; 53 54 import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility; 55 import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; 56 import org.eclipse.jdt.internal.ui.search.JavaSearchScopeFactory; 57 58 /** 59 * Java reference code mining. 60 * 61 * @since 3.16 62 */ 63 public class JavaReferenceCodeMining extends AbstractJavaElementLineHeaderCodeMining { 64 65 private final JavaEditor editor; 66 67 private final boolean showReferencesAtLeastOne; 68 69 private Consumer<MouseEvent> action; 70 JavaReferenceCodeMining(IJavaElement element, JavaEditor editor, IDocument document, ICodeMiningProvider provider, boolean showReferencesAtLeastOne)71 public JavaReferenceCodeMining(IJavaElement element, JavaEditor editor, IDocument document, 72 ICodeMiningProvider provider, boolean showReferencesAtLeastOne) 73 throws JavaModelException, BadLocationException { 74 super(element, document, provider, null); 75 this.editor= editor; 76 this.showReferencesAtLeastOne= showReferencesAtLeastOne; 77 } 78 79 @SuppressWarnings("boxing") 80 @Override doResolve(ITextViewer viewer, IProgressMonitor monitor)81 protected CompletableFuture<Void> doResolve(ITextViewer viewer, IProgressMonitor monitor) { 82 return CompletableFuture.runAsync(() -> { 83 try { 84 monitor.isCanceled(); 85 IJavaElement element= super.getElement(); 86 long refCount= countReferences(element, monitor); 87 monitor.isCanceled(); 88 action= refCount > 0 ? e -> { 89 if (refCount == 1 && (e.stateMask & SWT.CTRL) == SWT.CTRL) { 90 // Ctrl + Click is done, open the referenced element in the Java Editor 91 try { 92 SearchMatch match= getReferenceMatch(element, monitor); 93 IJavaElement javaElement= (IJavaElement) match.getElement(); 94 IEditorPart part= EditorUtility.openInEditor(javaElement); 95 if (part != null) { 96 EditorUtility.revealInEditor(part, javaElement); 97 if (part instanceof ITextEditor) { 98 ITextEditor textEditor= (ITextEditor) part; 99 textEditor.selectAndReveal(match.getOffset(), match.getLength()); 100 } 101 } 102 } catch (JavaModelException e1) { 103 // Should never occur 104 } catch (CoreException e1) { 105 // Should never occur 106 } 107 } else { 108 // Otherwise, launch references search 109 new FindReferencesAction(editor).run(element); 110 } 111 } : null; 112 if (refCount == 0 && showReferencesAtLeastOne) { 113 super.setLabel(""); //$NON-NLS-1$ 114 } else { 115 super.setLabel(MessageFormat.format(JavaCodeMiningMessages.JavaReferenceCodeMining_label, refCount)); 116 } 117 } catch (JavaModelException e) { 118 // Should never occur 119 } catch (CoreException e) { 120 // Should never occur 121 } 122 }); 123 } 124 125 @Override 126 public Consumer<MouseEvent> getAction() { 127 return action; 128 } 129 130 /** 131 * Return the number of references for the given java element. 132 * 133 * @param element the java element. 134 * @param monitor the monitor 135 * @return he number of references for the given java element. 136 * @throws JavaModelException throws when java error. 137 * @throws CoreException throws when java error. 138 */ 139 private static long countReferences(IJavaElement element, IProgressMonitor monitor) 140 throws JavaModelException, CoreException { 141 if (element == null) { 142 return 0; 143 } 144 final AtomicLong count= new AtomicLong(0); 145 SearchPattern pattern= SearchPattern.createPattern(element, IJavaSearchConstants.REFERENCES); 146 SearchEngine engine= new SearchEngine(); 147 final boolean ignoreInaccurate= NewSearchUI.arePotentialMatchesIgnored(); 148 engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, 149 createSearchScope(element), new SearchRequestor() { 150 151 @Override 152 public void acceptSearchMatch(SearchMatch match) throws CoreException { 153 if (match.getAccuracy() == SearchMatch.A_INACCURATE && ignoreInaccurate) { 154 return; 155 } 156 Object o= match.getElement(); 157 if (o instanceof IJavaElement) { 158 IJavaElement e= (IJavaElement)o; 159 if (e.getAncestor(IJavaElement.COMPILATION_UNIT) != null 160 || e.getAncestor(IJavaElement.CLASS_FILE) != null) { 161 count.incrementAndGet(); 162 } 163 } 164 } 165 }, monitor); 166 167 return count.get(); 168 } 169 170 /** 171 * Return the single search match of references for the given java element. 172 * 173 * @param element the java element. 174 * @param monitor the monitor 175 * @return he number of references for the given java element. 176 * @throws JavaModelException throws when java error. 177 * @throws CoreException throws when java error. 178 */ 179 private SearchMatch getReferenceMatch(IJavaElement element, IProgressMonitor monitor) 180 throws JavaModelException, CoreException { 181 if (element == null) { 182 return null; 183 } 184 final SearchMatch[] matches= new SearchMatch[1]; 185 SearchPattern pattern= SearchPattern.createPattern(element, IJavaSearchConstants.REFERENCES); 186 SearchEngine engine= new SearchEngine(); 187 engine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, 188 createSourceSearchScope(), new SearchRequestor() { 189 190 @Override 191 public void acceptSearchMatch(final SearchMatch match) throws CoreException { 192 Object o= match.getElement(); 193 if (o instanceof IJavaElement 194 && ((IJavaElement) o).getAncestor(IJavaElement.COMPILATION_UNIT) != null) { 195 matches[0]= match; 196 } 197 } 198 }, monitor); 199 200 return matches[0]; 201 } 202 203 /** 204 * Create Java workspace scope. 205 * 206 * @param element IJavaElement to search references for 207 * 208 * @return the Java workspace scope. 209 * @throws JavaModelException when java error. 210 */ 211 private static IJavaSearchScope createSearchScope(IJavaElement element) throws JavaModelException { 212 JavaSearchScopeFactory factory= JavaSearchScopeFactory.getInstance(); 213 boolean isInsideJRE = factory.isInsideJRE(element); 214 IJavaSearchScope scope= factory.createWorkspaceScope(isInsideJRE); 215 return scope; 216 } 217 218 /** 219 * Create Java source search scope. 220 * 221 * @return the Java workspace scope. 222 * @throws JavaModelException when java error. 223 */ 224 private static IJavaSearchScope createSourceSearchScope() throws JavaModelException { 225 IJavaProject[] projects= JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects(); 226 return SearchEngine.createJavaSearchScope(projects, IJavaSearchScope.SOURCES); 227 } 228 } 229