private static @Nullable XmlTag findSpecialTag(
      @NonNls String name,
      @NonNls String specialName,
      XmlTag rootTag,
      XmlNSDescriptorImpl descriptor,
      HashSet<XmlTag> visited) {
    XmlNSDescriptorImpl nsDescriptor = getNSDescriptorToSearchIn(rootTag, name, descriptor);

    if (nsDescriptor != descriptor) {
      final XmlDocument document =
          nsDescriptor.getDescriptorFile() != null
              ? nsDescriptor.getDescriptorFile().getDocument()
              : null;
      if (document == null) return null;

      return findSpecialTag(
          XmlUtil.findLocalNameByQualifiedName(name),
          specialName,
          document.getRootTag(),
          nsDescriptor,
          visited);
    }

    if (visited == null) visited = new HashSet<XmlTag>(1);
    else if (visited.contains(rootTag)) return null;
    visited.add(rootTag);

    XmlTag[] tags = rootTag.getSubTags();

    return findSpecialTagIn(tags, specialName, name, rootTag, descriptor, visited);
  }
  @Nullable
  protected TypeDescriptor findTypeDescriptorImpl(
      XmlTag rootTag, final String name, String namespace, Set<XmlTag> visited) {
    XmlNSDescriptorImpl responsibleDescriptor = this;
    if (namespace != null && namespace.length() != 0 && !namespace.equals(getDefaultNamespace())) {
      final XmlNSDescriptor nsDescriptor = rootTag.getNSDescriptor(namespace, true);

      if (nsDescriptor instanceof XmlNSDescriptorImpl) {
        responsibleDescriptor = (XmlNSDescriptorImpl) nsDescriptor;
      }
    }

    if (responsibleDescriptor != this) {
      return responsibleDescriptor.findTypeDescriptor(XmlUtil.findLocalNameByQualifiedName(name));
    }

    if (rootTag == null) return null;
    if (visited != null) {
      if (visited.contains(rootTag)) return null;
      visited.add(rootTag);
    }

    final Pair<QNameKey, XmlTag> pair =
        new Pair<QNameKey, XmlTag>(new QNameKey(name, namespace), rootTag);

    final CachedValue<TypeDescriptor> descriptor = myTypesMap.get(pair);
    if (descriptor != null) {
      TypeDescriptor value = descriptor.getValue();
      if (value == null
          || (value instanceof ComplexTypeDescriptor
              && ((ComplexTypeDescriptor) value).getDeclaration().isValid())) return value;
    }

    XmlTag[] tags = rootTag.getSubTags();

    if (visited == null) {
      visited = new HashSet<XmlTag>(1);
      visited.add(rootTag);
    }

    return doFindIn(tags, name, namespace, pair, rootTag, visited);
  }
  private static XmlTag findSpecialTagIn(
      final XmlTag[] tags,
      final String specialName,
      final String name,
      final XmlTag rootTag,
      final XmlNSDescriptorImpl descriptor,
      final HashSet<XmlTag> visited) {
    for (XmlTag tag : tags) {
      if (equalsToSchemaName(tag, specialName)) {
        String attribute = tag.getAttributeValue("name");

        if (name.equals(attribute)
            || name.indexOf(":") >= 0 && name.substring(name.indexOf(":") + 1).equals(attribute)) {
          return tag;
        }
      } else if (equalsToSchemaName(tag, INCLUDE_TAG_NAME)
          || (equalsToSchemaName(tag, IMPORT_TAG_NAME)
              && rootTag
                  .getNamespaceByPrefix(XmlUtil.findPrefixByQualifiedName(name))
                  .equals(tag.getAttributeValue("namespace")))) {
        final String schemaLocation = tag.getAttributeValue("schemaLocation");

        if (schemaLocation != null) {
          final XmlFile xmlFile =
              XmlUtil.findNamespaceByLocation(rootTag.getContainingFile(), schemaLocation);

          if (xmlFile != null) {
            final XmlDocument document = xmlFile.getDocument();
            if (document != null) {
              final XmlTag rTag =
                  findSpecialTag(name, specialName, document.getRootTag(), descriptor, visited);

              if (rTag != null) return rTag;
            }
          }
        }
      } else if (equalsToSchemaName(tag, REDEFINE_TAG_NAME)) {
        XmlTag rTag =
            findSpecialTagIn(tag.getSubTags(), specialName, name, rootTag, descriptor, visited);
        if (rTag != null) return rTag;

        final XmlNSDescriptorImpl nsDescriptor = getRedefinedElementDescriptor(tag);
        if (nsDescriptor != null) {
          final XmlTag redefinedRootTag =
              ((XmlDocument) nsDescriptor.getDeclaration()).getRootTag();

          rTag =
              findSpecialTagIn(
                  redefinedRootTag.getSubTags(),
                  specialName,
                  name,
                  redefinedRootTag,
                  nsDescriptor,
                  visited);
          if (rTag != null) return rTag;
        }
      }
    }

    return null;
  }
  private TypeDescriptor doFindIn(
      final XmlTag[] tags,
      final String name,
      final String namespace,
      final Pair<QNameKey, XmlTag> pair,
      final XmlTag rootTag,
      final Set<XmlTag> visited) {
    for (final XmlTag tag : tags) {
      if (equalsToSchemaName(tag, "complexType")) {
        if (name == null) {
          CachedValue<TypeDescriptor> value = createAndPutTypesCachedValue(tag, pair);
          return value.getValue();
        }

        String nameAttribute = tag.getAttributeValue("name");

        if (isSameName(name, namespace, nameAttribute)) {
          CachedValue<TypeDescriptor> cachedValue = createAndPutTypesCachedValue(tag, pair);
          return cachedValue.getValue();
        }
      } else if (equalsToSchemaName(tag, "simpleType")) {

        if (name == null) {
          CachedValue<TypeDescriptor> value = createAndPutTypesCachedValueSimpleType(tag, pair);
          return value.getValue();
        }

        String nameAttribute = tag.getAttributeValue("name");

        if (isSameName(name, namespace, nameAttribute)) {
          CachedValue<TypeDescriptor> cachedValue = createAndPutTypesCachedValue(tag, pair);
          return cachedValue.getValue();
        }
      } else if (equalsToSchemaName(tag, INCLUDE_TAG_NAME)
          || (equalsToSchemaName(tag, IMPORT_TAG_NAME)
              && (namespace == null || !namespace.equals(getDefaultNamespace())))) {
        final String schemaLocation = tag.getAttributeValue("schemaLocation");
        if (schemaLocation != null) {
          final XmlFile xmlFile =
              XmlUtil.findNamespaceByLocation(rootTag.getContainingFile(), schemaLocation);

          if (xmlFile != null) {
            final XmlDocument document = xmlFile.getDocument();

            if (document != null) {

              final CachedValue<TypeDescriptor> value =
                  CachedValuesManager.getManager(tag.getProject())
                      .createCachedValue(
                          new CachedValueProvider<TypeDescriptor>() {
                            public Result<TypeDescriptor> compute() {
                              final String currentName = tag.getAttributeValue("name");

                              if ((currentName != null
                                      && !currentName.equals(
                                          XmlUtil.findLocalNameByQualifiedName(name)))
                                  || !xmlFile.isValid()
                                  || xmlFile.getDocument() == null) {
                                myTypesMap.remove(pair);
                                return new Result<TypeDescriptor>(null);
                              }

                              final XmlDocument document = xmlFile.getDocument();
                              final XmlNSDescriptorImpl nsDescriptor =
                                  findNSDescriptor(tag, document);

                              if (nsDescriptor == null) {
                                myTypesMap.remove(pair);
                                return new Result<TypeDescriptor>(null);
                              }

                              final XmlTag rTag = document.getRootTag();

                              final TypeDescriptor complexTypeDescriptor =
                                  nsDescriptor.findTypeDescriptorImpl(
                                      rTag, name, namespace, visited);
                              return new Result<TypeDescriptor>(complexTypeDescriptor, rTag);
                            }
                          },
                          false);

              if (value.getValue() != null) {
                myTypesMap.put(pair, value);
                return value.getValue();
              }
            }
          }
        }
      } else if (equalsToSchemaName(tag, REDEFINE_TAG_NAME)) {
        final XmlTag[] subTags = tag.getSubTags();
        TypeDescriptor descriptor = doFindIn(subTags, name, namespace, pair, rootTag, visited);
        if (descriptor != null) return descriptor;

        final XmlNSDescriptorImpl nsDescriptor = getRedefinedElementDescriptor(tag);
        if (nsDescriptor != null) {
          final XmlTag redefinedRootTag =
              ((XmlDocument) nsDescriptor.getDeclaration()).getRootTag();
          descriptor =
              doFindIn(
                  redefinedRootTag.getSubTags(), name, namespace, pair, redefinedRootTag, visited);
          if (descriptor != null) return descriptor;
        }
      }
    }
    return null;
  }
  @Nullable
  private XmlAttributeDescriptor getAttributeImpl(
      String localName, String namespace, Set<XmlTag> visited) {
    if (myTag == null) return null;

    XmlNSDescriptorImpl nsDescriptor = (XmlNSDescriptorImpl) myTag.getNSDescriptor(namespace, true);

    if (nsDescriptor != this && nsDescriptor != null) {
      return nsDescriptor.getAttributeImpl(localName, namespace, visited);
    }

    if (visited == null) visited = new HashSet<XmlTag>(1);
    else if (visited.contains(myTag)) return null;
    visited.add(myTag);
    XmlTag[] tags = myTag.getSubTags();

    for (XmlTag tag : tags) {
      if (equalsToSchemaName(tag, ATTRIBUTE_TAG_NAME)) {
        String name = tag.getAttributeValue("name");

        if (name != null) {
          if (checkElementNameEquivalence(localName, namespace, name, tag)) {
            return createAttributeDescriptor(tag);
          }
        }
      } else if (equalsToSchemaName(tag, INCLUDE_TAG_NAME)
          || (equalsToSchemaName(tag, IMPORT_TAG_NAME)
              && namespace.equals(tag.getAttributeValue("namespace")))) {
        final XmlAttribute schemaLocation = tag.getAttribute("schemaLocation", null);

        if (schemaLocation != null) {
          final XmlFile xmlFile =
              XmlUtil.findNamespaceByLocation(myTag.getContainingFile(), schemaLocation.getValue());

          if (xmlFile != null) {

            final XmlDocument includedDocument = xmlFile.getDocument();
            if (includedDocument != null) {
              final PsiMetaData data = includedDocument.getMetaData();

              if (data instanceof XmlNSDescriptorImpl) {
                final XmlAttributeDescriptor attributeDescriptor =
                    ((XmlNSDescriptorImpl) data).getAttributeImpl(localName, namespace, visited);

                if (attributeDescriptor != null) {
                  final CachedValue<XmlAttributeDescriptor> value =
                      CachedValuesManager.getManager(includedDocument.getProject())
                          .createCachedValue(
                              new CachedValueProvider<XmlAttributeDescriptor>() {
                                public Result<XmlAttributeDescriptor> compute() {
                                  return new Result<XmlAttributeDescriptor>(
                                      attributeDescriptor, attributeDescriptor.getDependences());
                                }
                              },
                              false);
                  return value.getValue();
                }
              }
            }
          }
        }
      }
    }

    return null;
  }
  @Nullable
  public XmlElementDescriptor getElementDescriptor(
      String localName, String namespace, Set<XmlNSDescriptorImpl> visited, boolean reference) {
    if (visited.contains(this)) return null;

    final QNameKey pair = new QNameKey(namespace, localName);
    final CachedValue<XmlElementDescriptor> descriptor = myDescriptorsMap.get(pair);
    if (descriptor != null) {
      final XmlElementDescriptor value = descriptor.getValue();
      if (value == null || value.getDeclaration().isValid()) return value;
    }

    final XmlTag rootTag = myTag;
    if (rootTag == null) return null;
    XmlTag[] tags = rootTag.getSubTags();
    visited.add(this);

    for (final XmlTag tag : tags) {
      if (equalsToSchemaName(tag, ELEMENT_TAG_NAME)) {
        String name = tag.getAttributeValue("name");

        if (name != null) {
          if (checkElementNameEquivalence(localName, namespace, name, tag)) {
            final CachedValue<XmlElementDescriptor> cachedValue =
                CachedValuesManager.getManager(tag.getProject())
                    .createCachedValue(
                        new CachedValueProvider<XmlElementDescriptor>() {
                          public Result<XmlElementDescriptor> compute() {
                            final String name = tag.getAttributeValue("name");

                            if (name != null && !name.equals(pair.second)) {
                              myDescriptorsMap.remove(pair);
                              return new Result<XmlElementDescriptor>(null);
                            }
                            final XmlElementDescriptor xmlElementDescriptor =
                                createElementDescriptor(tag);
                            return new Result<XmlElementDescriptor>(
                                xmlElementDescriptor, xmlElementDescriptor.getDependences());
                          }
                        },
                        false);
            myDescriptorsMap.put(pair, cachedValue);
            return cachedValue.getValue();
          }
        }
      } else if (equalsToSchemaName(tag, INCLUDE_TAG_NAME)
          || (reference
              && equalsToSchemaName(tag, IMPORT_TAG_NAME)
              && (namespace.equals(tag.getAttributeValue("namespace"))
                  || namespace.length() == 0 && tag.getAttributeValue("namespace") == null))) {
        final XmlAttribute schemaLocation = tag.getAttribute("schemaLocation", null);
        if (schemaLocation != null) {
          final XmlFile xmlFile =
              XmlUtil.findNamespaceByLocation(
                  rootTag.getContainingFile(), schemaLocation.getValue());
          if (xmlFile != null) {
            final XmlDocument includedDocument = xmlFile.getDocument();
            if (includedDocument != null) {
              final PsiMetaData data = includedDocument.getMetaData();
              if (data instanceof XmlNSDescriptorImpl) {
                final XmlElementDescriptor elementDescriptor =
                    ((XmlNSDescriptorImpl) data)
                        .getElementDescriptor(localName, namespace, visited, reference);
                if (elementDescriptor != null) {
                  // final CachedValue<XmlElementDescriptor> value =
                  // includedDocument.getManager().getCachedValuesManager()
                  //  .createCachedValue(new CachedValueProvider<XmlElementDescriptor>() {
                  //    public Result<XmlElementDescriptor> compute() {
                  //      return new Result<XmlElementDescriptor>(elementDescriptor,
                  // elementDescriptor.getDependences());
                  //    }
                  //  }, false);
                  // return value.getValue();
                  return elementDescriptor;
                }
              }
            }
          }
        }
      } else if (equalsToSchemaName(tag, REDEFINE_TAG_NAME)) {
        final XmlNSDescriptorImpl nsDescriptor = getRedefinedElementDescriptor(tag);
        if (nsDescriptor != null) {
          final XmlElementDescriptor xmlElementDescriptor =
              nsDescriptor.getElementDescriptor(localName, namespace, visited, reference);
          if (xmlElementDescriptor != null) return xmlElementDescriptor;
        }
      }
    }

    return null;
  }