bcf0c747dc13f01ef286991680158ad0890b8b99
[idea/community.git] / java / java-impl / src / com / intellij / util / xml / impl / ExtendsClassChecker.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.util.xml.impl;
17
18 import com.intellij.openapi.project.Project;
19 import com.intellij.psi.*;
20 import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReference;
21 import com.intellij.psi.impl.source.resolve.reference.impl.providers.JavaClassReferenceProvider;
22 import com.intellij.psi.search.GlobalSearchScope;
23 import com.intellij.psi.util.InheritanceUtil;
24 import com.intellij.psi.util.PsiUtil;
25 import com.intellij.util.ProcessingContext;
26 import com.intellij.util.ReflectionCache;
27 import com.intellij.util.SmartList;
28 import com.intellij.util.xml.*;
29 import com.intellij.util.xml.highlighting.DomCustomAnnotationChecker;
30 import com.intellij.util.xml.highlighting.DomElementAnnotationHolder;
31 import com.intellij.util.xml.highlighting.DomElementProblemDescriptor;
32 import com.intellij.util.xml.highlighting.DomHighlightingHelper;
33 import org.jetbrains.annotations.NotNull;
34
35 import java.util.Collections;
36 import java.util.List;
37
38 /**
39  * @author peter
40  */
41 public class ExtendsClassChecker extends DomCustomAnnotationChecker<ExtendClass>{
42   private static final GenericValueReferenceProvider ourProvider = new GenericValueReferenceProvider();
43
44   @NotNull
45   public Class<ExtendClass> getAnnotationClass() {
46     return ExtendClass.class;
47   }
48
49   public List<DomElementProblemDescriptor> checkForProblems(@NotNull final ExtendClass extend, @NotNull final DomElement _element, @NotNull final DomElementAnnotationHolder holder,
50                             @NotNull final DomHighlightingHelper helper) {
51     if (!(_element instanceof GenericDomValue)) return Collections.emptyList();
52     GenericDomValue element = (GenericDomValue)_element;
53
54     final Class genericValueParameter = DomUtil.getGenericValueParameter(element.getDomElementType());
55     if (genericValueParameter == null || (!ReflectionCache.isAssignable(genericValueParameter, PsiClass.class) &&
56                                            !ReflectionCache.isAssignable(genericValueParameter, PsiType.class))) {
57       return Collections.emptyList();
58     }
59
60     final Object valueObject = element.getValue();
61     PsiClass psiClass = null;
62
63     if (valueObject instanceof PsiClass) {
64       psiClass = (PsiClass)valueObject;
65     } else if (valueObject instanceof PsiClassType) {
66       psiClass = ((PsiClassType)valueObject).resolve();
67     }
68
69     if (psiClass != null) {
70         return checkExtendClass(element, psiClass, extend.value(),
71                                 extend.instantiatable(), extend.canBeDecorator(), extend.allowInterface(),
72                                 extend.allowNonPublic(), extend.allowAbstract(), extend.allowEnum(), holder);
73     }
74     return Collections.emptyList();
75   }
76
77   @NotNull
78   public static List<DomElementProblemDescriptor> checkExtendClass(final GenericDomValue element, final PsiClass value, final String name,
79                                                                    final boolean instantiatable, final boolean canBeDecorator, final boolean allowInterface,
80                                                                    final boolean allowNonPublic,
81                                                                    final boolean allowAbstract,
82                                                                    final boolean allowEnum,
83                                                                    final DomElementAnnotationHolder holder) {
84     final Project project = element.getManager().getProject();
85     PsiClass extendClass = JavaPsiFacade.getInstance(project).findClass(name, GlobalSearchScope.allScope(project));
86     final SmartList<DomElementProblemDescriptor> list = new SmartList<DomElementProblemDescriptor>();
87     if (extendClass != null) {
88       if (!name.equals(value.getQualifiedName()) && !value.isInheritor(extendClass, true)) {
89         String message = DomBundle.message("class.is.not.a.subclass", value.getQualifiedName(), extendClass.getQualifiedName());
90         list.add(holder.createProblem(element, message));
91       }
92     }
93
94     if (instantiatable) {
95       if (value.hasModifierProperty(PsiModifier.ABSTRACT)) {
96         list.add(holder.createProblem(element, DomBundle.message("class.is.not.concrete", value.getQualifiedName())));
97       }
98       else if (!allowNonPublic && !value.hasModifierProperty(PsiModifier.PUBLIC)) {
99         list.add(holder.createProblem(element, DomBundle.message("class.is.not.public", value.getQualifiedName())));
100       }
101       else if (!PsiUtil.hasDefaultConstructor(value, true)) {
102         if (canBeDecorator) {
103           boolean hasConstructor = false;
104
105           for (PsiMethod method : value.getConstructors()) {
106             final PsiParameterList psiParameterList = method.getParameterList();
107             if (psiParameterList.getParametersCount() != 1) continue;
108             PsiTypeElement typeElement = psiParameterList.getParameters()[0].getTypeElement();
109             if (typeElement != null) {
110               final PsiType psiType = typeElement.getType();
111               if (psiType instanceof PsiClassType) {
112                 final PsiClass psiClass = ((PsiClassType)psiType).resolve();
113                 if (psiClass != null && InheritanceUtil.isInheritorOrSelf(psiClass, extendClass, true)) {
114                   hasConstructor = true;
115                   break;
116                 }
117               }
118             }
119           }
120           if (!hasConstructor) {
121             list.add(holder.createProblem(element, DomBundle.message("class.decorator.or.has.default.constructor", value.getQualifiedName())));
122           }
123         }
124         else {
125           list.add(holder.createProblem(element, DomBundle.message("class.has.no.default.constructor", value.getQualifiedName())));
126         }
127       }
128     }
129     if (!allowInterface && value.isInterface()) {
130       list.add(holder.createProblem(element, DomBundle.message("interface.not.allowed", value.getQualifiedName())));
131     }
132     if (!allowEnum && value.isEnum()) {
133       list.add(holder.createProblem(element, DomBundle.message("enum.not.allowed", value.getQualifiedName())));
134     }
135     if (!allowAbstract && value.hasModifierProperty(PsiModifier.ABSTRACT) && !value.isInterface()) {
136       list.add(holder.createProblem(element, DomBundle.message("abstract.class.not.allowed", value.getQualifiedName())));
137     }
138     return list;
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 }