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.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;
35 import java.util.Collections;
36 import java.util.List;
41 public class ExtendsClassChecker extends DomCustomAnnotationChecker<ExtendClass>{
42 private static final GenericValueReferenceProvider ourProvider = new GenericValueReferenceProvider();
45 public Class<ExtendClass> getAnnotationClass() {
46 return ExtendClass.class;
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;
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();
60 final Object valueObject = element.getValue();
61 PsiClass psiClass = null;
63 if (valueObject instanceof PsiClass) {
64 psiClass = (PsiClass)valueObject;
65 } else if (valueObject instanceof PsiClassType) {
66 psiClass = ((PsiClassType)valueObject).resolve();
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);
74 return Collections.emptyList();
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));
95 if (value.hasModifierProperty(PsiModifier.ABSTRACT)) {
96 list.add(holder.createProblem(element, DomBundle.message("class.is.not.concrete", value.getQualifiedName())));
98 else if (!allowNonPublic && !value.hasModifierProperty(PsiModifier.PUBLIC)) {
99 list.add(holder.createProblem(element, DomBundle.message("class.is.not.public", value.getQualifiedName())));
101 else if (!PsiUtil.hasDefaultConstructor(value, true)) {
102 if (canBeDecorator) {
103 boolean hasConstructor = false;
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;
117 if (!hasConstructor) {
118 list.add(holder.createProblem(element, DomBundle.message("class.decorator.or.has.default.constructor", value.getQualifiedName())));
122 list.add(holder.createProblem(element, DomBundle.message("class.has.no.default.constructor", value.getQualifiedName())));
126 if (!allowInterface && value.isInterface()) {
127 list.add(holder.createProblem(element, DomBundle.message("interface.not.allowed", value.getQualifiedName())));
129 if (!allowEnum && value.isEnum()) {
130 list.add(holder.createProblem(element, DomBundle.message("enum.not.allowed", value.getQualifiedName())));
132 if (!allowAbstract && value.hasModifierProperty(PsiModifier.ABSTRACT) && !value.isInterface()) {
133 list.add(holder.createProblem(element, DomBundle.message("abstract.class.not.allowed", value.getQualifiedName())));
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();
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;
159 return Collections.emptyList();