remove duplication
[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             final PsiType psiType = psiParameterList.getParameters()[0].getTypeElement().getType();
109             if (psiType instanceof PsiClassType) {
110               final PsiClass psiClass = ((PsiClassType)psiType).resolve();
111               if (psiClass != null && InheritanceUtil.isInheritorOrSelf(psiClass, extendClass, true)) {
112                 hasConstructor = true;
113                 break;
114               }
115             }
116           }
117           if (!hasConstructor) {
118             list.add(holder.createProblem(element, DomBundle.message("class.decorator.or.has.default.constructor", value.getQualifiedName())));
119           }
120         }
121         else {
122           list.add(holder.createProblem(element, DomBundle.message("class.has.no.default.constructor", value.getQualifiedName())));
123         }
124       }
125     }
126     if (!allowInterface && value.isInterface()) {
127       list.add(holder.createProblem(element, DomBundle.message("interface.not.allowed", value.getQualifiedName())));
128     }
129     if (!allowEnum && value.isEnum()) {
130       list.add(holder.createProblem(element, DomBundle.message("enum.not.allowed", value.getQualifiedName())));
131     }
132     if (!allowAbstract && value.hasModifierProperty(PsiModifier.ABSTRACT) && !value.isInterface()) {
133       list.add(holder.createProblem(element, DomBundle.message("abstract.class.not.allowed", value.getQualifiedName())));
134     }
135     return list;
136   }
137
138   public static List<DomElementProblemDescriptor> checkExtendsClassInReferences(final GenericDomValue element, final DomElementAnnotationHolder holder) {
139     final Object valueObject = element.getValue();
140     if (!(valueObject instanceof PsiClass)) return Collections.emptyList();
141
142     final PsiReference[] references = ourProvider.getReferencesByElement(DomUtil.getValueElement(element), new ProcessingContext());
143     for (PsiReference reference : references) {
144       if (reference instanceof JavaClassReference) {
145         final PsiReferenceProvider psiReferenceProvider = ((JavaClassReference)reference).getProvider();
146         final String[] value = psiReferenceProvider instanceof JavaClassReferenceProvider ? JavaClassReferenceProvider.EXTEND_CLASS_NAMES
147           .getValue(((JavaClassReferenceProvider)psiReferenceProvider).getOptions()) : null;
148         if (value != null && value.length != 0) {
149           for (String className : value) {
150             final List<DomElementProblemDescriptor> problemDescriptors =
151               checkExtendClass(element, ((PsiClass)valueObject), className, false, false, true, false, true, true, holder);
152             if (!problemDescriptors.isEmpty()) {
153               return problemDescriptors;
154             }
155           }
156         }
157       }
158     }
159     return Collections.emptyList();
160   }
161 }