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.util.InheritanceUtil;
23 import com.intellij.psi.util.PsiUtil;
24 import com.intellij.util.ProcessingContext;
25 import com.intellij.util.ReflectionCache;
26 import com.intellij.util.SmartList;
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, value.getResolveScope());
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 (!PsiUtil.hasDefaultConstructor(value, true)) {
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 PsiTypeElement typeElement = psiParameterList.getParameters()[0].getTypeElement();
108 if (typeElement != null) {
109 final PsiType psiType = typeElement.getType();
110 if (psiType instanceof PsiClassType) {
111 final PsiClass psiClass = ((PsiClassType)psiType).resolve();
112 if (psiClass != null && InheritanceUtil.isInheritorOrSelf(psiClass, extendClass, true)) {
113 hasConstructor = true;
119 if (!hasConstructor) {
120 list.add(holder.createProblem(element, DomBundle.message("class.decorator.or.has.default.constructor", value.getQualifiedName())));
124 list.add(holder.createProblem(element, DomBundle.message("class.has.no.default.constructor", value.getQualifiedName())));
128 if (!allowInterface && value.isInterface()) {
129 list.add(holder.createProblem(element, DomBundle.message("interface.not.allowed", value.getQualifiedName())));
131 if (!allowEnum && value.isEnum()) {
132 list.add(holder.createProblem(element, DomBundle.message("enum.not.allowed", value.getQualifiedName())));
134 if (!allowAbstract && value.hasModifierProperty(PsiModifier.ABSTRACT) && !value.isInterface()) {
135 list.add(holder.createProblem(element, DomBundle.message("abstract.class.not.allowed", value.getQualifiedName())));
140 public static List<DomElementProblemDescriptor> checkExtendsClassInReferences(final GenericDomValue element, final DomElementAnnotationHolder holder) {
141 final Object valueObject = element.getValue();
142 if (!(valueObject instanceof PsiClass)) return Collections.emptyList();
144 final PsiReference[] references = ourProvider.getReferencesByElement(DomUtil.getValueElement(element), new ProcessingContext());
145 for (PsiReference reference : references) {
146 if (reference instanceof JavaClassReference) {
147 final PsiReferenceProvider psiReferenceProvider = ((JavaClassReference)reference).getProvider();
148 final String[] value = psiReferenceProvider instanceof JavaClassReferenceProvider ? JavaClassReferenceProvider.EXTEND_CLASS_NAMES
149 .getValue(((JavaClassReferenceProvider)psiReferenceProvider).getOptions()) : null;
150 if (value != null && value.length != 0) {
151 for (String className : value) {
152 final List<DomElementProblemDescriptor> problemDescriptors =
153 checkExtendClass(element, ((PsiClass)valueObject), className, false, false, true, false, true, true, holder);
154 if (!problemDescriptors.isEmpty()) {
155 return problemDescriptors;
161 return Collections.emptyList();