| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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 org.netbeans.modules.junit.ui; |
| |
| import com.sun.source.tree.ClassTree; |
| import com.sun.source.tree.CompilationUnitTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.source.util.SourcePositions; |
| import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner; |
| import com.sun.source.util.Trees; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import javax.lang.model.element.Element; |
| import javax.swing.Action; |
| import javax.swing.JEditorPane; |
| import javax.swing.text.StyledDocument; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.java.queries.UnitTestForSourceQuery; |
| import org.netbeans.api.java.source.CancellableTask; |
| import org.netbeans.api.java.source.CompilationController; |
| import org.netbeans.api.java.source.ElementHandle; |
| import org.netbeans.api.java.source.JavaSource; |
| import org.netbeans.api.java.source.JavaSource.Phase; |
| import org.netbeans.modules.junit.api.JUnitTestUtil; |
| import org.netbeans.spi.java.classpath.PathResourceImplementation; |
| import org.netbeans.spi.java.classpath.support.ClassPathSupport; |
| import org.openide.ErrorManager; |
| import org.openide.cookies.EditorCookie; |
| import org.openide.cookies.LineCookie; |
| import org.openide.cookies.OpenCookie; |
| import org.openide.filesystems.FileObject; |
| import org.openide.loaders.DataObject; |
| import org.openide.loaders.DataObjectNotFoundException; |
| import org.openide.nodes.Node; |
| import org.openide.text.Line; |
| import org.openide.text.Line.ShowOpenType; |
| import org.openide.text.Line.ShowVisibilityType; |
| import org.openide.text.NbDocument; |
| import org.openide.util.HelpCtx; |
| import org.openide.util.NbBundle; |
| |
| /** Action sensitive to some DataFolder or SourceCookie cookie |
| * which tries to open JUnit test corresponding to the selected source file. |
| * |
| * @author Nathan W. Phelps, David Konecny |
| * @author Marian Petras |
| * @ version 1.0 |
| */ |
| @SuppressWarnings("serial") |
| public class OpenTestAction extends TestAction { |
| |
| public OpenTestAction() { |
| putValue("noIconInMenu", Boolean.TRUE); |
| } |
| |
| |
| protected void performAction (Node[] nodes) { |
| FileObject selectedFO; |
| |
| for (int i = 0; i < nodes.length; i++) { |
| // get test class or suite class file, if it was not such one pointed by the node |
| selectedFO = org.netbeans.modules.gsf.testrunner.ui.api.UICommonUtils.getFileObjectFromNode(nodes[i]); |
| if (selectedFO == null) { |
| JUnitTestUtil.notifyUser(NbBundle.getMessage(OpenTestAction.class, "MSG_file_from_node_failed")); |
| continue; |
| } |
| ClassPath cp = ClassPath.getClassPath(selectedFO, ClassPath.SOURCE); |
| if (cp == null) { |
| JUnitTestUtil.notifyUser(NbBundle.getMessage(OpenTestAction.class, |
| "MSG_no_project", selectedFO)); |
| continue; |
| } |
| |
| FileObject packageRoot = cp.findOwnerRoot(selectedFO); |
| URL[] testRoots = UnitTestForSourceQuery.findUnitTests(packageRoot); |
| FileObject fileToOpen = null; |
| for (int j = 0 ; j < testRoots.length; j++) { |
| fileToOpen = findUnitTestInTestRoot(cp, selectedFO, testRoots[j]); |
| if (fileToOpen != null) break; |
| } |
| |
| if (fileToOpen != null) { |
| openFile(fileToOpen); |
| } else { |
| String testClsName = getTestName(cp, selectedFO).replace('/','.'); |
| String pkgName = cp.getResourceName(selectedFO, '.', false); |
| boolean isPackage = selectedFO.isFolder(); |
| boolean isDefPkg = isPackage && (pkgName.length() == 0); |
| String msgPattern = !isPackage |
| ? "MSG_test_class_not_found" //NOI18N |
| : isDefPkg |
| ? "MSG_testsuite_class_not_found_def_pkg" //NOI18N |
| : "MSG_testsuite_class_not_found"; //NOI18N |
| |
| String[] params = isDefPkg ? new String[] { testClsName } |
| : new String[] { testClsName, |
| pkgName }; |
| |
| JUnitTestUtil.notifyUser(NbBundle.getMessage(OpenTestAction.class, |
| msgPattern, params), |
| ErrorManager.INFORMATIONAL); |
| continue; |
| } |
| } |
| } |
| |
| private static FileObject findUnitTestInTestRoot(ClassPath cp, FileObject selectedFO, URL testRoot) { |
| ClassPath testClassPath = null; |
| if (testRoot == null) { //no tests, use sources instead |
| testClassPath = cp; |
| } else { |
| try { |
| List<PathResourceImplementation> cpItems |
| = new ArrayList<PathResourceImplementation>(); |
| cpItems.add(ClassPathSupport.createResource(testRoot)); |
| testClassPath = ClassPathSupport.createClassPath(cpItems); |
| } catch (IllegalArgumentException ex) { |
| ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); |
| testClassPath = cp; |
| } |
| } |
| String testName = getTestName(cp, selectedFO); |
| return testClassPath.findResource(testName+".java"); |
| } |
| |
| private static String getTestName(ClassPath cp, FileObject selectedFO) { |
| String resource = cp.getResourceName(selectedFO, '/', false); |
| String testName = null; |
| if (selectedFO.isFolder()) { |
| //find Suite for package |
| testName = JUnitTestUtil.convertPackage2SuiteName(resource); |
| } else { |
| // find Test for class |
| testName = JUnitTestUtil.convertClass2TestName(resource); |
| } |
| |
| return testName; |
| } |
| |
| /** |
| * Open given file in editor. |
| * @return true if file was opened or false |
| */ |
| public static boolean openFile(FileObject fo) { |
| DataObject dobj; |
| try { |
| dobj = DataObject.find(fo); |
| } catch (DataObjectNotFoundException e) { |
| getLogger().log(Level.WARNING, null, e); |
| return false; |
| } |
| assert dobj != null; |
| |
| EditorCookie editorCookie = dobj.getCookie(EditorCookie.class); |
| if (editorCookie != null) { |
| editorCookie.open(); |
| return true; |
| } |
| |
| OpenCookie openCookie = dobj.getCookie(OpenCookie.class); |
| if (openCookie != null) { |
| openCookie.open(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| */ |
| static boolean openFileAtElement(FileObject fileObject, |
| ElementHandle<Element> element) { |
| final DataObject dataObject; |
| try { |
| dataObject = DataObject.find(fileObject); |
| } catch (DataObjectNotFoundException e) { |
| getLogger().log(Level.INFO, null, e); |
| return false; |
| } |
| assert dataObject != null; |
| |
| final EditorCookie editorCookie = dataObject.getCookie(EditorCookie.class); |
| if (editorCookie != null) { |
| |
| StyledDocument doc; |
| try { |
| doc = editorCookie.openDocument(); |
| } catch (IOException ex) { |
| String msg = ex.getLocalizedMessage(); |
| if (msg == null) { |
| msg = ex.getMessage(); |
| } |
| getLogger().log(Level.SEVERE, msg, ex); |
| return false; |
| } |
| |
| editorCookie.open(); |
| |
| LineCookie lineCookie = dataObject.getCookie(LineCookie.class); |
| if ((lineCookie != null) && (element != null) && (doc != null)) { |
| int currentPos = -1; |
| JEditorPane[] editorPanes = editorCookie.getOpenedPanes(); |
| if ((editorPanes != null) && (editorPanes.length != 0)) { |
| currentPos = editorPanes[0].getCaretPosition(); |
| } |
| int[] elementPositionBounds = null; |
| try { |
| elementPositionBounds = getPositionRange(fileObject, element); |
| } catch (IOException ex) { |
| getLogger().log(Level.WARNING, null, ex); |
| } |
| if ((currentPos == -1) |
| || (elementPositionBounds != null) |
| && ((currentPos < elementPositionBounds[0]) |
| ||(currentPos >= elementPositionBounds[1]))) { |
| int startPos = elementPositionBounds[0]; |
| int lineNum = NbDocument.findLineNumber(doc, startPos); |
| if (lineNum != -1) { |
| Line line = lineCookie.getLineSet().getCurrent(lineNum); |
| try { |
| int lineOffset = NbDocument.findLineOffset(doc, |
| lineNum); |
| int column = startPos - lineOffset; |
| line.show(ShowOpenType.OPEN, ShowVisibilityType.FOCUS, column); |
| } catch (IndexOutOfBoundsException ex) { |
| Logger.getLogger(OpenTestAction.class.getName()) |
| .log(Level.INFO, null, ex); |
| line.show(ShowOpenType.OPEN, ShowVisibilityType.FOCUS); |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| OpenCookie openCookie = dataObject.getCookie(OpenCookie.class); |
| if (openCookie != null) { |
| openCookie.open(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| */ |
| private static Logger getLogger() { |
| return Logger.getLogger(OpenTestAction.class.getName()); |
| } |
| |
| /** |
| */ |
| private static int[] getPositionRange( |
| FileObject fileObj, |
| ElementHandle<Element> elemHandle) throws IOException { |
| PositionRangeFinder posFinder = new PositionRangeFinder(fileObj, elemHandle); |
| JavaSource.forFileObject(fileObj).runUserActionTask(posFinder, true); |
| return posFinder.getPositionRange(); |
| } |
| |
| /** |
| * |
| */ |
| private static class PositionRangeFinder |
| implements CancellableTask<CompilationController> { |
| |
| private final FileObject fileObj; |
| private final ElementHandle<Element> elemHandle; |
| private int[] positionRange = null; |
| private volatile boolean cancelled; |
| |
| /** |
| */ |
| private PositionRangeFinder(FileObject fileObj, |
| ElementHandle<Element> elemHandle) { |
| this.fileObj = fileObj; |
| this.elemHandle = elemHandle; |
| } |
| |
| /** |
| */ |
| public void run(CompilationController controller) throws IOException { |
| try { |
| controller.toPhase(Phase.RESOLVED); //cursor position needed |
| } catch (IOException ex) { |
| Logger.getLogger("global").log(Level.SEVERE, null, ex); //NOI18N |
| } |
| if (cancelled) { |
| return; |
| } |
| |
| Element element = elemHandle.resolve(controller); |
| if (cancelled || (element == null)) { |
| return; |
| } |
| |
| Trees trees = controller.getTrees(); |
| CompilationUnitTree compUnit = controller.getCompilationUnit(); |
| DeclarationTreeFinder treeFinder = new DeclarationTreeFinder( |
| element, trees); |
| treeFinder.scan(compUnit, null); |
| Tree tree = treeFinder.getDeclarationTree(); |
| |
| if (tree != null) { |
| SourcePositions srcPositions = trees.getSourcePositions(); |
| long startPos = srcPositions.getStartPosition(compUnit, tree); |
| long endPos = srcPositions.getEndPosition(compUnit, tree); |
| |
| if ((startPos >= 0) && (startPos <= (long) Integer.MAX_VALUE) |
| && (endPos >= 0) && (endPos <= (long) Integer.MAX_VALUE)) { |
| positionRange = new int[2]; |
| positionRange[0] = (int) startPos; |
| positionRange[1] = (int) endPos; |
| } |
| } |
| } |
| |
| /** |
| */ |
| public void cancel() { |
| cancelled = true; |
| } |
| |
| /** |
| */ |
| int[] getPositionRange() { |
| return positionRange; |
| } |
| |
| } |
| |
| /** |
| * |
| */ |
| private static class DeclarationTreeFinder extends ErrorAwareTreePathScanner<Void, Void> { |
| |
| private final Element element; |
| private final Trees trees; |
| private Tree declTree; |
| |
| /** |
| */ |
| private DeclarationTreeFinder(Element element, Trees trees) { |
| this.element = element; |
| this.trees = trees; |
| } |
| |
| @Override |
| public Void visitClass(ClassTree tree, Void d) { |
| if (declTree == null) { |
| handleDeclaration(); |
| super.visitClass(tree, d); |
| } |
| return null; |
| } |
| |
| @Override |
| public Void visitMethod(MethodTree tree, Void d) { |
| if (declTree == null) { |
| handleDeclaration(); |
| super.visitMethod(tree, d); |
| } |
| return null; |
| } |
| |
| /** |
| */ |
| public void handleDeclaration() { |
| Element found = trees.getElement(getCurrentPath()); |
| |
| if (element.equals(found)) { |
| declTree = getCurrentPath().getLeaf(); |
| } |
| } |
| |
| /** |
| */ |
| Tree getDeclarationTree() { |
| return declTree; |
| } |
| |
| } |
| |
| public String getName () { |
| return NbBundle.getMessage (OpenTestAction.class, "LBL_Action_OpenTest"); |
| } |
| |
| protected String iconResource () { |
| return "org/netbeans/modules/junit/resources/OpenTestActionIcon.gif"; |
| } |
| |
| public HelpCtx getHelpCtx () { |
| return new HelpCtx(OpenTestAction.class); |
| } |
| |
| /** Perform special enablement check in addition to the normal one. |
| protected boolean enable (Node[] nodes) { |
| if (! super.enable (nodes)) return false; |
| if (...) ...; |
| } |
| */ |
| |
| protected void initialize () { |
| super.initialize (); |
| putProperty(Action.SHORT_DESCRIPTION, NbBundle.getMessage(OpenTestAction.class, "HINT_Action_OpenTest")); |
| } |
| |
| } |