blob: 8b0628b20e510a7910edbe2b18c94c18cf0f30d0 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* 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 org.jetbrains.android.inspections.lint;
import com.android.sdklib.SdkVersionInfo;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.intention.AddAnnotationFix;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NotNull;
import java.util.Locale;
import static com.android.SdkConstants.*;
/** Fix which adds a {@code @TargetApi} annotation at the nearest surrounding method or class */
class AddTargetApiQuickFix implements AndroidLintQuickFix {
private int myApi;
private PsiElement myElement;
AddTargetApiQuickFix(int api, PsiElement element) {
myApi = api;
myElement = element;
}
private String getAnnotationValue(boolean fullyQualified) {
return AddTargetVersionCheckQuickFix.getVersionField(myApi, fullyQualified);
}
@NotNull
@Override
public String getName() {
String key = getAnnotationValue(false);
final PsiFile file = PsiTreeUtil.getParentOfType(myElement, PsiFile.class);
if (file instanceof XmlFile) {
// The quickfixes are sorted alphabetically, but for resources we really don't want
// this quickfix (Add Target API) to appear before Override Resource, which is
// usually the better solution. So instead of "Add tools:targetApi" we use a label
// which sorts later alphabetically.
return "Suppress With tools:targetApi Attribute";
}
return AndroidBundle.message("android.lint.fix.add.target.api", key);
}
@Override
public boolean isApplicable(@NotNull PsiElement startElement,
@NotNull PsiElement endElement,
@NotNull AndroidQuickfixContexts.ContextType contextType) {
return PsiTreeUtil.getParentOfType(startElement, PsiModifierListOwner.class, false) != null
|| PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false) != null;
}
@SuppressWarnings("unchecked")
@Override
public void apply(@NotNull PsiElement startElement, @NotNull PsiElement endElement, @NotNull AndroidQuickfixContexts.Context context) {
// Find nearest method or class; can't add @TargetApi on modifier list owners like variable declarations
@SuppressWarnings("unchecked")
PsiModifierListOwner container = PsiTreeUtil.getParentOfType(startElement, PsiMethod.class, PsiClass.class);
if (container == null) {
// XML file? Set attribute
XmlTag element = PsiTreeUtil.getParentOfType(startElement, XmlTag.class, false);
if (element != null) {
XmlFile file = PsiTreeUtil.getParentOfType(element, XmlFile.class, false);
if (file != null) {
SuppressLintIntentionAction.ensureNamespaceImported(element.getProject(), file, TOOLS_URI);
String codeName = SdkVersionInfo.getBuildCode(myApi);
if (codeName == null) {
codeName = Integer.toString(myApi);
} else {
codeName = codeName.toLowerCase(Locale.US);
}
element.setAttribute(ATTR_TARGET_API, TOOLS_URI, codeName);
}
}
return;
}
while (container != null && container instanceof PsiAnonymousClass) {
container = PsiTreeUtil.getParentOfType(container, PsiMethod.class, true, PsiClass.class);
}
if (container == null) {
return;
}
if (!FileModificationService.getInstance().preparePsiElementForWrite(container)) {
return;
}
final PsiModifierList modifierList = container.getModifierList();
if (modifierList != null) {
Project project = startElement.getProject();
PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
String annotationText = "@" + FQCN_TARGET_API + "(" + getAnnotationValue(true) + ")";
PsiAnnotation newAnnotation = elementFactory.createAnnotationFromText(annotationText, container);
PsiAnnotation annotation = AnnotationUtil.findAnnotation(container, FQCN_TARGET_API);
if (annotation != null && annotation.isPhysical()) {
annotation.replace(newAnnotation);
} else {
PsiNameValuePair[] attributes = newAnnotation.getParameterList().getAttributes();
AddAnnotationFix fix = new AddAnnotationFix(FQCN_TARGET_API, container, attributes);
fix.invoke(project, null /*editor*/, container.getContainingFile());
}
}
}
}