2 * Copyright (c) 2000-2005 by JetBrains s.r.o. All Rights Reserved.
3 * Use is subject to license terms.
5 package com
.intellij
.util
.xml
.highlighting
;
7 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.XmlHighlightVisitor
;
8 import com
.intellij
.ide
.IdeBundle
;
9 import com
.intellij
.openapi
.project
.Project
;
10 import com
.intellij
.openapi
.util
.text
.StringUtil
;
11 import com
.intellij
.psi
.*;
12 import com
.intellij
.psi
.search
.GlobalSearchScope
;
13 import com
.intellij
.psi
.util
.InheritanceUtil
;
14 import com
.intellij
.psi
.xml
.XmlAttributeValue
;
15 import com
.intellij
.psi
.xml
.XmlElement
;
16 import com
.intellij
.util
.SmartList
;
17 import com
.intellij
.util
.containers
.ContainerUtil
;
18 import com
.intellij
.util
.xml
.*;
19 import com
.intellij
.util
.xml
.impl
.ConvertContextImpl
;
20 import com
.intellij
.util
.xml
.impl
.DomManagerImpl
;
21 import com
.intellij
.util
.xml
.impl
.GenericDomValueReference
;
22 import com
.intellij
.util
.xml
.impl
.GenericValueReferenceProvider
;
23 import com
.intellij
.util
.xml
.reflect
.DomChildrenDescription
;
24 import com
.intellij
.util
.xml
.reflect
.DomCollectionChildDescription
;
25 import com
.intellij
.util
.xml
.reflect
.DomGenericInfo
;
26 import org
.jetbrains
.annotations
.NotNull
;
27 import org
.jetbrains
.annotations
.Nullable
;
29 import java
.util
.Arrays
;
30 import java
.util
.Collections
;
31 import java
.util
.List
;
37 public class DomHighlightingHelperImpl
extends DomHighlightingHelper
{
38 private final GenericValueReferenceProvider myProvider
= new GenericValueReferenceProvider();
39 private final DomElementAnnotationsManagerImpl myAnnotationsManager
;
41 public DomHighlightingHelperImpl(final DomElementAnnotationsManagerImpl annotationsManager
) {
42 myAnnotationsManager
= annotationsManager
;
45 public void runAnnotators(DomElement element
, DomElementAnnotationHolder holder
, Class
<?
extends DomElement
> rootClass
) {
46 myAnnotationsManager
.annotate(element
, holder
, rootClass
);
50 public List
<DomElementProblemDescriptor
> checkRequired(DomElement element
, final DomElementAnnotationHolder holder
) {
51 final Required required
= element
.getAnnotation(Required
.class);
52 if (required
!= null) {
53 final XmlElement xmlElement
= element
.getXmlElement();
54 if (xmlElement
== null) {
55 if (required
.value()) {
56 if (element
instanceof GenericAttributeValue
) {
57 return Arrays
.asList(holder
.createProblem(element
, IdeBundle
.message("attribute.0.should.be.defined", element
.getXmlElementName())));
59 return Arrays
.asList(holder
.createProblem(element
, IdeBundle
.message("child.tag.0.should.be.defined", element
.getXmlElementName())));
62 else if (element
instanceof GenericDomValue
) {
63 return ContainerUtil
.createMaybeSingletonList(checkRequiredGenericValue((GenericDomValue
)element
, required
, holder
));
66 if (element
.getXmlElement() != null) {
67 final SmartList
<DomElementProblemDescriptor
> list
= new SmartList
<DomElementProblemDescriptor
>();
68 final DomGenericInfo info
= element
.getGenericInfo();
69 for (final DomChildrenDescription description
: info
.getChildrenDescriptions()) {
70 if (description
instanceof DomCollectionChildDescription
&& description
.getValues(element
).isEmpty()) {
71 final DomCollectionChildDescription childDescription
= (DomCollectionChildDescription
)description
;
72 final Required annotation
= description
.getAnnotation(Required
.class);
73 if (annotation
!= null && annotation
.value()) {
74 list
.add(holder
.createProblem(element
, childDescription
,
75 IdeBundle
.message("child.tag.0.should.be.defined", description
.getXmlElementName())));
81 return Collections
.emptyList();
85 public List
<DomElementProblemDescriptor
> checkExtendClass(GenericDomValue element
, final DomElementAnnotationHolder holder
) {
86 final Class genericValueParameter
= DomUtil
.getGenericValueParameter(element
.getDomElementType());
87 if (genericValueParameter
== null || !PsiClass
.class.isAssignableFrom(genericValueParameter
)) {
88 return Collections
.emptyList();
91 PsiClass value
= (PsiClass
)element
.getValue();
93 ExtendClass extend
= element
.getAnnotation(ExtendClass
.class);
95 Project project
= element
.getManager().getProject();
96 final String name
= extend
.value();
97 PsiClass extendClass
= PsiManager
.getInstance(project
).findClass(name
, GlobalSearchScope
.allScope(project
));
98 if (extendClass
!= null) {
99 final SmartList
<DomElementProblemDescriptor
> list
= new SmartList
<DomElementProblemDescriptor
>();
100 if (!name
.equals(value
.getQualifiedName()) && !value
.isInheritor(extendClass
, true)) {
101 String message
= IdeBundle
.message("class.is.not.a.subclass", value
.getQualifiedName(), extendClass
.getQualifiedName());
102 list
.add(holder
.createProblem(element
, message
));
103 } else if (extend
.instantiatable()) {
104 if (value
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
105 list
.add(holder
.createProblem(element
, IdeBundle
.message("class.is.not.concrete", value
.getQualifiedName())));
106 } else if (!value
.hasModifierProperty(PsiModifier
.PUBLIC
)) {
107 list
.add(holder
.createProblem(element
, IdeBundle
.message("class.is.not.public", value
.getQualifiedName())));
108 } else if (!hasDefaultConstructor(value
)) {
109 if (extend
.canBeDecorator()) {
110 boolean hasConstructor
= false;
112 for (PsiMethod method
: value
.getConstructors()) {
113 final PsiParameterList psiParameterList
= method
.getParameterList();
114 if (psiParameterList
.getParametersCount() != 1) continue;
115 final PsiType psiType
= psiParameterList
.getParameters()[0].getTypeElement().getType();
116 if (psiType
instanceof PsiClassType
) {
117 final PsiClass psiClass
= ((PsiClassType
)psiType
).resolve();
118 if (psiClass
!= null && InheritanceUtil
.isInheritorOrSelf(psiClass
, extendClass
, true)) {
119 hasConstructor
= true;
123 } if (!hasConstructor
) {
124 list
.add(holder
.createProblem(element
, IdeBundle
.message("class.decorator.or.has.default.constructor", value
.getQualifiedName())));
127 list
.add(holder
.createProblem(element
, IdeBundle
.message("class.has.no.default.constructor", value
.getQualifiedName())));
135 return Collections
.emptyList();
139 public List
<DomElementProblemDescriptor
> checkResolveProblems(GenericDomValue element
, final DomElementAnnotationHolder holder
) {
140 final XmlElement valueElement
= DomUtil
.getValueElement(element
);
141 if (valueElement
!= null && !isSoftReference(element
)) {
142 final SmartList
<DomElementProblemDescriptor
> list
= new SmartList
<DomElementProblemDescriptor
>();
143 final PsiReference
[] psiReferences
= myProvider
.getReferencesByElement(valueElement
);
144 boolean hasBadResolve
= false;
145 boolean hasDomValueReference
= false;
146 for (final PsiReference reference
: psiReferences
) {
147 hasDomValueReference
= hasDomValueReference
|| reference
instanceof GenericDomValueReference
;
148 if (hasBadResolve(element
, reference
)) {
149 hasBadResolve
= true;
150 list
.add(holder
.createResolveProblem(element
, reference
));
153 if (!hasBadResolve
&& !hasDomValueReference
&& element
.getConverter() instanceof ResolvingConverter
) {
154 final GenericDomValueReference reference
= new GenericDomValueReference(element
);
155 if (hasBadResolve(element
, reference
)) {
156 list
.add(holder
.createResolveProblem(element
, reference
));
159 if (psiReferences
.length
== 0 && element
.getValue() == null) {
160 final String errorMessage
= element
.getConverter()
161 .getErrorMessage(element
.getStringValue(), new ConvertContextImpl(DomManagerImpl
.getDomInvocationHandler(element
)));
162 list
.add(holder
.createProblem(element
, errorMessage
));
166 return Collections
.emptyList();
170 public List
<DomElementProblemDescriptor
> checkNameIdentity(DomElement element
, final DomElementAnnotationHolder holder
) {
171 final String elementName
= ElementPresentationManager
.getElementName(element
);
172 if (StringUtil
.isNotEmpty(elementName
)) {
173 final DomElement domElement
= DomUtil
.findDuplicateNamedValue(element
, elementName
);
174 if (domElement
!= null) {
175 final String typeName
= ElementPresentationManager
.getTypeNameForObject(element
);
176 final GenericDomValue genericDomValue
= domElement
.getGenericInfo().getNameDomElement(element
);
177 return Arrays
.asList(holder
.createProblem(genericDomValue
, domElement
.getRoot().equals(element
.getRoot())
178 ? IdeBundle
.message("model.highlighting.identity", typeName
)
179 : IdeBundle
.message("model.highlighting.identity.in.other.file", typeName
,
180 domElement
.getXmlTag().getContainingFile().getName())));
183 return Collections
.emptyList();
186 private static boolean hasBadResolve(GenericDomValue value
, PsiReference reference
) {
187 if (XmlHighlightVisitor
.hasBadResolve(reference
)) {
188 final Converter converter
= value
.getConverter();
189 if (converter
instanceof ResolvingConverter
) {
190 final ResolvingConverter resolvingConverter
= (ResolvingConverter
)converter
;
191 final Set additionalVariants
= resolvingConverter
.getAdditionalVariants();
192 if (additionalVariants
.contains(value
.getStringValue())) {
201 private static boolean isSoftReference(GenericDomValue value
) {
202 final Resolve resolve
= value
.getAnnotation(Resolve
.class);
203 if (resolve
!= null && resolve
.soft()) return true;
205 final Convert convert
= value
.getAnnotation(Convert
.class);
206 if (convert
!= null && convert
.soft()) return true;
208 final Referencing referencing
= value
.getAnnotation(Referencing
.class);
209 if (referencing
!= null && referencing
.soft()) return true;
215 private static DomElementProblemDescriptor
checkRequiredGenericValue(final GenericDomValue child
, final Required required
,
216 final DomElementAnnotationHolder annotator
) {
217 assert child
.getXmlTag() != null;
219 final String stringValue
= child
.getStringValue();
220 assert stringValue
!= null;
221 if (required
.nonEmpty() && isEmpty(child
, stringValue
)) {
222 return annotator
.createProblem(child
, IdeBundle
.message("value.must.not.be.empty"));
224 if (required
.identifier() && !PsiManager
.getInstance(child
.getManager().getProject()).getNameHelper().isIdentifier(stringValue
)) {
225 return annotator
.createProblem(child
, IdeBundle
.message("value.must.be.identifier"));
230 private static boolean isEmpty(final GenericDomValue child
, final String stringValue
) {
231 if (stringValue
.trim().length() != 0) {
234 if (child
instanceof GenericAttributeValue
) {
235 final XmlAttributeValue value
= ((GenericAttributeValue
)child
).getXmlAttributeValue();
236 if (value
!= null && value
.getTextRange().isEmpty()) {
244 private static boolean hasDefaultConstructor(PsiClass clazz
) {
245 final PsiMethod
[] constructors
= clazz
.getConstructors();
246 if (constructors
.length
> 0) {
247 for (PsiMethod cls
: constructors
) {
248 if ((cls
.hasModifierProperty(PsiModifier
.PUBLIC
) || cls
.hasModifierProperty(PsiModifier
.PROTECTED
)) && cls
.getParameterList().getParametersCount() == 0) {
253 final PsiClass superClass
= clazz
.getSuperClass();
254 return superClass
== null || hasDefaultConstructor(superClass
);