2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com
.intellij
.util
.xml
.impl
;
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
.util
.ReflectionCache
;
25 import com
.intellij
.util
.SmartList
;
26 import com
.intellij
.util
.ProcessingContext
;
27 import com
.intellij
.util
.xml
.*;
28 import com
.intellij
.util
.xml
.highlighting
.DomCustomAnnotationChecker
;
29 import com
.intellij
.util
.xml
.highlighting
.DomElementAnnotationHolder
;
30 import com
.intellij
.util
.xml
.highlighting
.DomElementProblemDescriptor
;
31 import com
.intellij
.util
.xml
.highlighting
.DomHighlightingHelper
;
32 import org
.jetbrains
.annotations
.NotNull
;
34 import java
.util
.Collections
;
35 import java
.util
.List
;
40 public class ExtendsClassChecker
extends DomCustomAnnotationChecker
<ExtendClass
>{
41 private static final GenericValueReferenceProvider ourProvider
= new GenericValueReferenceProvider();
44 public Class
<ExtendClass
> getAnnotationClass() {
45 return ExtendClass
.class;
48 public List
<DomElementProblemDescriptor
> checkForProblems(@NotNull final ExtendClass extend
, @NotNull final DomElement _element
, @NotNull final DomElementAnnotationHolder holder
,
49 @NotNull final DomHighlightingHelper helper
) {
50 if (!(_element
instanceof GenericDomValue
)) return Collections
.emptyList();
51 GenericDomValue element
= (GenericDomValue
)_element
;
53 final Class genericValueParameter
= DomUtil
.getGenericValueParameter(element
.getDomElementType());
54 if (genericValueParameter
== null || (!ReflectionCache
.isAssignable(genericValueParameter
, PsiClass
.class) &&
55 !ReflectionCache
.isAssignable(genericValueParameter
, PsiType
.class))) {
56 return Collections
.emptyList();
59 final Object valueObject
= element
.getValue();
60 PsiClass psiClass
= null;
62 if (valueObject
instanceof PsiClass
) {
63 psiClass
= (PsiClass
)valueObject
;
64 } else if (valueObject
instanceof PsiClassType
) {
65 psiClass
= ((PsiClassType
)valueObject
).resolve();
68 if (psiClass
!= null) {
69 return checkExtendClass(element
, psiClass
, extend
.value(),
70 extend
.instantiatable(), extend
.canBeDecorator(), extend
.allowInterface(),
71 extend
.allowNonPublic(), extend
.allowAbstract(), extend
.allowEnum(), holder
);
73 return Collections
.emptyList();
77 public static List
<DomElementProblemDescriptor
> checkExtendClass(final GenericDomValue element
, final PsiClass value
, final String name
,
78 final boolean instantiatable
, final boolean canBeDecorator
, final boolean allowInterface
,
79 final boolean allowNonPublic
,
80 final boolean allowAbstract
,
81 final boolean allowEnum
,
82 final DomElementAnnotationHolder holder
) {
83 final Project project
= element
.getManager().getProject();
84 PsiClass extendClass
= JavaPsiFacade
.getInstance(project
).findClass(name
, GlobalSearchScope
.allScope(project
));
85 final SmartList
<DomElementProblemDescriptor
> list
= new SmartList
<DomElementProblemDescriptor
>();
86 if (extendClass
!= null) {
87 if (!name
.equals(value
.getQualifiedName()) && !value
.isInheritor(extendClass
, true)) {
88 String message
= DomBundle
.message("class.is.not.a.subclass", value
.getQualifiedName(), extendClass
.getQualifiedName());
89 list
.add(holder
.createProblem(element
, message
));
94 if (value
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
95 list
.add(holder
.createProblem(element
, DomBundle
.message("class.is.not.concrete", value
.getQualifiedName())));
97 else if (!allowNonPublic
&& !value
.hasModifierProperty(PsiModifier
.PUBLIC
)) {
98 list
.add(holder
.createProblem(element
, DomBundle
.message("class.is.not.public", value
.getQualifiedName())));
100 else if (!hasDefaultConstructor(value
)) {
101 if (canBeDecorator
) {
102 boolean hasConstructor
= false;
104 for (PsiMethod method
: value
.getConstructors()) {
105 final PsiParameterList psiParameterList
= method
.getParameterList();
106 if (psiParameterList
.getParametersCount() != 1) continue;
107 final PsiType psiType
= psiParameterList
.getParameters()[0].getTypeElement().getType();
108 if (psiType
instanceof PsiClassType
) {
109 final PsiClass psiClass
= ((PsiClassType
)psiType
).resolve();
110 if (psiClass
!= null && InheritanceUtil
.isInheritorOrSelf(psiClass
, extendClass
, true)) {
111 hasConstructor
= true;
116 if (!hasConstructor
) {
117 list
.add(holder
.createProblem(element
, DomBundle
.message("class.decorator.or.has.default.constructor", value
.getQualifiedName())));
121 list
.add(holder
.createProblem(element
, DomBundle
.message("class.has.no.default.constructor", value
.getQualifiedName())));
125 if (!allowInterface
&& value
.isInterface()) {
126 list
.add(holder
.createProblem(element
, DomBundle
.message("interface.not.allowed", value
.getQualifiedName())));
128 if (!allowEnum
&& value
.isEnum()) {
129 list
.add(holder
.createProblem(element
, DomBundle
.message("enum.not.allowed", value
.getQualifiedName())));
131 if (!allowAbstract
&& value
.hasModifierProperty(PsiModifier
.ABSTRACT
) && !value
.isInterface()) {
132 list
.add(holder
.createProblem(element
, DomBundle
.message("abstract.class.not.allowed", value
.getQualifiedName())));
137 public static boolean hasDefaultConstructor(PsiClass clazz
) {
138 final PsiMethod
[] constructors
= clazz
.getConstructors();
139 if (constructors
.length
> 0) {
140 for (PsiMethod cls
: constructors
) {
141 if ((cls
.hasModifierProperty(PsiModifier
.PUBLIC
) || cls
.hasModifierProperty(PsiModifier
.PROTECTED
)) && cls
.getParameterList().getParametersCount() == 0) {
146 final PsiClass superClass
= clazz
.getSuperClass();
147 return superClass
== null || hasDefaultConstructor(superClass
);
152 public static List
<DomElementProblemDescriptor
> checkExtendsClassInReferences(final GenericDomValue element
, final DomElementAnnotationHolder holder
) {
153 final Object valueObject
= element
.getValue();
154 if (!(valueObject
instanceof PsiClass
)) return Collections
.emptyList();
156 final PsiReference
[] references
= ourProvider
.getReferencesByElement(DomUtil
.getValueElement(element
), new ProcessingContext());
157 for (PsiReference reference
: references
) {
158 if (reference
instanceof JavaClassReference
) {
159 final PsiReferenceProvider psiReferenceProvider
= ((JavaClassReference
)reference
).getProvider();
160 final String
[] value
= psiReferenceProvider
instanceof JavaClassReferenceProvider ? JavaClassReferenceProvider
.EXTEND_CLASS_NAMES
161 .getValue(((JavaClassReferenceProvider
)psiReferenceProvider
).getOptions()) : null;
162 if (value
!= null && value
.length
!= 0) {
163 for (String className
: value
) {
164 final List
<DomElementProblemDescriptor
> problemDescriptors
=
165 checkExtendClass(element
, ((PsiClass
)valueObject
), className
, false, false, true, false, true, true, holder
);
166 if (!problemDescriptors
.isEmpty()) {
167 return problemDescriptors
;
173 return Collections
.emptyList();