Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / plugins / testng / src / com / theoryinpractice / testng / inspection / UndeclaredTestInspection.java
blob974f4dd07a3f3635286f025d31a729d81e93f266
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * User: anna
19 * Date: 18-Jun-2007
21 package com.theoryinpractice.testng.inspection;
23 import com.intellij.codeInspection.*;
24 import com.intellij.openapi.application.Result;
25 import com.intellij.openapi.command.WriteCommandAction;
26 import com.intellij.openapi.diagnostic.Logger;
27 import com.intellij.openapi.fileChooser.FileChooser;
28 import com.intellij.openapi.fileChooser.FileChooserDescriptor;
29 import com.intellij.openapi.module.Module;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.vfs.LocalFileSystem;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.psi.*;
34 import com.intellij.psi.search.GlobalSearchScope;
35 import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor;
36 import com.intellij.psi.util.PsiClassUtil;
37 import com.intellij.psi.util.PsiTreeUtil;
38 import com.intellij.psi.xml.XmlFile;
39 import com.intellij.psi.xml.XmlTag;
40 import com.intellij.util.IncorrectOperationException;
41 import com.theoryinpractice.testng.configuration.browser.SuiteBrowser;
42 import com.theoryinpractice.testng.util.TestNGUtil;
43 import org.jetbrains.annotations.Nls;
44 import org.jetbrains.annotations.NonNls;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import javax.swing.*;
50 public class UndeclaredTestInspection extends BaseJavaLocalInspectionTool {
51 private static final Logger LOG = Logger.getInstance("#" + UndeclaredTestInspection.class.getName());
53 @Nls
54 @NotNull
55 public String getGroupDisplayName() {
56 return TestNGUtil.TESTNG_GROUP_NAME;
59 @Nls
60 @NotNull
61 public String getDisplayName() {
62 return "Undeclared test";
65 @NonNls
66 @NotNull
67 public String getShortName() {
68 return "UndeclaredTests";
71 @Nullable
72 public ProblemDescriptor[] checkClass(@NotNull final PsiClass aClass,
73 @NotNull final InspectionManager manager,
74 final boolean isOnTheFly) {
75 if (TestNGUtil.hasTest(aClass) && PsiClassUtil.isRunnableClass(aClass, true)) {
76 final Project project = aClass.getProject();
77 final String qName = aClass.getQualifiedName();
78 if (qName == null) return null;
79 final String packageName = ((PsiJavaFile)aClass.getContainingFile()).getPackageName();
80 final String[] names = new String[]{qName, packageName};
81 for (final String name : names) {
82 final boolean[] found = new boolean[]{false};
83 PsiManager.getInstance(project).getSearchHelper()
84 .processUsagesInNonJavaFiles(name, new PsiNonJavaFileReferenceProcessor() {
85 public boolean process(final PsiFile file, final int startOffset, final int endOffset) {
86 if (file.findReferenceAt(startOffset) != null) {
87 if (name.equals(packageName)) { //special package tag required
88 final XmlTag tag = PsiTreeUtil.getParentOfType(file.findElementAt(startOffset), XmlTag.class);
89 if (tag == null || !tag.getName().equals("package")){
90 return true;
93 found[0] = true;
94 return false;
96 return true;
98 }, new TestNGSearchScope());
99 if (found[0]) return null;
101 final PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
102 LOG.assertTrue(nameIdentifier != null);
103 return new ProblemDescriptor[]{manager.createProblemDescriptor(nameIdentifier, "Undeclared test \'" + aClass.getName() + "\'",
104 new LocalQuickFix[]{new RegisterClassFix(aClass),
105 new CreateTestngFix()},
106 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, isOnTheFly)};
108 return null;
111 private static class RegisterClassFix implements LocalQuickFix {
112 private final PsiClass myClass;
114 public RegisterClassFix(final PsiClass aClass) {
115 myClass = aClass;
118 @NotNull
119 public String getName() {
120 return "Register \'" + myClass.getName() + "\'";
123 @NotNull
124 public String getFamilyName() {
125 return getName();
128 public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) {
129 final PsiClass psiClass = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiClass.class);
130 LOG.assertTrue(psiClass != null);
131 SwingUtilities.invokeLater(new Runnable() { //need to show dialog
133 public void run() {
134 final String testngXmlPath = new SuiteBrowser(project).showDialog();
135 if (testngXmlPath == null) return;
136 final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(testngXmlPath);
137 LOG.assertTrue(virtualFile != null);
138 final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
139 LOG.assertTrue(psiFile instanceof XmlFile);
140 final XmlFile testngXML = (XmlFile)psiFile;
141 new WriteCommandAction(project, getName(), testngXML) {
142 protected void run(final Result result) throws Throwable {
143 patchTestngXml(testngXML, psiClass);
145 }.execute();
151 //make public for tests only
152 public static void patchTestngXml(final XmlFile testngXML, final PsiClass psiClass) {
153 final XmlTag rootTag = testngXML.getDocument().getRootTag();
154 if (rootTag != null && rootTag.getName().equals("suite")) {
155 try {
156 XmlTag testTag = rootTag.findFirstSubTag("test");
157 if (testTag == null) {
158 testTag = (XmlTag)rootTag.add(rootTag.createChildTag("test", rootTag.getNamespace(), null, false));
159 testTag.setAttribute("name", psiClass.getName());
161 XmlTag classesTag = testTag.findFirstSubTag("classes");
162 if (classesTag == null) {
163 classesTag = (XmlTag)testTag.add(testTag.createChildTag("classes", testTag.getNamespace(), null, false));
165 final XmlTag classTag = (XmlTag)classesTag.add(classesTag.createChildTag("class", classesTag.getNamespace(), null, false));
166 final String qualifiedName = psiClass.getQualifiedName();
167 LOG.assertTrue(qualifiedName != null);
168 classTag.setAttribute("name", qualifiedName);
170 catch (IncorrectOperationException e) {
171 LOG.error(e);
176 private static class CreateTestngFix implements LocalQuickFix {
177 @NotNull
178 public String getName() {
179 return "Create suite";
182 @NotNull
183 public String getFamilyName() {
184 return getName();
187 public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
188 final PsiClass psiClass = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiClass.class);
189 SwingUtilities.invokeLater(new Runnable() {
190 public void run() {
191 final VirtualFile[] files = FileChooser.chooseFiles(project, new FileChooserDescriptor(false, true, false, false, false, false));
192 if (files.length == 1) {
193 final PsiManager psiManager = PsiManager.getInstance(project);
194 final PsiDirectory directory = psiManager.findDirectory(files[0]);
195 LOG.assertTrue(directory != null);
196 new WriteCommandAction(project, getName(), null) {
197 protected void run(final Result result) throws Throwable {
198 XmlFile testngXml = (XmlFile)PsiFileFactory.getInstance(psiManager.getProject())
199 .createFileFromText("testng.xml", "<!DOCTYPE suite SYSTEM \"http://testng.org/testng-1.0.dtd\">\n<suite></suite>");
200 try {
201 testngXml = (XmlFile)directory.add(testngXml);
203 catch (IncorrectOperationException e) {
204 //todo suggest new name
205 return;
207 patchTestngXml(testngXml, psiClass);
209 }.execute();
216 private static class TestNGSearchScope extends GlobalSearchScope {
217 public boolean contains(VirtualFile file) {
218 return TestNGUtil.isTestngXML(file);
221 public int compare(VirtualFile file1, VirtualFile file2) {
222 return 0;
225 public boolean isSearchInModuleContent(@NotNull Module aModule) {
226 return true;
229 public boolean isSearchInLibraries() {
230 return false;