Java tutorial
/* * Copyright (c) 2015 the original author or authors. * 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: * Simon Scholz (vogella GmbH) - initial API and implementation and initial documentation */ package org.eclipse.buildship.ui.view.execution; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Pattern; import org.gradle.tooling.events.OperationDescriptor; import org.gradle.tooling.events.test.JvmTestOperationDescriptor; import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; 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.internal.corext.util.JavaConventionsUtil; import org.eclipse.jdt.ui.JavaUI; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.FindReplaceDocumentAdapter; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.editors.text.TextFileDocumentProvider; import org.eclipse.buildship.ui.UiPlugin; import org.eclipse.buildship.ui.util.editor.EditorUtils; /** * Opens the test source files for the given * {@link org.eclipse.buildship.ui.view.execution.OperationItem} test nodes. Knows how to handle * both Java and Groovy test source files. */ public final class OpenTestSourceFileJob extends Job { private final ImmutableList<OperationItem> operationItems; public OpenTestSourceFileJob(List<OperationItem> operationItems) { super("Opening test source files"); this.operationItems = ImmutableList.copyOf(operationItems); } @Override protected IStatus run(IProgressMonitor monitor) { SubMonitor subMonitor = SubMonitor.convert(monitor, this.operationItems.size()); try { for (OperationItem operationItem : this.operationItems) { if (!monitor.isCanceled()) { searchForTestSource(operationItem, subMonitor.newChild(1)); } else { Bundle bundle = FrameworkUtil.getBundle(getClass()); return new Status(IStatus.CANCEL, bundle.getSymbolicName(), IStatus.CANCEL, "Opening test source files was canceled.", null); } } } finally { monitor.done(); } return Status.OK_STATUS; } private void searchForTestSource(OperationItem operationItem, IProgressMonitor monitor) { OperationDescriptor operationDescriptor = (OperationDescriptor) operationItem .getAdapter(OperationDescriptor.class); if (operationDescriptor instanceof JvmTestOperationDescriptor) { JvmTestOperationDescriptor testOperationDescriptor = (JvmTestOperationDescriptor) operationDescriptor; String className = testOperationDescriptor.getClassName(); if (className != null) { String methodName = testOperationDescriptor.getMethodName(); searchForTestSource(className, methodName, monitor); } } } private void searchForTestSource(String className, String methodName, IProgressMonitor monitor) { monitor.setTaskName(String.format("Open test source file for class %s.", className)); try { // search for Java file SearchEngine searchEngine = new SearchEngine(); SearchPattern pattern = SearchPattern.createPattern(className, IJavaSearchConstants.TYPE, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH); ShowTestSourceFileSearchRequester requester = new ShowTestSourceFileSearchRequester(methodName); searchEngine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, SearchEngine.createWorkspaceScope(), requester, monitor); // if no Java file has been found, search for Groovy file if (!requester.isFoundJavaTestSourceFile()) { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); workspaceRoot.accept( new ShowTestSourceFileResourceVisitor(methodName, className, ImmutableList.of("groovy"))); //$NON-NLS-1$ } } catch (CoreException e) { UiPlugin.logger().error(e.getMessage(), e); } finally { monitor.done(); } } /** * Match the type and potentially also the method name. */ private final class ShowTestSourceFileSearchRequester extends SearchRequestor { private final String methodName; private final AtomicBoolean foundJavaTestSourceFile; private ShowTestSourceFileSearchRequester(String methodName) { this.methodName = methodName; this.foundJavaTestSourceFile = new AtomicBoolean(false); } @Override public void acceptSearchMatch(SearchMatch match) throws CoreException { if (match.getElement() instanceof IType) { this.foundJavaTestSourceFile.set(true); IType classElement = (IType) match.getElement(); IJavaElement methodElement = findMethod(this.methodName, classElement); openInEditor(methodElement != null ? methodElement : classElement); } } private IJavaElement findMethod(String methodName, IType type) { // abort search for invalid method names IStatus status = JavaConventionsUtil.validateMethodName(methodName, type); if (!status.isOK()) { return null; } // find parameter-less method by name IMethod method = type.getMethod(methodName, new String[0]); if (method != null && method.exists()) { return method; } // search textually by name (for custom runner with test methods having parameters) try { for (IMethod methodItem : type.getMethods()) { if (methodItem.getElementName().equals(methodName)) { return methodItem; } } return null; } catch (JavaModelException e) { // ignore and treat as no method being found return null; } } private void openInEditor(final IJavaElement javaElement) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { try { JavaUI.openInEditor(javaElement); } catch (Exception e) { String message = String.format("Cannot open Java element %s in editor.", javaElement); UiPlugin.logger().error(message, e); } } }); } private boolean isFoundJavaTestSourceFile() { return this.foundJavaTestSourceFile.get(); } } /** * Find the file for the given class name as a resource in the workspace. */ private static final class ShowTestSourceFileResourceVisitor implements IResourceVisitor { private static final String BIN_FOLDER_NAME = "bin"; //$NON-NLS-1$ private final String methodName; private final String className; private final ImmutableList<String> fileExtensions; private ShowTestSourceFileResourceVisitor(String methodName, String className, List<String> fileExtensions) { this.methodName = methodName; this.className = Preconditions.checkNotNull(className); this.fileExtensions = ImmutableList.copyOf(fileExtensions); } @Override public boolean visit(final IResource resource) throws CoreException { // short-circuit if the resource is not a file with one of the requested extensions if (resource.getType() != IResource.FILE || !this.fileExtensions.contains(resource.getFileExtension())) { return true; } // prepare to compare package path of the requested class name with the project path of // the given resource String classNameToPath = this.className.replaceAll(Pattern.quote("."), "/"); //$NON-NLS-1$ //$NON-NLS-2$ String projectRelativePath = resource.getProjectRelativePath().toString(); // short-circuit if the resource is in the bin folder or if the paths do not match if (projectRelativePath.startsWith(BIN_FOLDER_NAME) || !projectRelativePath.contains(classNameToPath)) { return true; } // short-circuit if the resource does not map to a file @SuppressWarnings({ "cast", "RedundantCast" }) final IFile file = (IFile) resource.getAdapter(IFile.class); if (file == null) { return true; } // open the requested class and optionally mark the requested method Display display = PlatformUI.getWorkbench().getDisplay(); display.asyncExec(new Runnable() { @Override public void run() { IEditorPart editor = EditorUtils.openInInternalEditor(file, true); IRegion region = getClassOrMethodRegion(file); if (region != null) { EditorUtils.selectAndReveal(region.getOffset(), region.getLength(), editor, file); } } }); return false; } private org.eclipse.jface.text.IRegion getClassOrMethodRegion(IFile file) { // if no method name is available find the class name if (this.methodName == null) { try { FindReplaceDocumentAdapter documentAdapter = createFindReplaceDocumentAdapter(file); return find(documentAdapter, Files.getNameWithoutExtension(file.getName())); } catch (Exception e) { // ignore and treat as no method being found return null; } } // try to find method name and fall back to class name if method name cannot be found try { FindReplaceDocumentAdapter documentAdapter = createFindReplaceDocumentAdapter(file); IRegion region = find(documentAdapter, this.methodName); if (region == null) { documentAdapter = createFindReplaceDocumentAdapter(file); return find(documentAdapter, Files.getNameWithoutExtension(file.getName())); } return region; } catch (Exception e) { // ignore and treat as no method being found return null; } } private FindReplaceDocumentAdapter createFindReplaceDocumentAdapter(IFile file) throws CoreException { TextFileDocumentProvider textFileDocumentProvider = new TextFileDocumentProvider(); textFileDocumentProvider.connect(file); IDocument document = textFileDocumentProvider.getDocument(file); return new FindReplaceDocumentAdapter(document); } private IRegion find(FindReplaceDocumentAdapter findReplaceDocumentAdapter, String findString) throws BadLocationException { return findReplaceDocumentAdapter.find(0, findString, true, true, false, false); } } }