IDEADEV-42022: merge schema provided by xsd file and by java class for ui.xml files
[fedora-idea.git] / xml / impl / src / com / intellij / xml / impl / schema / XmlNSDescriptorImpl.java
blobd387b63c3969eb45fc3e6e7982b600444ca83636
1 /*
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;
45 import java.util.*;
47 /**
48 * @author Mike
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;
55 private XmlTag myTag;
56 private String myTargetNamespace;
57 @NonNls
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)
90 ) {
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);
98 try {
99 final XmlFile file = getRedefinedElementDescriptorFile(tag);
100 addDependency(file, visited);
101 } finally {
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) {
125 if (file != null) {
126 final XmlDocument document = file.getDocument();
127 collectDependencies(document != null ? document.getRootTag():null, file, visited);
131 public XmlFile getDescriptorFile() {
132 return myFile;
135 public boolean isHierarhyEnabled() {
136 return true;
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>>());
151 @Nullable
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();
169 visited.add( this );
171 for (final XmlTag tag : tags) {
172 if (equalsToSchemaName(tag, ELEMENT_TAG_NAME)) {
173 String name = tag.getAttributeValue("name");
175 if (name != null) {
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());
189 }, false);
190 myDescriptorsMap.put(pair, cachedValue);
191 return cachedValue.getValue();
195 else if (equalsToSchemaName(tag, INCLUDE_TAG_NAME) ||
196 (reference &&
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());
218 // }
219 // }, false);
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;
236 return null;
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))
251 return true;
253 else {
254 final boolean b = myTargetNamespace.equals(namespace);
255 if (b) return b;
256 return context.getNSDescriptor(namespace, true) == this; // schema's targetNamespace could be different from file systemId
258 return false;
261 @Nullable
262 public XmlAttributeDescriptor getAttribute(String localName, String namespace, final XmlTag context) {
263 return getAttributeImpl(localName, namespace,null);
266 @Nullable
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(
274 localName,
275 namespace,
276 visited
280 if (visited == null) visited = new HashSet<XmlTag>(1);
281 else if(visited.contains(myTag)) return null;
282 visited.add(myTag);
283 XmlTag[] tags = myTag.getSubTags();
285 for (XmlTag tag : tags) {
286 if (equalsToSchemaName(tag, ATTRIBUTE_TAG_NAME)) {
287 String name = tag.getAttributeValue("name");
289 if (name != null) {
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());
320 false
322 return value.getValue();
331 return null;
334 protected XmlAttributeDescriptorImpl createAttributeDescriptor(final XmlTag tag) {
335 return new XmlAttributeDescriptorImpl(tag);
338 public TypeDescriptor getTypeDescriptor(XmlTag descriptorTag) {
339 String type = descriptorTag.getAttributeValue("type");
341 if (type != null) {
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;
408 @Nullable
409 protected TypeDescriptor findTypeDescriptor(final String qname) {
410 return findTypeDescriptor(qname, myTag);
413 @Nullable
414 protected TypeDescriptor findTypeDescriptor(final String qname, XmlTag context) {
415 String namespace = context.getNamespaceByPrefix(XmlUtil.findPrefixByQualifiedName(qname));
416 return findTypeDescriptor(XmlUtil.findLocalNameByQualifiedName(qname), namespace);
419 @Nullable
420 private TypeDescriptor findTypeDescriptor(String localName, String namespace) {
421 return findTypeDescriptorImpl(myTag, localName, namespace, null);
424 @Nullable
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();
450 if (value == null ||
451 ( value instanceof ComplexTypeDescriptor &&
452 ((ComplexTypeDescriptor)value).getDeclaration().isValid()
455 return value;
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")) {
471 if (name == null) {
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")) {
485 if (name == null) {
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);
539 }, false
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;
562 return null;
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:
578 this;
580 else {
581 nsDescriptor = this;
583 return nsDescriptor;
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);
592 }, false);
593 myTypesMap.put(pair, value);
594 return 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");
602 if (name != null &&
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);
613 }, false);
614 myTypesMap.put(pair, value);
615 return 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;
639 else{
640 return null;
643 else {
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;
663 @NotNull
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()));
670 return true;
674 CollectElementsProcessor processor = new CollectElementsProcessor() {
675 public boolean execute(final XmlTag element) {
676 if (!XmlElementDescriptorImpl.isAbstractDeclaration(element)) return super.execute(element);
677 return true;
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));
691 return true;
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();
713 NextTag:
714 for (XmlTag tag : tags) {
715 for(String tagName:tagNames) {
716 if (equalsToSchemaName(tag, tagName)) {
717 final String name = tag.getAttributeValue("name");
719 if (name != null) {
720 if (!processor.execute(tag)) {
721 return false;
725 continue NextTag;
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;
746 return true;
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),
763 specialName,
764 document.getRootTag(),
765 nsDescriptor,
766 visited
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,
781 final String name,
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)) {
790 return tag;
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;
827 return null;
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>();
843 initSubstitutes();
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);
869 if (list == null) {
870 list = new LinkedList<XmlTag>();
871 mySubstitutions.put(substLocalName, list);
873 list.add(tag);
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){
889 return getName();
892 public String getName(){
893 return "";
896 public void init(PsiElement element){
897 myFile = (XmlFile) element.getContainingFile();
899 if (element instanceof XmlTag) {
900 myTag = (XmlTag)element;
901 } else {
902 final XmlDocument document = myFile.getDocument();
904 if (document != null) {
905 myTag = document.getRootTag();
909 if (myTag != null) {
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
924 return dependencies;
927 static {
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");
964 STD_TYPES.add("ID");
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() {
982 return myTag;
985 public static XmlNSDescriptorImpl getRedefinedElementDescriptor(final XmlTag parentTag) {
986 XmlFile file = getRedefinedElementDescriptorFile(parentTag);
987 if (file != null) {
988 final XmlDocument document = file.getDocument();
989 final PsiMetaData metaData = document != null ? document.getMetaData():null;
990 if (metaData instanceof XmlNSDescriptorImpl) return (XmlNSDescriptorImpl)metaData;
992 return null;
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);
1009 return null;
1012 public boolean hasSubstitutions() {
1013 initSubstitutes();
1014 return mySubstitutions != null && mySubstitutions.size() > 0;
1017 public boolean isValid() {
1018 return myFile != null && getDeclaration().isValid();