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();