create annotation method from usage (IDEA-75910 )

This commit is contained in:
anna
2011-10-25 18:15:40 +02:00
parent 78e0fb21bb
commit ed24c9b396
11 changed files with 246 additions and 5 deletions

View File

@@ -19,6 +19,7 @@ import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.quickfix.CreateAnnotationMethodFromUsageFix;
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.diagnostic.Logger;
@@ -51,8 +52,10 @@ public class AnnotationsHighlightUtil {
PsiMethod method = (PsiMethod)ref.resolve();
if (method == null) {
if (pair.getName() != null) {
String description = JavaErrorMessages.message("annotation.unknown.method", ref.getCanonicalText());
return HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, ref.getElement(), description);
final String description = JavaErrorMessages.message("annotation.unknown.method", ref.getCanonicalText());
final HighlightInfo highlightInfo = HighlightInfo.createHighlightInfo(HighlightInfoType.WRONG_REF, ref.getElement(), description);
QuickFixAction.registerQuickFixAction(highlightInfo, new CreateAnnotationMethodFromUsageFix(pair));
return highlightInfo;
}
else {
String description = JavaErrorMessages.message("annotation.missing.method", ref.getCanonicalText());
@@ -334,8 +337,8 @@ public class AnnotationsHighlightUtil {
return null;
}
private static class AnnotationReturnTypeVisitor extends PsiTypeVisitor<Boolean> {
private static final AnnotationReturnTypeVisitor INSTANCE = new AnnotationReturnTypeVisitor();
public static class AnnotationReturnTypeVisitor extends PsiTypeVisitor<Boolean> {
public static final AnnotationReturnTypeVisitor INSTANCE = new AnnotationReturnTypeVisitor();
public Boolean visitType(PsiType type) {
return Boolean.FALSE;
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2000-2011 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.
*/
package com.intellij.codeInsight.daemon.impl.quickfix;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypesProvider;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.analysis.AnnotationsHighlightUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class CreateAnnotationMethodFromUsageFix extends CreateFromUsageBaseFix {
private static final Logger LOG = Logger.getInstance("#" + CreateAnnotationMethodFromUsageFix.class.getName());
private final SmartPsiElementPointer<PsiNameValuePair> myNameValuePair;
public CreateAnnotationMethodFromUsageFix(PsiNameValuePair valuePair) {
myNameValuePair = SmartPointerManager.getInstance(valuePair.getProject()).createSmartPsiElementPointer(valuePair);
}
protected boolean isAvailableImpl(int offset) {
final PsiNameValuePair call = getNameValuePair();
if (call == null || !call.isValid()) return false;
String name = call.getName();
if (name == null || !JavaPsiFacade.getInstance(call.getProject()).getNameHelper().isIdentifier(name)) return false;
if (getAnnotationValueType(call.getValue()) == null) return false;
setText(QuickFixBundle.message("create.method.from.usage.text", name));
return true;
}
protected PsiElement getElement() {
final PsiNameValuePair call = getNameValuePair();
if (call == null || !call.getManager().isInProject(call)) return null;
return call;
}
protected void invokeImpl(final PsiClass targetClass) {
if (targetClass == null) return;
PsiNameValuePair nameValuePair = getNameValuePair();
if (nameValuePair == null || isValidElement(nameValuePair)) return;
final PsiElementFactory factory = JavaPsiFacade.getInstance(nameValuePair.getProject()).getElementFactory();
final String methodName = nameValuePair.getName();
LOG.assertTrue(methodName != null);
PsiMethod method = factory.createMethod(methodName, PsiType.VOID);
method = (PsiMethod)targetClass.add(method);
PsiCodeBlock body = method.getBody();
assert body != null;
body.delete();
final PsiElement context = PsiTreeUtil.getParentOfType(nameValuePair, PsiClass.class, PsiMethod.class);
final PsiType type = getAnnotationValueType(nameValuePair.getValue());
LOG.assertTrue(type != null);
final ExpectedTypeInfo[] expectedTypes =
new ExpectedTypeInfo[]{ExpectedTypesProvider.createInfo(type, ExpectedTypeInfo.TYPE_OR_SUBTYPE, type, TailType.NONE)};
CreateMethodFromUsageFix.doCreate(targetClass, method, true, ContainerUtil.map2List(PsiExpression.EMPTY_ARRAY, Pair.<PsiExpression, PsiType>createFunction(null)),
getTargetSubstitutor(nameValuePair), expectedTypes, context);
}
@Nullable
private static PsiType getAnnotationValueType(PsiAnnotationMemberValue value) {
PsiType type = null;
if (value instanceof PsiExpression) {
type = ((PsiExpression)value).getType();
} else if (value instanceof PsiArrayInitializerMemberValue) {
final PsiAnnotationMemberValue[] initializers = ((PsiArrayInitializerMemberValue)value).getInitializers();
PsiType currentType = null;
for (PsiAnnotationMemberValue initializer : initializers) {
if (initializer instanceof PsiArrayInitializerMemberValue) return null;
if (!(initializer instanceof PsiExpression)) return null;
final PsiType psiType = ((PsiExpression)initializer).getType();
if (psiType != null) {
if (currentType == null) {
currentType = psiType;
} else {
if (!TypeConversionUtil.isAssignable(currentType, psiType)) {
if (TypeConversionUtil.isAssignable(psiType, currentType)) {
currentType = psiType;
} else {
return null;
}
}
}
}
}
if (currentType != null) {
type = currentType.createArrayType();
}
}
if (type != null && type.accept(AnnotationsHighlightUtil.AnnotationReturnTypeVisitor.INSTANCE).booleanValue()) {
return type;
}
return null;
}
protected boolean isValidElement(PsiElement element) {
final PsiReference reference = element.getReference();
return reference != null && reference.resolve() != null;
}
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("create.method.from.usage.family");
}
@Nullable
protected PsiNameValuePair getNameValuePair() {
return myNameValuePair.getElement();
}
}

View File

@@ -249,6 +249,24 @@ public abstract class CreateFromUsageBaseFix extends BaseIntentionAction {
PsiClass psiClass = null;
PsiExpression qualifier = null;
if (element instanceof PsiNameValuePair) {
final PsiAnnotation annotation = PsiTreeUtil.getParentOfType(element, PsiAnnotation.class);
if (annotation != null) {
PsiJavaCodeReferenceElement nameRef = annotation.getNameReferenceElement();
if (nameRef == null) {
return Collections.emptyList();
}
else {
final PsiElement resolve = nameRef.resolve();
if (resolve instanceof PsiClass) {
return Collections.singletonList((PsiClass)resolve);
}
else {
return Collections.emptyList();
}
}
}
}
if (element instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)element;
PsiJavaCodeReferenceElement ref = newExpression.getClassOrAnonymousClassReference();

View File

@@ -196,7 +196,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix {
doCreate(targetClass, method, shouldBeAbstractImpl(targetClass), arguments, substitutor, expectedTypes, context);
}
private static void doCreate(PsiClass targetClass,
public static void doCreate(PsiClass targetClass,
PsiMethod method,
boolean shouldBeAbstract,
List<Pair<PsiExpression, PsiType>> arguments,

View File

@@ -0,0 +1,10 @@
// "Create Method 'test'" "true"
public class Test {
@Attr(test= "")
public Test() {
}
}
@interface Attr {
String test();
}

View File

@@ -0,0 +1,10 @@
// "Create Method 'test'" "true"
public class Test {
@Attr(test= {""})
public Test() {
}
}
@interface Attr {
String[] test();
}

View File

@@ -0,0 +1,9 @@
// "Create Method 'test'" "true"
public class Test {
@Attr(te<caret>st= "")
public Test() {
}
}
@interface Attr {
}

View File

@@ -0,0 +1,9 @@
// "Create Method 'test'" "true"
public class Test {
@Attr(te<caret>st= {""})
public Test() {
}
}
@interface Attr {
}

View File

@@ -0,0 +1,9 @@
// "Create Method 'test'" "false"
public class Test {
@Attr(te<caret>st= {"", 1})
public Test() {
}
}
@interface Attr {
}

View File

@@ -0,0 +1,9 @@
// "Create Method 'test'" "false"
public class Test {
@Attr(te<caret>st= {new String[]{""}})
public Test() {
}
}
@interface Attr {
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2000-2011 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.
*/
package com.intellij.codeInsight.daemon.quickFix;
public class CreateAnnotationMethodFromUsageTest extends LightQuickFix15TestCase {
public void test() throws Exception { doAllTests(); }
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/createAnnotationMethodFromUsage";
}
}