java dom stuff -> java-impl
[idea/community.git] / java / java-impl / src / com / intellij / util / xml / impl / ExtendsClassChecker.java
1 /*
2  * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
3  * Use is subject to license terms.
4  */
5 package com.intellij.util.xml.impl;
6
7 import com.intellij.openapi.project.Project;
8 import com.intellij.psi.*;
9 import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference;
10 import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
11 import com.intellij.psi.search.GlobalSearchScope;
12 import com.intellij.psi.util.InheritanceUtil;
13 import com.intellij.util.ReflectionCache;
14 import com.intellij.util.SmartList;
15 import com.intellij.util.ProcessingContext;
16 import com.intellij.util.xml.*;
17 import com.intellij.util.xml.highlighting.DomCustomAnnotationChecker;
18 import com.intellij.util.xml.highlighting.DomElementAnnotationHolder;
19 import com.intellij.util.xml.highlighting.DomElementProblemDescriptor;
20 import com.intellij.util.xml.highlighting.DomHighlightingHelper;
21 import org.jetbrains.annotations.NotNull;
22
23 import java.util.Collections;
24 import java.util.List;
25
26 /**
27  * @author peter
28  */
29 public class ExtendsClassChecker extends DomCustomAnnotationChecker<ExtendClass>{
30   private static final GenericValueReferenceProvider ourProvider = new GenericValueReferenceProvider();
31
32   @NotNull
33   public Class<ExtendClass> getAnnotationClass() {
34     return ExtendClass.class;
35   }
36
37   public List<DomElementProblemDescriptor> checkForProblems(@NotNull final ExtendClass extend, @NotNull final DomElement _element, @NotNull final DomElementAnnotationHolder holder,
38                             @NotNull final DomHighlightingHelper helper) {
39     if (!(_element instanceof GenericDomValue)) return Collections.emptyList();
40     GenericDomValue element = (GenericDomValue)_element;
41
42     final Class genericValueParameter = DomUtil.getGenericValueParameter(element.getDomElementType());
43     if (genericValueParameter == null || (!ReflectionCache.isAssignable(genericValueParameter, PsiClass.class) &&
44                                            !ReflectionCache.isAssignable(genericValueParameter, PsiType.class))) {
45       return Collections.emptyList();
46     }
47
48     final Object valueObject = element.getValue();
49     PsiClass psiClass = null;
50
51     if (valueObject instanceof PsiClass) {
52       psiClass = (PsiClass)valueObject;
53     } else if (valueObject instanceof PsiClassType) {
54       psiClass = ((PsiClassType)valueObject).resolve();
55     }
56
57     if (psiClass != null) {
58         return checkExtendClass(element, psiClass, extend.value(),
59                                 extend.instantiatable(), extend.canBeDecorator(), extend.allowInterface(),
60                                 extend.allowNonPublic(), extend.allowAbstract(), extend.allowEnum(), holder);
61     }
62     return Collections.emptyList();
63   }
64
65   @NotNull
66   public static List<DomElementProblemDescriptor> checkExtendClass(final GenericDomValue element, final PsiClass value, final String name,
67                                                                    final boolean instantiatable, final boolean canBeDecorator, final boolean allowInterface,
68                                                                    final boolean allowNonPublic,
69                                                                    final boolean allowAbstract,
70                                                                    final boolean allowEnum,
71                                                                    final DomElementAnnotationHolder holder) {
72     final Project project = element.getManager().getProject();
73     PsiClass extendClass = JavaPsiFacade.getInstance(project).findClass(name, GlobalSearchScope.allScope(project));
74     final SmartList<DomElementProblemDescriptor> list = new SmartList<DomElementProblemDescriptor>();
75     if (extendClass != null) {
76       if (!name.equals(value.getQualifiedName()) && !value.isInheritor(extendClass, true)) {
77         String message = DomBundle.message("class.is.not.a.subclass", value.getQualifiedName(), extendClass.getQualifiedName());
78         list.add(holder.createProblem(element, message));
79       }
80     }
81
82     if (instantiatable) {
83       if (value.hasModifierProperty(PsiModifier.ABSTRACT)) {
84         list.add(holder.createProblem(element, DomBundle.message("class.is.not.concrete", value.getQualifiedName())));
85       }
86       else if (!allowNonPublic && !value.hasModifierProperty(PsiModifier.PUBLIC)) {
87         list.add(holder.createProblem(element, DomBundle.message("class.is.not.public", value.getQualifiedName())));
88       }
89       else if (!hasDefaultConstructor(value)) {
90         if (canBeDecorator) {
91           boolean hasConstructor = false;
92
93           for (PsiMethod method : value.getConstructors()) {
94             final PsiParameterList psiParameterList = method.getParameterList();
95             if (psiParameterList.getParametersCount() != 1) continue;
96             final PsiType psiType = psiParameterList.getParameters()[0].getTypeElement().getType();
97             if (psiType instanceof PsiClassType) {
98               final PsiClass psiClass = ((PsiClassType)psiType).resolve();
99               if (psiClass != null && InheritanceUtil.isInheritorOrSelf(psiClass, extendClass, true)) {
100                 hasConstructor = true;
101                 break;
102               }
103             }
104           }
105           if (!hasConstructor) {
106             list.add(holder.createProblem(element, DomBundle.message("class.decorator.or.has.default.constructor", value.getQualifiedName())));
107           }
108         }
109         else {
110           list.add(holder.createProblem(element, DomBundle.message("class.has.no.default.constructor", value.getQualifiedName())));
111         }
112       }
113     }
114     if (!allowInterface && value.isInterface()) {
115       list.add(holder.createProblem(element, DomBundle.message("interface.not.allowed", value.getQualifiedName())));
116     }
117     if (!allowEnum && value.isEnum()) {
118       list.add(holder.createProblem(element, DomBundle.message("enum.not.allowed", value.getQualifiedName())));
119     }
120     if (!allowAbstract && value.hasModifierProperty(PsiModifier.ABSTRACT) && !value.isInterface()) {
121       list.add(holder.createProblem(element, DomBundle.message("abstract.class.not.allowed", value.getQualifiedName())));
122     }
123     return list;
124   }
125
126   public static boolean hasDefaultConstructor(PsiClass clazz) {
127     final PsiMethod[] constructors = clazz.getConstructors();
128     if (constructors.length > 0) {
129       for (PsiMethod cls: constructors) {
130         if ((cls.hasModifierProperty(PsiModifier.PUBLIC) || cls.hasModifierProperty(PsiModifier.PROTECTED)) && cls.getParameterList().getParametersCount() == 0) {
131           return true;
132         }
133       }
134     } else {
135       final PsiClass superClass = clazz.getSuperClass();
136       return superClass == null || hasDefaultConstructor(superClass);
137     }
138     return false;
139   }
140
141   public static List<DomElementProblemDescriptor> checkExtendsClassInReferences(final GenericDomValue element, final DomElementAnnotationHolder holder) {
142     final Object valueObject = element.getValue();
143     if (!(valueObject instanceof PsiClass)) return Collections.emptyList();
144
145     final PsiReference[] references = ourProvider.getReferencesByElement(DomUtil.getValueElement(element), new ProcessingContext());
146     for (PsiReference reference : references) {
147       if (reference instanceof JavaClassReference) {
148         final PsiReferenceProvider psiReferenceProvider = ((JavaClassReference)reference).getProvider();
149         final String[] value = psiReferenceProvider instanceof JavaClassReferenceProvider ? JavaClassReferenceProvider.EXTEND_CLASS_NAMES
150           .getValue(((JavaClassReferenceProvider)psiReferenceProvider).getOptions()) : null;
151         if (value != null && value.length != 0) {
152           for (String className : value) {
153             final List<DomElementProblemDescriptor> problemDescriptors =
154               checkExtendClass(element, ((PsiClass)valueObject), className, false, false, true, false, true, true, holder);
155             if (!problemDescriptors.isEmpty()) {
156               return problemDescriptors;
157             }
158           }
159         }
160       }
161     }
162     return Collections.emptyList();
163   }
164 }