blob: 74bb548fcbbf5ff968d3c1bdad2621dd08cb44fc [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed 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.
*/
/*
* Created by IntelliJ IDEA.
* User: amrk
* Date: 11/11/2006
* Time: 16:15:10
*/
package com.theoryinpractice.testng;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.lookup.LookupValueFactory;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.position.FilterPattern;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import com.theoryinpractice.testng.inspection.DependsOnGroupsInspection;
import com.theoryinpractice.testng.util.TestNGUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.testng.annotations.DataProvider;
import java.util.ArrayList;
import java.util.List;
public class TestNGReferenceContributor extends PsiReferenceContributor {
private static PsiElementPattern.Capture<PsiLiteral> getElementPattern(String annotation) {
return PlatformPatterns.psiElement(PsiLiteral.class).and(new FilterPattern(new TestAnnotationFilter(annotation)));
}
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(getElementPattern("dependsOnMethods"), new PsiReferenceProvider() {
@NotNull
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) {
return new MethodReference[]{new MethodReference((PsiLiteral)element)};
}
});
registrar.registerReferenceProvider(getElementPattern("dataProvider"), new PsiReferenceProvider() {
@NotNull
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) {
return new DataProviderReference[]{new DataProviderReference((PsiLiteral)element)};
}
});
registrar.registerReferenceProvider(getElementPattern("groups"), new PsiReferenceProvider() {
@NotNull
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) {
return new GroupReference[]{new GroupReference(element.getProject(), (PsiLiteral)element)};
}
});
registrar.registerReferenceProvider(getElementPattern("dependsOnGroups"), new PsiReferenceProvider() {
@NotNull
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull final ProcessingContext context) {
return new GroupReference[]{new GroupReference(element.getProject(), (PsiLiteral)element)};
}
});
}
private static class DataProviderReference extends PsiReferenceBase<PsiLiteral> {
public DataProviderReference(PsiLiteral element) {
super(element, false);
}
@Nullable
public PsiElement resolve() {
final PsiClass cls = getProviderClass(PsiUtil.getTopLevelClass(getElement()));
if (cls != null) {
PsiMethod[] methods = cls.getAllMethods();
@NonNls String val = getValue();
for (PsiMethod method : methods) {
PsiAnnotation dataProviderAnnotation = AnnotationUtil.findAnnotation(method, DataProvider.class.getName());
if (dataProviderAnnotation != null) {
final PsiAnnotationMemberValue dataProviderMethodName = dataProviderAnnotation.findDeclaredAttributeValue("name");
if (dataProviderMethodName != null && val.equals(StringUtil.unquoteString(dataProviderMethodName.getText()))) {
return method;
}
if (val.equals(method.getName())) {
return method;
}
}
}
}
return null;
}
@NotNull
public Object[] getVariants() {
final List<Object> list = new ArrayList<Object>();
final PsiClass topLevelClass = PsiUtil.getTopLevelClass(getElement());
final PsiClass cls = getProviderClass(topLevelClass);
final boolean needToBeStatic = cls != topLevelClass;
if (cls != null) {
final PsiMethod current = PsiTreeUtil.getParentOfType(getElement(), PsiMethod.class);
final PsiMethod[] methods = cls.getAllMethods();
for (PsiMethod method : methods) {
if (current != null && method.getName().equals(current.getName())) continue;
if (needToBeStatic) {
if (!method.hasModifierProperty(PsiModifier.STATIC)) continue;
} else {
if (cls != method.getContainingClass() && method.hasModifierProperty(PsiModifier.PRIVATE)) continue;
}
final PsiAnnotation dataProviderAnnotation = AnnotationUtil.findAnnotation(method, DataProvider.class.getName());
if (dataProviderAnnotation != null) {
final PsiAnnotationMemberValue memberValue = dataProviderAnnotation.findDeclaredAttributeValue("name");
if (memberValue != null) {
list.add(LookupValueFactory.createLookupValue(StringUtil.unquoteString(memberValue.getText()), null));
} else {
list.add(LookupValueFactory.createLookupValue(method.getName(), null));
}
}
}
}
return list.toArray();
}
private PsiClass getProviderClass(final PsiClass topLevelClass) {
final PsiAnnotation annotation = PsiTreeUtil.getParentOfType(getElement(), PsiAnnotation.class);
if (annotation != null) {
final PsiAnnotationMemberValue value = annotation.findDeclaredAttributeValue("dataProviderClass");
if (value instanceof PsiClassObjectAccessExpression) {
final PsiTypeElement operand = ((PsiClassObjectAccessExpression)value).getOperand();
final PsiClass psiClass = PsiUtil.resolveClassInType(operand.getType());
if (psiClass != null) {
return psiClass;
}
}
}
return topLevelClass;
}
}
private static class MethodReference extends PsiReferenceBase<PsiLiteral> {
public MethodReference(PsiLiteral element) {
super(element, false);
}
@Nullable
public PsiElement resolve() {
@NonNls String val = getValue();
final String methodName = StringUtil.getShortName(val);
PsiClass cls = getDependsClass(val);
if (cls != null) {
PsiMethod[] methods = cls.findMethodsByName(methodName, true);
for (PsiMethod method : methods) {
if (TestNGUtil.hasTest(method, false) || TestNGUtil.hasConfig(method)) {
return method;
}
}
}
return null;
}
@Nullable
private PsiClass getDependsClass(String val) {
final String className = StringUtil.getPackageName(val);
final PsiLiteral element = getElement();
return StringUtil.isEmpty(className) ? PsiUtil.getTopLevelClass(element)
: JavaPsiFacade.getInstance(element.getProject()).findClass(className, element.getResolveScope());
}
@NotNull
public Object[] getVariants() {
List<Object> list = new ArrayList<Object>();
@NonNls String val = getValue();
int hackIndex = val.indexOf(CompletionUtil.DUMMY_IDENTIFIER);
if (hackIndex > -1) {
val = val.substring(0, hackIndex);
}
final String className = StringUtil.getPackageName(val);
PsiClass cls = getDependsClass(val);
if (cls != null) {
final PsiMethod current = PsiTreeUtil.getParentOfType(getElement(), PsiMethod.class);
final String configAnnotation = TestNGUtil.getConfigAnnotation(current);
final PsiMethod[] methods = cls.getMethods();
for (PsiMethod method : methods) {
final String methodName = method.getName();
if (current != null && methodName.equals(current.getName())) continue;
if (configAnnotation == null && TestNGUtil.hasTest(method) || configAnnotation != null && AnnotationUtil.isAnnotated(method, configAnnotation, true)) {
final String nameToInsert = StringUtil.isEmpty(className) ? methodName : StringUtil.getQualifiedName(cls.getQualifiedName(), methodName);
list.add(LookupElementBuilder.create(nameToInsert));
}
}
}
return list.toArray();
}
}
private static class GroupReference extends PsiReferenceBase<PsiLiteral> {
private final Project myProject;
public GroupReference(Project project, PsiLiteral element) {
super(element, false);
myProject = project;
}
@Nullable
public PsiElement resolve() {
return null;
}
@NotNull
public Object[] getVariants() {
List<Object> list = new ArrayList<Object>();
InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile();
DependsOnGroupsInspection inspection = (DependsOnGroupsInspection)inspectionProfile.getUnwrappedTool(
DependsOnGroupsInspection.SHORT_NAME, myElement);
for (String groupName : inspection.groups) {
list.add(LookupValueFactory.createLookupValue(groupName, null));
}
if (!list.isEmpty()) {
return list.toArray();
}
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
}
private static class TestAnnotationFilter implements ElementFilter {
private final String myParameterName;
public TestAnnotationFilter(@NotNull @NonNls String parameterName) {
myParameterName = parameterName;
}
public boolean isAcceptable(Object element, PsiElement context) {
PsiNameValuePair pair = PsiTreeUtil.getParentOfType(context, PsiNameValuePair.class);
if (null == pair) return false;
if (!myParameterName.equals(pair.getName())) return false;
PsiAnnotation annotation = PsiTreeUtil.getParentOfType(pair, PsiAnnotation.class);
if (annotation == null) return false;
if (!TestNGUtil.isTestNGAnnotation(annotation)) return false;
return true;
}
public boolean isClassAcceptable(Class hintClass) {
return PsiLiteral.class.isAssignableFrom(hintClass);
}
}
}