Java tutorial
/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.auidt; import com.android.ide.eclipse.auidt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.auidt.internal.project.ProjectHelper; import com.android.ide.eclipse.ddms.ISourceRevealer; import com.google.common.base.Predicate; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.SearchRequestor; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.window.Window; import org.eclipse.ui.IPerspectiveRegistry; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.WorkbenchException; import org.eclipse.ui.dialogs.ListDialog; import org.eclipse.ui.ide.IDE; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Implementation of the com.android.ide.ddms.sourceRevealer extension point. * Note that this code is duplicated in the PDT plugin's SourceRevealer as well. */ public class SourceRevealer implements ISourceRevealer { @Override public boolean reveal(String applicationName, String className, int line) { IProject project = ProjectHelper.findAndroidProjectByAppName(applicationName); if (project != null) { return BaseProjectHelper.revealSource(project, className, line); } return false; } /** * Reveal the source for given fully qualified method name.<br> * * The method should take care of the following scenarios:<ol> * <li> A search, either by filename/line number, or for fqmn might provide only 1 result. * In such a case, just open that result. Give preference to the file name/line # search * since that is the most accurate (gets to the line number). </li> * <li> The search might not provide any results. e.g, the method name may be of the form * "com.x.y$1.methodName". Searches for methods within anonymous classes will fail. In * such a case, if the fileName:lineNumber argument is available, a search for that * should be made instead. </li> * <li> The search might provide multiple results. In such a case, the fileName/lineNumber * values should be utilized to narrow down the results.</li> * </ol> * * @param fqmn fully qualified method name * @param fileName file name in which the method is present, null if not known * @param lineNumber line number in the file which should be given focus, -1 if not known. * Line numbers begin at 1, not 0. * @param perspective perspective to switch to before the source is revealed, null to not * switch perspectives */ @Override public boolean revealMethod(String fqmn, String fileName, int lineNumber, String perspective) { // Search by filename:linenumber. If there is just one result for it, that would // be the correct match that is accurate to the line List<SearchMatch> fileMatches = Collections.emptyList(); if (fileName != null && lineNumber >= 0) { fileMatches = searchForFile(fileName); if (fileMatches.size() == 1) { return revealLineMatch(fileMatches, fileName, lineNumber, perspective); } } List<SearchMatch> methodMatches = searchForMethod(fqmn); // if there is a unique method name match: // 1. if there are > 1 file name matches, try to see if they can be narrowed down // 2. if not, display the method match if (methodMatches.size() == 1) { if (fileMatches.size() > 0) { List<SearchMatch> filteredMatches = filterMatchByResource(fileMatches, methodMatches.get(0).getResource()); if (filteredMatches.size() == 1) { return revealLineMatch(filteredMatches, fileName, lineNumber, perspective); } } return displayMethod((IMethod) methodMatches.get(0).getElement(), perspective); } // no matches for search by method, so search by filename if (methodMatches.size() == 0) { if (fileMatches.size() > 0) { return revealLineMatch(fileMatches, fileName, lineNumber, perspective); } else { return false; } } // multiple matches for search by method, narrow down by filename if (fileName != null) { return revealLineMatch(filterMatchByFileName(methodMatches, fileName), fileName, lineNumber, perspective); } // prompt the user SearchMatch match = getMatchToDisplay(methodMatches, fqmn); if (match == null) { return false; } else { return displayMethod((IMethod) match.getElement(), perspective); } } private boolean revealLineMatch(List<SearchMatch> matches, String fileName, int lineNumber, String perspective) { SearchMatch match = getMatchToDisplay(matches, String.format("%s:%d", fileName, lineNumber)); if (match == null) { return false; } if (perspective != null) { SourceRevealer.switchToPerspective(perspective); } return displayFile((IFile) match.getResource(), lineNumber); } private boolean displayFile(IFile file, int lineNumber) { try { IMarker marker = file.createMarker(IMarker.TEXT); marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); IDE.openEditor(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), marker); marker.delete(); return true; } catch (CoreException e) { AdtPlugin.printErrorToConsole(e.getMessage()); return false; } } private boolean displayMethod(IMethod method, String perspective) { if (perspective != null) { SourceRevealer.switchToPerspective(perspective); } try { JavaUI.openInEditor(method); return true; } catch (Exception e) { AdtPlugin.printErrorToConsole(e.getMessage()); return false; } } private List<SearchMatch> filterMatchByFileName(List<SearchMatch> matches, String fileName) { if (fileName == null) { return matches; } // Use a map to collapse multiple matches in a single file into just one match since // we know the line number in the file. Map<IResource, SearchMatch> matchesPerFile = new HashMap<IResource, SearchMatch>(matches.size()); for (SearchMatch m : matches) { if (m.getResource() instanceof IFile && m.getResource().getName().startsWith(fileName)) { matchesPerFile.put(m.getResource(), m); } } List<SearchMatch> filteredMatches = new ArrayList<SearchMatch>(matchesPerFile.values()); // sort results, first by project name, then by file name Collections.sort(filteredMatches, new Comparator<SearchMatch>() { @Override public int compare(SearchMatch m1, SearchMatch m2) { String p1 = m1.getResource().getProject().getName(); String p2 = m2.getResource().getProject().getName(); if (!p1.equals(p2)) { return p1.compareTo(p2); } String r1 = m1.getResource().getName(); String r2 = m2.getResource().getName(); return r1.compareTo(r2); } }); return filteredMatches; } private List<SearchMatch> filterMatchByResource(List<SearchMatch> matches, IResource resource) { List<SearchMatch> filteredMatches = new ArrayList<SearchMatch>(matches.size()); for (SearchMatch m : matches) { if (m.getResource().equals(resource)) { filteredMatches.add(m); } } return filteredMatches; } private SearchMatch getMatchToDisplay(List<SearchMatch> matches, String searchTerm) { // no matches for given search if (matches.size() == 0) { return null; } // there is only 1 match, so we return that if (matches.size() == 1) { return matches.get(0); } // multiple matches, prompt the user to select IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if (window == null) { return null; } ListDialog dlg = new ListDialog(window.getShell()); dlg.setMessage("Multiple files match search: " + searchTerm); dlg.setTitle("Select file to open"); dlg.setInput(matches); dlg.setContentProvider(new IStructuredContentProvider() { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public void dispose() { } @Override public Object[] getElements(Object inputElement) { return ((List<?>) inputElement).toArray(); } }); dlg.setLabelProvider(new LabelProvider() { @Override public String getText(Object element) { SearchMatch m = (SearchMatch) element; return String.format("/%s/%s", //$NON-NLS-1$ m.getResource().getProject().getName(), m.getResource().getProjectRelativePath().toString()); } }); dlg.setInitialSelections(new Object[] { matches.get(0) }); dlg.setHelpAvailable(false); if (dlg.open() == Window.OK) { Object[] selectedMatches = dlg.getResult(); if (selectedMatches.length > 0) { return (SearchMatch) selectedMatches[0]; } } return null; } private List<SearchMatch> searchForFile(String fileName) { return searchForPattern(fileName, IJavaSearchConstants.CLASS, MATCH_IS_FILE_PREDICATE); } private List<SearchMatch> searchForMethod(String fqmn) { return searchForPattern(fqmn, IJavaSearchConstants.METHOD, MATCH_IS_METHOD_PREDICATE); } private List<SearchMatch> searchForPattern(String pattern, int searchFor, Predicate<SearchMatch> filterPredicate) { SearchEngine se = new SearchEngine(); SearchPattern searchPattern = SearchPattern.createPattern(pattern, searchFor, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE); SearchResultAccumulator requestor = new SearchResultAccumulator(filterPredicate); try { se.search(searchPattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, SearchEngine.createWorkspaceScope(), requestor, new NullProgressMonitor()); } catch (CoreException e) { AdtPlugin.printErrorToConsole(e.getMessage()); return Collections.emptyList(); } return requestor.getMatches(); } private static final Predicate<SearchMatch> MATCH_IS_FILE_PREDICATE = new Predicate<SearchMatch>() { @Override public boolean apply(SearchMatch match) { return match.getResource() instanceof IFile; } }; private static final Predicate<SearchMatch> MATCH_IS_METHOD_PREDICATE = new Predicate<SearchMatch>() { @Override public boolean apply(SearchMatch match) { return match.getResource() instanceof IFile; } }; private static class SearchResultAccumulator extends SearchRequestor { private final List<SearchMatch> mSearchMatches = new ArrayList<SearchMatch>(); private final Predicate<SearchMatch> mPredicate; public SearchResultAccumulator(Predicate<SearchMatch> filterPredicate) { mPredicate = filterPredicate; } public List<SearchMatch> getMatches() { return mSearchMatches; } @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { if (mPredicate.apply(match)) { mSearchMatches.add(match); } } } private static void switchToPerspective(String perspectiveId) { IWorkbench workbench = PlatformUI.getWorkbench(); IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); IPerspectiveRegistry perspectiveRegistry = workbench.getPerspectiveRegistry(); if (perspectiveId != null && perspectiveId.length() > 0 && perspectiveRegistry.findPerspectiveWithId(perspectiveId) != null) { try { workbench.showPerspective(perspectiveId, window); } catch (WorkbenchException e) { AdtPlugin.printErrorToConsole(e.getMessage()); } } } }