blob: 48b5f4db0558236e78af049851d667bdad0be37e [file] [log] [blame]
/*
* 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"));
}
}