blob: 69d3cfcd9506890e4c0208520644b539dd8fa069 [file] [log] [blame]
package org.jetbrains.android;
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.android.dom.converters.OnClickConverter;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.android.SdkConstants.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidImplicitUsagesProvider implements ImplicitUsageProvider {
@Override
public boolean isImplicitUsage(PsiElement element) {
if (element instanceof PsiField) {
return isImplicitFieldUsage((PsiField)element);
}
else if (element instanceof PsiParameter) {
return isImplicitParameterUsage((PsiParameter)element);
}
else if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod)element;
return method.isConstructor() && isImplicitConstructorUsage(method);
}
return false;
}
private static boolean isImplicitParameterUsage(@NotNull PsiParameter parameter) {
if (AndroidFacet.getInstance(parameter) == null) {
return false;
}
final PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class);
if (method == null ||
!OnClickConverter.CONVERTER_FOR_LAYOUT.checkSignature(method) &&
!OnClickConverter.CONVERTER_FOR_MENU.checkSignature(method)) {
return false;
}
final PsiClass aClass = PsiTreeUtil.getParentOfType(method, PsiClass.class);
if (aClass == null) {
return false;
}
final PsiClass activityBaseClass = JavaPsiFacade.getInstance(aClass.getProject()).
findClass(AndroidUtils.ACTIVITY_BASE_CLASS_NAME, parameter.getResolveScope());
if (activityBaseClass == null) {
return false;
}
return aClass.isInheritor(activityBaseClass, true);
}
private static boolean isImplicitFieldUsage(@NotNull PsiField field) {
if (!"CREATOR".equals(field.getName())) {
return false;
}
final PsiModifierList modifierList = field.getModifierList();
if (modifierList == null || !modifierList.hasModifierProperty(PsiModifier.STATIC)) {
return false;
}
final PsiClass aClass = field.getContainingClass();
return aClass != null && InheritanceUtil.isInheritor(aClass, "android.os.Parcelable");
}
@Override
public boolean isImplicitRead(PsiElement element) {
return false;
}
@Override
public boolean isImplicitWrite(PsiElement element) {
if (!(element instanceof PsiField)) {
return false;
}
final AndroidFacet facet = AndroidFacet.getInstance(element);
if (facet == null) {
return false;
}
final PsiField field = (PsiField)element;
final PsiModifierList modifierList = field.getModifierList();
if (modifierList == null) {
return false;
}
for (PsiAnnotation annotation : modifierList.getAnnotations()) {
for (PsiNameValuePair pair : annotation.getParameterList().getAttributes()) {
final PsiAnnotationMemberValue value = pair.getValue();
if (isResourceReference(value)) {
return true;
}
}
}
return false;
}
private static boolean isResourceReference(@Nullable PsiAnnotationMemberValue value) {
if (!(value instanceof PsiReferenceExpression)) {
return false;
}
PsiReferenceExpression exp = (PsiReferenceExpression)value;
String refName = exp.getReferenceName();
if (refName == null || refName.length() == 0) {
return false;
}
PsiExpression qExp = exp.getQualifierExpression();
if (!(qExp instanceof PsiReferenceExpression)) {
return false;
}
exp = (PsiReferenceExpression)qExp;
refName = exp.getReferenceName();
if (refName == null || refName.length() == 0) {
return false;
}
qExp = exp.getQualifierExpression();
if (!(qExp instanceof PsiReferenceExpression)) {
return false;
}
exp = (PsiReferenceExpression)qExp;
return AndroidUtils.R_CLASS_NAME.equals(exp.getReferenceName());
}
public boolean isImplicitConstructorUsage(PsiMethod method) {
if (!method.isConstructor()) {
return false;
}
if (!method.hasModifierProperty(PsiModifier.PUBLIC)) {
return false;
}
PsiParameterList parameterList = method.getParameterList();
int parameterCount = parameterList.getParametersCount();
if (parameterCount == 0) {
// Some Android classes need default constructors, and are invoked by inflaters
final PsiClass aClass = method.getContainingClass();
if (aClass != null) {
if (InheritanceUtil.isInheritor(aClass, CLASS_FRAGMENT)
|| InheritanceUtil.isInheritor(aClass, CLASS_V4_FRAGMENT)
|| InheritanceUtil.isInheritor(aClass, CLASS_BACKUP_AGENT)) {
// Activity, Service, ContentProvider and BroadcastReceiver should also be treated as having implicit usages,
// but for some reason that's already the case (they are not marked as unused constructors currently;
// perhaps due to the XML DOM bindings?
return true;
}
}
return false;
}
// Look for View constructors; these are of one of these forms:
// View(android.content.Context context)
// View(android.content.Context context, android.util.AttributeSet attrs)
// View(android.content.Context context, android.util.AttributeSet attrs, int defStyle)
// Also check for
// ActionProvider(android.content.Context context)
if (parameterCount < 1 || parameterCount > 3) {
return false;
}
PsiParameter[] parameters = parameterList.getParameters();
PsiType type = parameters[0].getType();
if (!(type instanceof PsiClassReferenceType)) {
return false;
}
PsiClassReferenceType classType = (PsiClassReferenceType)type;
PsiClass resolvedParameter = classType.resolve();
if (resolvedParameter == null || !CLASS_CONTEXT.equals(resolvedParameter.getQualifiedName())) {
return false;
}
if (parameterCount > 1) {
type = parameters[1].getType();
if (!(type instanceof PsiClassReferenceType)) {
return false;
}
classType = (PsiClassReferenceType)type;
resolvedParameter = classType.resolve();
if (resolvedParameter == null || !CLASS_ATTRIBUTE_SET.equals(resolvedParameter.getQualifiedName())) {
return false;
}
if (parameterCount > 2) {
type = parameters[2].getType();
if (!PsiType.INT.equals(type)) {
return false;
}
}
}
final PsiClass aClass = PsiTreeUtil.getParentOfType(method, PsiClass.class);
if (aClass == null) {
return false;
}
PsiClass viewBaseClass = JavaPsiFacade.getInstance(aClass.getProject()).findClass(CLASS_VIEW, method.getResolveScope());
if (viewBaseClass == null) {
return false;
}
return aClass.isInheritor(viewBaseClass, true) || parameterCount == 1 && InheritanceUtil.isInheritor(aClass, CLASS_ACTION_PROVIDER);
}
}