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
.xml
.impl
.schema
;
18 import com
.intellij
.codeInsight
.daemon
.Validator
;
19 import com
.intellij
.javaee
.ExternalResourceManager
;
20 import com
.intellij
.openapi
.project
.DumbAware
;
21 import com
.intellij
.openapi
.util
.Pair
;
22 import com
.intellij
.openapi
.util
.text
.StringUtil
;
23 import com
.intellij
.psi
.PsiElement
;
24 import com
.intellij
.psi
.PsiFile
;
25 import com
.intellij
.psi
.PsiReference
;
26 import com
.intellij
.psi
.meta
.PsiMetaData
;
27 import com
.intellij
.psi
.search
.PsiElementProcessor
;
28 import com
.intellij
.psi
.util
.CachedValue
;
29 import com
.intellij
.psi
.util
.CachedValueProvider
;
30 import com
.intellij
.psi
.xml
.XmlAttribute
;
31 import com
.intellij
.psi
.xml
.XmlDocument
;
32 import com
.intellij
.psi
.xml
.XmlFile
;
33 import com
.intellij
.psi
.xml
.XmlTag
;
34 import com
.intellij
.util
.ArrayUtil
;
35 import com
.intellij
.xml
.XmlAttributeDescriptor
;
36 import com
.intellij
.xml
.XmlElementDescriptor
;
37 import com
.intellij
.xml
.XmlNSDescriptor
;
38 import com
.intellij
.xml
.impl
.ExternalDocumentValidator
;
39 import com
.intellij
.xml
.util
.XmlUtil
;
40 import gnu
.trove
.THashSet
;
41 import org
.jetbrains
.annotations
.NonNls
;
42 import org
.jetbrains
.annotations
.NotNull
;
43 import org
.jetbrains
.annotations
.Nullable
;
50 @SuppressWarnings({"HardCodedStringLiteral"})
51 public class XmlNSDescriptorImpl
implements XmlNSDescriptor
,Validator
<XmlDocument
>, DumbAware
, XmlNSTypeDescriptorProvider
{
52 @NonNls private static final Set
<String
> STD_TYPES
= new HashSet
<String
>();
53 private static final Set
<String
> UNDECLARED_STD_TYPES
= new HashSet
<String
>();
54 private XmlFile myFile
;
56 private String myTargetNamespace
;
58 public static final String XSD_PREFIX
= "xsd";
60 @NonNls static final String ELEMENT_TAG_NAME
= "element";
61 @NonNls static final String ATTRIBUTE_TAG_NAME
= "attribute";
62 @NonNls static final String COMPLEX_TYPE_TAG_NAME
= "complexType";
63 @NonNls static final String SEQUENCE_TAG_NAME
= "sequence";
64 @NonNls static final String SCHEMA_TAG_NAME
= "schema";
65 @NonNls private static final String INCLUDE_TAG_NAME
= "include";
66 @NonNls private static final String IMPORT_TAG_NAME
= "import";
67 @NonNls private static final String REDEFINE_TAG_NAME
= "redefine";
69 public XmlNSDescriptorImpl(XmlFile file
) {
70 init(file
.getDocument());
73 public XmlNSDescriptorImpl() {
76 private Object
[] dependencies
;
78 private static final ThreadLocal
<Set
<PsiFile
>> myRedefinedDescriptorsInProcessing
= new ThreadLocal
<Set
<PsiFile
>>();
80 private static void collectDependencies(@Nullable XmlTag myTag
, @NotNull XmlFile myFile
, @NotNull Set
<PsiFile
> visited
) {
81 if (visited
.contains(myFile
)) return;
82 visited
.add( myFile
);
84 if (myTag
== null) return;
85 XmlTag
[] tags
= myTag
.getSubTags();
87 for (final XmlTag tag
: tags
) {
88 if (equalsToSchemaName(tag
, INCLUDE_TAG_NAME
) ||
89 equalsToSchemaName(tag
, IMPORT_TAG_NAME
)
91 final XmlAttribute schemaLocation
= tag
.getAttribute("schemaLocation", null);
92 if (schemaLocation
!= null) {
93 final XmlFile xmlFile
= XmlUtil
.findNamespaceByLocation(myFile
, schemaLocation
.getValue());
94 addDependency(xmlFile
, visited
);
96 } else if (equalsToSchemaName(tag
, REDEFINE_TAG_NAME
)) {
97 myRedefinedDescriptorsInProcessing
.set(visited
);
99 final XmlFile file
= getRedefinedElementDescriptorFile(tag
);
100 addDependency(file
, visited
);
102 myRedefinedDescriptorsInProcessing
.set(null);
107 final String schemaLocationDeclaration
= myTag
.getAttributeValue("schemaLocation", XmlUtil
.XML_SCHEMA_INSTANCE_URI
);
108 if(schemaLocationDeclaration
!= null) {
109 final StringTokenizer tokenizer
= new StringTokenizer(schemaLocationDeclaration
);
111 while(tokenizer
.hasMoreTokens()){
112 final String uri
= tokenizer
.nextToken();
114 if(tokenizer
.hasMoreTokens()){
115 PsiFile resourceLocation
= ExternalResourceManager
.getInstance().getResourceLocation(tokenizer
.nextToken(), myFile
, null);
116 if (resourceLocation
== null && uri
!= null) resourceLocation
= ExternalResourceManager
.getInstance().getResourceLocation(uri
, myFile
, null);
118 if (resourceLocation
instanceof XmlFile
) addDependency((XmlFile
)resourceLocation
, visited
);
124 private static void addDependency(final XmlFile file
, final Set
<PsiFile
> visited
) {
126 final XmlDocument document
= file
.getDocument();
127 collectDependencies(document
!= null ? document
.getRootTag():null, file
, visited
);
131 public XmlFile
getDescriptorFile() {
135 public boolean isHierarhyEnabled() {
139 public String
getDefaultNamespace(){
140 return myTargetNamespace
!= null ? myTargetNamespace
: "";
143 static class QNameKey
extends Pair
<String
, String
>{
144 QNameKey(String name
, String namespace
) {
145 super(name
, namespace
);
148 private final Map
<QNameKey
, CachedValue
<XmlElementDescriptor
>> myDescriptorsMap
= Collections
.synchronizedMap(new HashMap
<QNameKey
, CachedValue
<XmlElementDescriptor
>>());
149 private final Map
<Pair
<QNameKey
, XmlTag
>, CachedValue
<TypeDescriptor
>> myTypesMap
= Collections
.synchronizedMap(new HashMap
<Pair
<QNameKey
,XmlTag
>, CachedValue
<TypeDescriptor
>>());
152 public XmlElementDescriptor
getElementDescriptor(String localName
, String namespace
) {
153 return getElementDescriptor(localName
, namespace
, new HashSet
<XmlNSDescriptorImpl
>(),false);
156 public XmlElementDescriptor
getElementDescriptor(String localName
, String namespace
, Set
<XmlNSDescriptorImpl
> visited
, boolean reference
) {
157 if(visited
.contains(this)) return null;
159 final QNameKey pair
= new QNameKey(namespace
, localName
);
160 final CachedValue
<XmlElementDescriptor
> descriptor
= myDescriptorsMap
.get(pair
);
161 if(descriptor
!= null) {
162 final XmlElementDescriptor value
= descriptor
.getValue();
163 if (value
== null || value
.getDeclaration().isValid()) return value
;
166 final XmlTag rootTag
= myTag
;
167 if (rootTag
== null) return null;
168 XmlTag
[] tags
= rootTag
.getSubTags();
171 for (final XmlTag tag
: tags
) {
172 if (equalsToSchemaName(tag
, ELEMENT_TAG_NAME
)) {
173 String name
= tag
.getAttributeValue("name");
176 if (checkElementNameEquivalence(localName
, namespace
, name
, tag
)) {
177 final CachedValue
<XmlElementDescriptor
> cachedValue
=
178 tag
.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider
<XmlElementDescriptor
>() {
179 public Result
<XmlElementDescriptor
> compute() {
180 final String name
= tag
.getAttributeValue("name");
182 if (name
!= null && !name
.equals(pair
.second
)) {
183 myDescriptorsMap
.remove(pair
);
184 return new Result
<XmlElementDescriptor
>(null);
186 final XmlElementDescriptor xmlElementDescriptor
= createElementDescriptor(tag
);
187 return new Result
<XmlElementDescriptor
>(xmlElementDescriptor
, xmlElementDescriptor
.getDependences());
190 myDescriptorsMap
.put(pair
, cachedValue
);
191 return cachedValue
.getValue();
195 else if (equalsToSchemaName(tag
, INCLUDE_TAG_NAME
) ||
197 equalsToSchemaName(tag
, IMPORT_TAG_NAME
) &&
198 ( namespace
.equals(tag
.getAttributeValue("namespace")) ||
199 namespace
.length() == 0 && tag
.getAttributeValue("namespace") == null
203 final XmlAttribute schemaLocation
= tag
.getAttribute("schemaLocation", null);
204 if (schemaLocation
!= null) {
205 final XmlFile xmlFile
= XmlUtil
.findNamespaceByLocation(rootTag
.getContainingFile(), schemaLocation
.getValue());
206 if (xmlFile
!= null) {
207 final XmlDocument includedDocument
= xmlFile
.getDocument();
208 if (includedDocument
!= null) {
209 final PsiMetaData data
= includedDocument
.getMetaData();
210 if (data
instanceof XmlNSDescriptorImpl
) {
211 final XmlElementDescriptor elementDescriptor
=
212 ((XmlNSDescriptorImpl
)data
).getElementDescriptor(localName
, namespace
, visited
, reference
);
213 if (elementDescriptor
!= null) {
214 //final CachedValue<XmlElementDescriptor> value = includedDocument.getManager().getCachedValuesManager()
215 // .createCachedValue(new CachedValueProvider<XmlElementDescriptor>() {
216 // public Result<XmlElementDescriptor> compute() {
217 // return new Result<XmlElementDescriptor>(elementDescriptor, elementDescriptor.getDependences());
220 //return value.getValue();
221 return elementDescriptor
;
227 } else if (equalsToSchemaName(tag
, REDEFINE_TAG_NAME
)) {
228 final XmlNSDescriptorImpl nsDescriptor
= getRedefinedElementDescriptor(tag
);
229 if (nsDescriptor
!= null) {
230 final XmlElementDescriptor xmlElementDescriptor
= nsDescriptor
.getElementDescriptor(localName
, namespace
, visited
, reference
);
231 if (xmlElementDescriptor
!= null) return xmlElementDescriptor
;
239 protected XmlElementDescriptor
createElementDescriptor(final XmlTag tag
) {
240 return new XmlElementDescriptorImpl(tag
);
243 private boolean checkElementNameEquivalence(String localName
, String namespace
, String fqn
, XmlTag context
){
244 final String localAttrName
= XmlUtil
.findLocalNameByQualifiedName(fqn
);
245 if (!localAttrName
.equals(localName
)) return false;
246 final String attrNamespace
= context
.getNamespaceByPrefix(XmlUtil
.findPrefixByQualifiedName(fqn
));
247 if (attrNamespace
.equals(namespace
)) return true;
249 if(myTargetNamespace
== null){
250 if(XmlUtil
.EMPTY_URI
.equals(attrNamespace
))
254 final boolean b
= myTargetNamespace
.equals(namespace
);
256 return context
.getNSDescriptor(namespace
, true) == this; // schema's targetNamespace could be different from file systemId
262 public XmlAttributeDescriptor
getAttribute(String localName
, String namespace
, final XmlTag context
) {
263 return getAttributeImpl(localName
, namespace
,null);
267 private XmlAttributeDescriptor
getAttributeImpl(String localName
, String namespace
, Set
<XmlTag
> visited
) {
268 if (myTag
== null) return null;
270 XmlNSDescriptorImpl nsDescriptor
= (XmlNSDescriptorImpl
)myTag
.getNSDescriptor(namespace
, true);
272 if (nsDescriptor
!= this && nsDescriptor
!= null) {
273 return nsDescriptor
.getAttributeImpl(
280 if (visited
== null) visited
= new HashSet
<XmlTag
>(1);
281 else if(visited
.contains(myTag
)) return null;
283 XmlTag
[] tags
= myTag
.getSubTags();
285 for (XmlTag tag
: tags
) {
286 if (equalsToSchemaName(tag
, ATTRIBUTE_TAG_NAME
)) {
287 String name
= tag
.getAttributeValue("name");
290 if (checkElementNameEquivalence(localName
, namespace
, name
, tag
)) {
291 return createAttributeDescriptor(tag
);
294 } else if (equalsToSchemaName(tag
, INCLUDE_TAG_NAME
) ||
295 (equalsToSchemaName(tag
, IMPORT_TAG_NAME
) &&
296 namespace
.equals(tag
.getAttributeValue("namespace"))
299 final XmlAttribute schemaLocation
= tag
.getAttribute("schemaLocation", null);
301 if (schemaLocation
!= null) {
302 final XmlFile xmlFile
= XmlUtil
.findNamespaceByLocation(myTag
.getContainingFile(), schemaLocation
.getValue());
304 if (xmlFile
!= null) {
306 final XmlDocument includedDocument
= xmlFile
.getDocument();
307 if (includedDocument
!= null) {
308 final PsiMetaData data
= includedDocument
.getMetaData();
310 if(data
instanceof XmlNSDescriptorImpl
){
311 final XmlAttributeDescriptor attributeDescriptor
= ((XmlNSDescriptorImpl
)data
).getAttributeImpl(localName
, namespace
,visited
);
313 if(attributeDescriptor
!= null){
314 final CachedValue
<XmlAttributeDescriptor
> value
= includedDocument
.getManager().getCachedValuesManager().createCachedValue(
315 new CachedValueProvider
<XmlAttributeDescriptor
>(){
316 public Result
<XmlAttributeDescriptor
> compute() {
317 return new Result
<XmlAttributeDescriptor
>(attributeDescriptor
, attributeDescriptor
.getDependences());
322 return value
.getValue();
334 protected XmlAttributeDescriptorImpl
createAttributeDescriptor(final XmlTag tag
) {
335 return new XmlAttributeDescriptorImpl(tag
);
338 public TypeDescriptor
getTypeDescriptor(XmlTag descriptorTag
) {
339 String type
= descriptorTag
.getAttributeValue("type");
342 return getTypeDescriptor(type
, descriptorTag
);
345 return findTypeDescriptorImpl(descriptorTag
, null, null, null);
348 public TypeDescriptor
getTypeDescriptor(final String name
, XmlTag context
) {
349 if(checkSchemaNamespace(name
, context
)){
350 final String localNameByQualifiedName
= XmlUtil
.findLocalNameByQualifiedName(name
);
352 if (STD_TYPES
.contains(localNameByQualifiedName
) &&
353 ( name
.length() == localNameByQualifiedName
.length() ||
354 UNDECLARED_STD_TYPES
.contains(localNameByQualifiedName
)
357 return new StdTypeDescriptor(localNameByQualifiedName
);
360 return findTypeDescriptor(name
, context
);
363 public XmlElementDescriptor
getDescriptorByType(String qName
, XmlTag instanceTag
){
364 if(myTag
== null) return null;
365 final TypeDescriptor typeDescriptor
= findTypeDescriptor(qName
, instanceTag
);
366 if(!(typeDescriptor
instanceof ComplexTypeDescriptor
)) return null;
367 return new XmlElementDescriptorByType(instanceTag
, (ComplexTypeDescriptor
)typeDescriptor
);
370 private boolean checkSchemaNamespace(String name
, XmlTag context
){
371 final String namespace
= context
.getNamespaceByPrefix(XmlUtil
.findPrefixByQualifiedName(name
));
372 if(namespace
!= null && namespace
.length() > 0){
373 return checkSchemaNamespace(namespace
);
375 return XSD_PREFIX
.equals(XmlUtil
.findPrefixByQualifiedName(name
));
378 private static boolean checkSchemaNamespace(String namespace
) {
379 return XmlUtil
.XML_SCHEMA_URI
.equals(namespace
) ||
380 XmlUtil
.XML_SCHEMA_URI2
.equals(namespace
) ||
381 XmlUtil
.XML_SCHEMA_URI3
.equals(namespace
);
384 static boolean checkSchemaNamespace(XmlTag context
){
385 final String namespace
= context
.getNamespace();
386 if(namespace
!= null && namespace
.length() > 0){
387 return checkSchemaNamespace(namespace
);
389 return StringUtil
.startsWithConcatenationOf(context
.getName(), XSD_PREFIX
, ":");
392 static @NotNull XmlNSDescriptorImpl
getNSDescriptorToSearchIn(XmlTag rootTag
, final String name
, XmlNSDescriptorImpl defaultNSDescriptor
) {
393 if (name
== null) return defaultNSDescriptor
;
394 final String namespacePrefix
= XmlUtil
.findPrefixByQualifiedName(name
);
396 if (namespacePrefix
!= null && namespacePrefix
.length() > 0) {
397 final String namespace
= rootTag
.getNamespaceByPrefix(namespacePrefix
);
398 final XmlNSDescriptor nsDescriptor
= rootTag
.getNSDescriptor(namespace
, true);
400 if (nsDescriptor
instanceof XmlNSDescriptorImpl
) {
401 return (XmlNSDescriptorImpl
)nsDescriptor
;
405 return defaultNSDescriptor
;
409 protected TypeDescriptor
findTypeDescriptor(final String qname
) {
410 return findTypeDescriptor(qname
, myTag
);
414 protected TypeDescriptor
findTypeDescriptor(final String qname
, XmlTag context
) {
415 String namespace
= context
.getNamespaceByPrefix(XmlUtil
.findPrefixByQualifiedName(qname
));
416 return findTypeDescriptor(XmlUtil
.findLocalNameByQualifiedName(qname
), namespace
);
420 private TypeDescriptor
findTypeDescriptor(String localName
, String namespace
) {
421 return findTypeDescriptorImpl(myTag
, localName
, namespace
, null);
425 protected TypeDescriptor
findTypeDescriptorImpl(XmlTag rootTag
, final String name
, String namespace
, Set
<XmlTag
> visited
) {
426 XmlNSDescriptorImpl responsibleDescriptor
= this;
427 if (namespace
!= null && namespace
.length() != 0 && !namespace
.equals(getDefaultNamespace())) {
428 final XmlNSDescriptor nsDescriptor
= rootTag
.getNSDescriptor(namespace
, true);
430 if (nsDescriptor
instanceof XmlNSDescriptorImpl
) {
431 responsibleDescriptor
= (XmlNSDescriptorImpl
)nsDescriptor
;
435 if (responsibleDescriptor
!= this) {
436 return responsibleDescriptor
.findTypeDescriptor(XmlUtil
.findLocalNameByQualifiedName(name
));
439 if (rootTag
== null) return null;
440 if (visited
!= null) {
441 if (visited
.contains(rootTag
)) return null;
442 visited
.add(rootTag
);
445 final Pair
<QNameKey
, XmlTag
> pair
= new Pair
<QNameKey
, XmlTag
>(new QNameKey(name
, namespace
), rootTag
);
447 final CachedValue
<TypeDescriptor
> descriptor
= myTypesMap
.get(pair
);
448 if(descriptor
!= null) {
449 TypeDescriptor value
= descriptor
.getValue();
451 ( value
instanceof ComplexTypeDescriptor
&&
452 ((ComplexTypeDescriptor
)value
).getDeclaration().isValid()
458 XmlTag
[] tags
= rootTag
.getSubTags();
460 if (visited
== null) {
461 visited
= new HashSet
<XmlTag
>(1);
462 visited
.add(rootTag
);
465 return doFindIn(tags
, name
, namespace
, pair
, rootTag
, visited
);
468 private TypeDescriptor
doFindIn(final XmlTag
[] tags
, final String name
, final String namespace
, final Pair
<QNameKey
, XmlTag
> pair
, final XmlTag rootTag
, final Set
<XmlTag
> visited
) {
469 for (final XmlTag tag
: tags
) {
470 if (equalsToSchemaName(tag
, "complexType")) {
472 CachedValue
<TypeDescriptor
> value
= createAndPutTypesCachedValue(tag
, pair
);
473 return value
.getValue();
476 String nameAttribute
= tag
.getAttributeValue("name");
478 if (isSameName(name
, namespace
, nameAttribute
)) {
479 CachedValue
<TypeDescriptor
> cachedValue
= createAndPutTypesCachedValue(tag
, pair
);
480 return cachedValue
.getValue();
483 else if (equalsToSchemaName(tag
, "simpleType")) {
486 CachedValue
<TypeDescriptor
> value
= createAndPutTypesCachedValueSimpleType(tag
, pair
);
487 return value
.getValue();
490 String nameAttribute
= tag
.getAttributeValue("name");
492 if (isSameName(name
, namespace
, nameAttribute
)) {
493 CachedValue
<TypeDescriptor
> cachedValue
= createAndPutTypesCachedValue(tag
, pair
);
494 return cachedValue
.getValue();
497 else if (equalsToSchemaName(tag
, INCLUDE_TAG_NAME
) ||
498 ( equalsToSchemaName(tag
, IMPORT_TAG_NAME
) &&
499 (namespace
== null || !namespace
.equals(getDefaultNamespace()))
502 final String schemaLocation
= tag
.getAttributeValue("schemaLocation");
503 if (schemaLocation
!= null) {
504 final XmlFile xmlFile
= XmlUtil
.findNamespaceByLocation(rootTag
.getContainingFile(), schemaLocation
);
506 if (xmlFile
!= null) {
507 final XmlDocument document
= xmlFile
.getDocument();
509 if (document
!= null) {
510 final Set
<XmlTag
> visited1
= visited
;
512 final CachedValue
<TypeDescriptor
> value
=
513 tag
.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider
<TypeDescriptor
>() {
514 public Result
<TypeDescriptor
> compute() {
515 final String currentName
= tag
.getAttributeValue("name");
517 if (( currentName
!= null &&
518 !currentName
.equals(XmlUtil
.findLocalNameByQualifiedName(name
)) ) ||
519 !xmlFile
.isValid() ||
520 xmlFile
.getDocument() == null
522 myTypesMap
.remove(pair
);
523 return new Result
<TypeDescriptor
>(null);
526 final XmlDocument document
= xmlFile
.getDocument();
527 final XmlNSDescriptorImpl nsDescriptor
= findNSDescriptor(tag
, document
);
529 if (nsDescriptor
== null) {
530 myTypesMap
.remove(pair
);
531 return new Result
<TypeDescriptor
>(null);
534 final XmlTag rTag
= document
.getRootTag();
536 final TypeDescriptor complexTypeDescriptor
= nsDescriptor
.findTypeDescriptorImpl(rTag
, name
, namespace
, visited1
);
537 return new Result
<TypeDescriptor
>(complexTypeDescriptor
, rTag
);
542 if (value
.getValue() != null) {
543 myTypesMap
.put(pair
, value
);
544 return value
.getValue();
549 } else if (equalsToSchemaName(tag
, REDEFINE_TAG_NAME
)) {
550 final XmlTag
[] subTags
= tag
.getSubTags();
551 TypeDescriptor descriptor
= doFindIn(subTags
, name
, namespace
, pair
, rootTag
, visited
);
552 if (descriptor
!= null) return descriptor
;
554 final XmlNSDescriptorImpl nsDescriptor
= getRedefinedElementDescriptor(tag
);
555 if (nsDescriptor
!= null) {
556 final XmlTag redefinedRootTag
= ((XmlDocument
)nsDescriptor
.getDeclaration()).getRootTag();
557 descriptor
= doFindIn(redefinedRootTag
.getSubTags(), name
, namespace
, pair
, redefinedRootTag
, visited
);
558 if (descriptor
!= null) return descriptor
;
565 private boolean isSameName(@NotNull String name
, String namespace
, String nameAttribute
) {
566 return nameAttribute
!= null &&
567 (nameAttribute
.equals(name
) || (name
.indexOf(":") >= 0 && nameAttribute
.equals(name
.substring(name
.indexOf(":") + 1)))) &&
568 (namespace
== null || namespace
.length() == 0 || namespace
.equals(getDefaultNamespace()))
572 private XmlNSDescriptorImpl
findNSDescriptor(final XmlTag tag
, final XmlDocument document
) {
573 final XmlNSDescriptorImpl nsDescriptor
;
574 if(IMPORT_TAG_NAME
.equals(tag
.getLocalName())) {
575 final XmlNSDescriptor importedDescriptor
= (XmlNSDescriptor
)document
.getMetaData();
576 nsDescriptor
= (importedDescriptor
instanceof XmlNSDescriptorImpl
) ?
577 (XmlNSDescriptorImpl
)importedDescriptor
:
586 private CachedValue
<TypeDescriptor
> createAndPutTypesCachedValueSimpleType(final XmlTag tag
, final Pair
<QNameKey
, XmlTag
> pair
) {
587 final CachedValue
<TypeDescriptor
> value
= tag
.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider
<TypeDescriptor
>() {
588 public CachedValueProvider
.Result
<TypeDescriptor
> compute() {
589 final SimpleTypeDescriptor simpleTypeDescriptor
= new SimpleTypeDescriptor(tag
);
590 return new Result
<TypeDescriptor
>(simpleTypeDescriptor
, tag
);
593 myTypesMap
.put(pair
, value
);
597 private CachedValue
<TypeDescriptor
> createAndPutTypesCachedValue(final XmlTag tag
, final Pair
<QNameKey
, XmlTag
> pair
) {
598 final CachedValue
<TypeDescriptor
> value
= tag
.getManager().getCachedValuesManager().createCachedValue(new CachedValueProvider
<TypeDescriptor
>() {
599 public CachedValueProvider
.Result
<TypeDescriptor
> compute() {
600 final String name
= tag
.getAttributeValue("name");
603 pair
.first
!= null &&
604 pair
.first
.first
!= null &&
605 !name
.equals(XmlUtil
.findLocalNameByQualifiedName(pair
.first
.first
))
607 myTypesMap
.remove(pair
);
608 return new Result
<TypeDescriptor
>(null);
610 final ComplexTypeDescriptor complexTypeDescriptor
= new ComplexTypeDescriptor(XmlNSDescriptorImpl
.this, tag
);
611 return new Result
<TypeDescriptor
>(complexTypeDescriptor
, tag
);
614 myTypesMap
.put(pair
, value
);
618 public XmlElementDescriptor
getElementDescriptor(@NotNull XmlTag tag
) {
619 PsiElement parent
= tag
.getParent();
620 final String namespace
= tag
.getNamespace();
621 while(parent
instanceof XmlTag
&& !namespace
.equals(((XmlTag
)parent
).getNamespace()))
622 parent
= parent
.getContext();
623 if (parent
instanceof XmlTag
) {
624 final XmlTag parentTag
= (XmlTag
)parent
;
625 final XmlElementDescriptor parentDescriptor
= parentTag
.getDescriptor();
627 if(parentDescriptor
!= null){
628 XmlElementDescriptor elementDescriptorFromParent
= parentDescriptor
.getElementDescriptor(tag
, parentTag
);
630 if (elementDescriptorFromParent
== null) {
631 elementDescriptorFromParent
= getDescriptorFromParent(tag
, elementDescriptorFromParent
);
633 if (elementDescriptorFromParent
instanceof AnyXmlElementDescriptor
) {
634 final XmlElementDescriptor elementDescriptor
= getElementDescriptor(tag
.getLocalName(), namespace
);
635 if (elementDescriptor
!= null) return elementDescriptor
;
637 return elementDescriptorFromParent
;
644 XmlElementDescriptor elementDescriptor
= getElementDescriptor(tag
.getLocalName(), tag
.getNamespace());
646 if (elementDescriptor
== null) {
647 elementDescriptor
= getDescriptorFromParent(tag
, elementDescriptor
);
650 return elementDescriptor
;
654 private static XmlElementDescriptor
getDescriptorFromParent(final XmlTag tag
, XmlElementDescriptor elementDescriptor
) {
655 final PsiElement parent
= tag
.getParent();
656 if (parent
instanceof XmlTag
) {
657 final XmlElementDescriptor descriptor
= ((XmlTag
)parent
).getDescriptor();
658 if (descriptor
!= null) elementDescriptor
= descriptor
.getElementDescriptor(tag
, (XmlTag
)parent
);
660 return elementDescriptor
;
664 public XmlElementDescriptor
[] getRootElementsDescriptors(@Nullable final XmlDocument doc
) {
665 class CollectElementsProcessor
implements PsiElementProcessor
<XmlTag
> {
666 final List
<XmlElementDescriptor
> result
= new ArrayList
<XmlElementDescriptor
>();
668 public boolean execute(final XmlTag element
) {
669 result
.add(getElementDescriptor(element
.getAttributeValue("name"),getDefaultNamespace()));
674 CollectElementsProcessor processor
= new CollectElementsProcessor() {
675 public boolean execute(final XmlTag element
) {
676 if (!XmlElementDescriptorImpl
.isAbstractDeclaration(element
)) return super.execute(element
);
680 processTagsInNamespace(myTag
, new String
[] {ELEMENT_TAG_NAME
}, processor
);
682 return processor
.result
.toArray(new XmlElementDescriptor
[processor
.result
.size()]);
685 public XmlAttributeDescriptor
[] getRootAttributeDescriptors(final XmlTag context
) {
686 class CollectAttributesProcessor
implements PsiElementProcessor
<XmlTag
> {
687 final List
<XmlAttributeDescriptor
> result
= new ArrayList
<XmlAttributeDescriptor
>();
689 public boolean execute(final XmlTag element
) {
690 result
.add(createAttributeDescriptor(element
));
695 CollectAttributesProcessor processor
= new CollectAttributesProcessor();
696 processTagsInNamespace(myTag
, new String
[] {ATTRIBUTE_TAG_NAME
}, processor
);
698 return processor
.result
.toArray(new XmlAttributeDescriptor
[processor
.result
.size()]);
701 public boolean processTagsInNamespace(@NotNull final XmlTag rootTag
, String
[] tagNames
, PsiElementProcessor
<XmlTag
> processor
) {
702 return processTagsInNamespaceInner(rootTag
, tagNames
, processor
, null);
705 private static boolean processTagsInNamespaceInner(@NotNull final XmlTag rootTag
, final String
[] tagNames
,
706 final PsiElementProcessor
<XmlTag
> processor
, Set
<XmlTag
> visitedTags
) {
707 if (visitedTags
== null) visitedTags
= new HashSet
<XmlTag
>(3);
708 else if (visitedTags
.contains(rootTag
)) return true;
710 visitedTags
.add(rootTag
);
711 XmlTag
[] tags
= rootTag
.getSubTags();
714 for (XmlTag tag
: tags
) {
715 for(String tagName
:tagNames
) {
716 if (equalsToSchemaName(tag
, tagName
)) {
717 final String name
= tag
.getAttributeValue("name");
720 if (!processor
.execute(tag
)) {
729 if (equalsToSchemaName(tag
, INCLUDE_TAG_NAME
)) {
730 final String schemaLocation
= tag
.getAttributeValue("schemaLocation");
732 if (schemaLocation
!= null) {
733 final XmlFile xmlFile
= XmlUtil
.findNamespaceByLocation(rootTag
.getContainingFile(), schemaLocation
);
735 if (xmlFile
!= null) {
736 final XmlDocument includedDocument
= xmlFile
.getDocument();
738 if (includedDocument
!= null) {
739 if (!processTagsInNamespaceInner(includedDocument
.getRootTag(), tagNames
, processor
, visitedTags
)) return false;
749 protected static boolean equalsToSchemaName(XmlTag tag
, @NonNls String schemaName
) {
750 return schemaName
.equals(tag
.getLocalName()) && checkSchemaNamespace(tag
);
753 private static @Nullable XmlTag
findSpecialTag(@NonNls String name
, @NonNls String specialName
, XmlTag rootTag
, XmlNSDescriptorImpl descriptor
,
754 HashSet
<XmlTag
> visited
) {
755 XmlNSDescriptorImpl nsDescriptor
= getNSDescriptorToSearchIn(rootTag
, name
, descriptor
);
757 if (nsDescriptor
!= descriptor
) {
758 final XmlDocument document
= nsDescriptor
.getDescriptorFile() != null ? nsDescriptor
.getDescriptorFile().getDocument():null;
759 if (document
== null) return null;
761 return findSpecialTag(
762 XmlUtil
.findLocalNameByQualifiedName(name
),
764 document
.getRootTag(),
770 if (visited
== null) visited
= new HashSet
<XmlTag
>(1);
771 else if (visited
.contains(rootTag
)) return null;
772 visited
.add(rootTag
);
774 XmlTag
[] tags
= rootTag
.getSubTags();
776 return findSpecialTagIn(tags
, specialName
, name
, rootTag
, descriptor
, visited
);
779 private static XmlTag
findSpecialTagIn(final XmlTag
[] tags
,
780 final String specialName
,
782 final XmlTag rootTag
,
783 final XmlNSDescriptorImpl descriptor
, final HashSet
<XmlTag
> visited
) {
784 for (XmlTag tag
: tags
) {
785 if (equalsToSchemaName(tag
, specialName
)) {
786 String attribute
= tag
.getAttributeValue("name");
788 if (name
.equals(attribute
)
789 || name
.indexOf(":") >= 0 && name
.substring(name
.indexOf(":") + 1).equals(attribute
)) {
792 } else if (equalsToSchemaName(tag
, INCLUDE_TAG_NAME
) ||
793 ( equalsToSchemaName(tag
, IMPORT_TAG_NAME
) &&
794 rootTag
.getNamespaceByPrefix(
795 XmlUtil
.findPrefixByQualifiedName(name
)
796 ).equals(tag
.getAttributeValue("namespace"))
799 final String schemaLocation
= tag
.getAttributeValue("schemaLocation");
801 if (schemaLocation
!= null) {
802 final XmlFile xmlFile
= XmlUtil
.findNamespaceByLocation(rootTag
.getContainingFile(), schemaLocation
);
804 if (xmlFile
!= null) {
805 final XmlDocument document
= xmlFile
.getDocument();
806 if (document
!= null) {
807 final XmlTag rTag
= findSpecialTag(name
, specialName
, document
.getRootTag(), descriptor
, visited
);
809 if (rTag
!= null) return rTag
;
813 } else if (equalsToSchemaName(tag
, REDEFINE_TAG_NAME
)) {
814 XmlTag rTag
= findSpecialTagIn(tag
.getSubTags(), specialName
, name
, rootTag
, descriptor
, visited
);
815 if (rTag
!= null) return rTag
;
817 final XmlNSDescriptorImpl nsDescriptor
= getRedefinedElementDescriptor(tag
);
818 if (nsDescriptor
!= null) {
819 final XmlTag redefinedRootTag
= ((XmlDocument
)nsDescriptor
.getDeclaration()).getRootTag();
821 rTag
= findSpecialTagIn(redefinedRootTag
.getSubTags(), specialName
, name
, redefinedRootTag
, nsDescriptor
, visited
);
822 if (rTag
!= null) return rTag
;
830 public XmlTag
findGroup(String name
) {
831 return findSpecialTag(name
,"group",myTag
, this, null);
834 public XmlTag
findAttributeGroup(String name
) {
835 return findSpecialTag(name
,"attributeGroup",myTag
,this, null);
838 private Map
<String
,List
<XmlTag
>> mySubstitutions
;
840 public XmlElementDescriptor
[] getSubstitutes(String localName
, String namespace
) {
841 List
<XmlElementDescriptor
> result
= new ArrayList
<XmlElementDescriptor
>();
845 List
<XmlTag
> substitutions
= mySubstitutions
.get(localName
);
846 if (substitutions
==null) return XmlElementDescriptor
.EMPTY_ARRAY
;
847 for (XmlTag tag
: substitutions
) {
848 final String substAttr
= tag
.getAttributeValue("substitutionGroup");
849 if (substAttr
!= null && checkElementNameEquivalence(localName
, namespace
, substAttr
, tag
)) {
850 result
.add(createElementDescriptor(tag
));
854 return result
.toArray(new XmlElementDescriptor
[result
.size()]);
857 private void initSubstitutes() {
858 if (mySubstitutions
==null) {
859 mySubstitutions
= new HashMap
<String
, List
<XmlTag
>>();
861 XmlTag
[] tags
= myTag
.getSubTags();
863 for (XmlTag tag
: tags
) {
864 if (equalsToSchemaName(tag
, ELEMENT_TAG_NAME
)) {
865 final String substAttr
= tag
.getAttributeValue("substitutionGroup");
866 if (substAttr
!= null) {
867 String substLocalName
= XmlUtil
.findLocalNameByQualifiedName(substAttr
);
868 List
<XmlTag
> list
= mySubstitutions
.get(substLocalName
);
870 list
= new LinkedList
<XmlTag
>();
871 mySubstitutions
.put(substLocalName
, list
);
880 public static String
getSchemaNamespace(XmlFile file
) {
881 return XmlUtil
.findNamespacePrefixByURI(file
, "http://www.w3.org/2001/XMLSchema");
884 public PsiElement
getDeclaration(){
885 return myFile
.getDocument();
888 public String
getName(PsiElement context
){
892 public String
getName(){
896 public void init(PsiElement element
){
897 myFile
= (XmlFile
) element
.getContainingFile();
899 if (element
instanceof XmlTag
) {
900 myTag
= (XmlTag
)element
;
902 final XmlDocument document
= myFile
.getDocument();
904 if (document
!= null) {
905 myTag
= document
.getRootTag();
910 myTargetNamespace
= myTag
.getAttributeValue("targetNamespace");
913 final THashSet
<PsiFile
> dependenciesSet
= new THashSet
<PsiFile
>();
914 final Set
<PsiFile
> redefineProcessingSet
= myRedefinedDescriptorsInProcessing
.get();
915 if (redefineProcessingSet
!= null) {
916 dependenciesSet
.addAll(redefineProcessingSet
);
918 collectDependencies(myTag
, myFile
, dependenciesSet
);
919 dependencies
= dependenciesSet
.toArray(ArrayUtil
.EMPTY_OBJECT_ARRAY
);
922 public Object
[] getDependences() {
923 if (dependencies
== null) dependencies
= new Object
[] {myFile
}; // init was not called
928 STD_TYPES
.add("string");
929 STD_TYPES
.add("normalizedString");
930 STD_TYPES
.add("token");
931 STD_TYPES
.add("byte");
932 STD_TYPES
.add("unsignedByte");
933 STD_TYPES
.add("base64Binary");
934 STD_TYPES
.add("hexBinary");
935 STD_TYPES
.add("integer");
936 STD_TYPES
.add("positiveInteger");
937 STD_TYPES
.add("negativeInteger");
938 STD_TYPES
.add("nonNegativeInteger");
939 STD_TYPES
.add("nonPositiveInteger");
940 STD_TYPES
.add("int");
941 STD_TYPES
.add("unsignedInt");
942 STD_TYPES
.add("long");
943 STD_TYPES
.add("unsignedLong");
944 STD_TYPES
.add("short");
945 STD_TYPES
.add("unsignedShort");
946 STD_TYPES
.add("decimal");
947 STD_TYPES
.add("float");
948 STD_TYPES
.add("double");
949 STD_TYPES
.add("boolean");
950 STD_TYPES
.add("time");
951 STD_TYPES
.add("dateTime");
952 STD_TYPES
.add("duration");
953 STD_TYPES
.add("date");
954 STD_TYPES
.add("gMonth");
955 STD_TYPES
.add("gYear");
956 STD_TYPES
.add("gYearMonth");
957 STD_TYPES
.add("gDay");
958 STD_TYPES
.add("gMonthDay");
959 STD_TYPES
.add("Name");
960 STD_TYPES
.add("QName");
961 STD_TYPES
.add("NCName");
962 STD_TYPES
.add("anyURI");
963 STD_TYPES
.add("language");
965 STD_TYPES
.add("IDREF");
966 STD_TYPES
.add("IDREFS");
967 STD_TYPES
.add("ENTITY");
968 STD_TYPES
.add("ENTITIES");
969 STD_TYPES
.add("NOTATION");
970 STD_TYPES
.add("NMTOKEN");
971 STD_TYPES
.add("NMTOKENS");
972 STD_TYPES
.add("anySimpleType");
974 UNDECLARED_STD_TYPES
.add("anySimpleType");
977 public void validate(@NotNull XmlDocument context
, @NotNull Validator
.ValidationHost host
) {
978 ExternalDocumentValidator
.doValidation(context
,host
);
981 public XmlTag
getTag() {
985 public static XmlNSDescriptorImpl
getRedefinedElementDescriptor(final XmlTag parentTag
) {
986 XmlFile file
= getRedefinedElementDescriptorFile(parentTag
);
988 final XmlDocument document
= file
.getDocument();
989 final PsiMetaData metaData
= document
!= null ? document
.getMetaData():null;
990 if (metaData
instanceof XmlNSDescriptorImpl
) return (XmlNSDescriptorImpl
)metaData
;
995 public static XmlFile
getRedefinedElementDescriptorFile(final XmlTag parentTag
) {
996 final String schemaL
= parentTag
.getAttributeValue(XmlUtil
.SCHEMA_LOCATION_ATT
);
998 if (schemaL
!= null) {
999 final PsiReference
[] references
= parentTag
.getAttribute(XmlUtil
.SCHEMA_LOCATION_ATT
, null).getValueElement().getReferences();
1001 if (references
.length
> 0) {
1002 final PsiElement psiElement
= references
[references
.length
- 1].resolve();
1004 if (psiElement
instanceof XmlFile
) {
1005 return ((XmlFile
)psiElement
);
1012 public boolean hasSubstitutions() {
1014 return mySubstitutions
!= null && mySubstitutions
.size() > 0;
1017 public boolean isValid() {
1018 return myFile
!= null && getDeclaration().isValid();