public static String findUrl(PsiFile file, int offset, String uri) {
    final PsiElement currentElement = file.findElementAt(offset);
    final XmlAttribute attribute = PsiTreeUtil.getParentOfType(currentElement, XmlAttribute.class);

    if (attribute != null) {
      final XmlTag tag = PsiTreeUtil.getParentOfType(currentElement, XmlTag.class);

      if (tag != null) {
        final String prefix = tag.getPrefixByNamespace(XmlUtil.XML_SCHEMA_INSTANCE_URI);
        if (prefix != null) {
          final String attrValue =
              tag.getAttributeValue(XmlUtil.SCHEMA_LOCATION_ATT, XmlUtil.XML_SCHEMA_INSTANCE_URI);
          if (attrValue != null) {
            final StringTokenizer tokenizer = new StringTokenizer(attrValue);

            while (tokenizer.hasMoreElements()) {
              if (uri.equals(tokenizer.nextToken())) {
                if (!tokenizer.hasMoreElements()) return uri;
                final String url = tokenizer.nextToken();

                return url.startsWith(HTTP_PROTOCOL) ? url : uri;
              }

              if (!tokenizer.hasMoreElements()) return uri;
              tokenizer.nextToken(); // skip file location
            }
          }
        }
      }
    }
    return uri;
  }
 public String[] knownNamespaces() {
   final PsiElement parentElement = getParent();
   BidirectionalMap<String, String> map = initNamespaceMaps(parentElement);
   Set<String> known = Collections.emptySet();
   if (map != null) {
     known = new HashSet<String>(map.values());
   }
   if (parentElement instanceof XmlTag) {
     if (known.isEmpty()) return ((XmlTag) parentElement).knownNamespaces();
     ContainerUtil.addAll(known, ((XmlTag) parentElement).knownNamespaces());
   } else {
     XmlExtension xmlExtension = XmlExtension.getExtensionByElement(this);
     if (xmlExtension != null) {
       final XmlFile xmlFile = xmlExtension.getContainingFile(this);
       if (xmlFile != null) {
         final XmlTag rootTag = xmlFile.getRootTag();
         if (rootTag != null && rootTag != this) {
           if (known.isEmpty()) return rootTag.knownNamespaces();
           ContainerUtil.addAll(known, rootTag.knownNamespaces());
         }
       }
     }
   }
   return ArrayUtil.toStringArray(known);
 }
 @Nullable
 @NonNls
 public String getSubTagText(@NonNls String qname) {
   final XmlTag tag = findFirstSubTag(qname);
   if (tag == null) return null;
   return tag.getValue().getText();
 }
  public XmlNSDescriptor getNSDescriptor(final String namespace, boolean strict) {
    final XmlTag parentTag = getParentTag();

    if (parentTag == null && namespace.equals(XmlUtil.XHTML_URI)) {
      final XmlNSDescriptor descriptor = getDtdDescriptor(XmlUtil.getContainingFile(this));
      if (descriptor != null) {
        return descriptor;
      }
    }

    Map<String, CachedValue<XmlNSDescriptor>> map = initNSDescriptorsMap();
    final CachedValue<XmlNSDescriptor> descriptor = map.get(namespace);
    if (descriptor != null) {
      final XmlNSDescriptor value = descriptor.getValue();
      if (value != null) {
        return value;
      }
    }

    if (parentTag == null) {
      final XmlDocument parentOfType = PsiTreeUtil.getParentOfType(this, XmlDocument.class);
      if (parentOfType == null) {
        return null;
      }
      return parentOfType.getDefaultNSDescriptor(namespace, strict);
    }

    return parentTag.getNSDescriptor(namespace, strict);
  }
 private static XmlTag createTag(
     @NotNull XmlTag contextTag, @NotNull XmlElementDescriptor descriptor) {
   String namespace = getNamespace(descriptor);
   XmlTag tag = contextTag.createChildTag(descriptor.getName(), namespace, null, false);
   PsiElement lastChild = tag.getLastChild();
   assert lastChild != null;
   lastChild.delete(); // remove XML_EMPTY_ELEMENT_END
   return tag;
 }
  @Override
  protected boolean isValidForFile(
      @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
    if (!(file instanceof XmlFile)) return false;

    XmlTag contextTag = getContextTag(editor, file);
    return contextTag != null
        && isInsideTagBody(contextTag, editor)
        && contextTag.getDescriptor() != null;
  }
 @NotNull
 public XmlTag[] findSubTags(final String name, final String namespace) {
   final XmlTag[] subTags = getSubTags();
   final List<XmlTag> result = new ArrayList<XmlTag>();
   for (final XmlTag subTag : subTags) {
     if (namespace == null) {
       if (name.equals(subTag.getName())) result.add(subTag);
     } else if (name.equals(subTag.getLocalName()) && namespace.equals(subTag.getNamespace())) {
       result.add(subTag);
     }
   }
   return ContainerUtil.toArray(result, new XmlTag[result.size()]);
 }
  @Nullable
  protected XmlElementDescriptor computeElementDescriptor() {
    for (XmlElementDescriptorProvider provider :
        Extensions.getExtensions(XmlElementDescriptorProvider.EP_NAME)) {
      XmlElementDescriptor elementDescriptor = provider.getDescriptor(this);
      if (elementDescriptor != null) {
        return elementDescriptor;
      }
    }

    final String namespace = getNamespace();
    if (XmlUtil.EMPTY_URI.equals(namespace)) { // nonqualified items
      final XmlTag parent = getParentTag();
      if (parent != null) {
        final XmlElementDescriptor descriptor = parent.getDescriptor();
        if (descriptor != null) {
          XmlElementDescriptor fromParent = descriptor.getElementDescriptor(this, parent);
          if (fromParent != null && !(fromParent instanceof AnyXmlElementDescriptor)) {
            return fromParent;
          }
        }
      }
    }

    XmlElementDescriptor elementDescriptor = null;
    final XmlNSDescriptor nsDescriptor = getNSDescriptor(namespace, false);

    LOG.debug(
        "Descriptor for namespace "
            + namespace
            + " is "
            + (nsDescriptor != null ? nsDescriptor.getClass().getCanonicalName() : "NULL"));

    if (nsDescriptor != null) {
      if (!DumbService.getInstance(getProject()).isDumb()
          || DumbService.isDumbAware(nsDescriptor)) {
        elementDescriptor = nsDescriptor.getElementDescriptor(this);
      }
    }
    if (elementDescriptor == null) {
      return XmlUtil.findXmlDescriptorByType(this);
    }

    return elementDescriptor;
  }
 public static void generateTag(@NotNull XmlTag newTag, Editor editor) {
   generateRaw(newTag);
   final XmlTag restored = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(newTag);
   if (restored == null) {
     LOG.error("Could not restore tag: " + newTag.getText());
   }
   TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(restored);
   replaceElements(restored, builder);
   builder.run(editor, false);
 }
 private static void replaceElements(XmlTag tag, TemplateBuilder builder) {
   for (XmlAttribute attribute : tag.getAttributes()) {
     XmlAttributeValue value = attribute.getValueElement();
     if (value != null) {
       builder.replaceElement(value, TextRange.from(1, 0), new MacroCallNode(new CompleteMacro()));
     }
   }
   if ("<".equals(tag.getText())) {
     builder.replaceElement(
         tag, TextRange.from(1, 0), new MacroCallNode(new CompleteSmartMacro()));
   } else if (tag.getSubTags().length == 0) {
     int i = tag.getText().indexOf("></");
     if (i > 0) {
       builder.replaceElement(
           tag, TextRange.from(i + 1, 0), new MacroCallNode(new CompleteMacro()));
     }
   }
   for (XmlTag subTag : tag.getSubTags()) {
     replaceElements(subTag, builder);
   }
 }
 private static void generateRaw(final @NotNull XmlTag newTag) {
   XmlElementDescriptor selected = newTag.getDescriptor();
   if (selected == null) return;
   switch (selected.getContentType()) {
     case XmlElementDescriptor.CONTENT_TYPE_EMPTY:
       newTag.collapseIfEmpty();
       ASTNode node = newTag.getNode();
       assert node != null;
       ASTNode elementEnd = node.findChildByType(XmlTokenType.XML_EMPTY_ELEMENT_END);
       if (elementEnd == null) {
         LeafElement emptyTagEnd =
             Factory.createSingleLeafElement(
                 XmlTokenType.XML_EMPTY_ELEMENT_END, "/>", 0, 2, null, newTag.getManager());
         node.addChild(emptyTagEnd);
       }
       break;
     case XmlElementDescriptor.CONTENT_TYPE_MIXED:
       newTag.getValue().setText("");
   }
   for (XmlAttributeDescriptor descriptor : selected.getAttributesDescriptors(newTag)) {
     if (descriptor.isRequired()) {
       newTag.setAttribute(descriptor.getName(), "");
     }
   }
   List<XmlElementDescriptor> tags = getRequiredSubTags(selected);
   for (XmlElementDescriptor descriptor : tags) {
     if (descriptor == null) {
       XmlTag tag =
           XmlElementFactory.getInstance(newTag.getProject())
               .createTagFromText("<", newTag.getLanguage());
       newTag.addSubTag(tag, false);
     } else {
       XmlTag subTag = newTag.addSubTag(createTag(newTag, descriptor), false);
       generateRaw(subTag);
     }
   }
 }
 @Nullable
 private static XmlTag getAnchor(
     @NotNull XmlTag contextTag, Editor editor, XmlElementDescriptor selected) {
   XmlContentDFA contentDFA = XmlContentDFA.getContentDFA(contextTag);
   int offset = editor.getCaretModel().getOffset();
   if (contentDFA == null) {
     return null;
   }
   XmlTag anchor = null;
   boolean previousPositionIsPossible = true;
   for (XmlTag subTag : contextTag.getSubTags()) {
     if (contentDFA.getPossibleElements().contains(selected)) {
       if (subTag.getTextOffset() > offset) {
         break;
       }
       anchor = subTag;
       previousPositionIsPossible = true;
     } else {
       previousPositionIsPossible = false;
     }
     contentDFA.transition(subTag);
   }
   return previousPositionIsPossible ? null : anchor;
 }
  @Override
  public void invoke(
      @NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
    if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return;
    try {
      final XmlTag contextTag = getContextTag(editor, file);
      if (contextTag == null) {
        throw new CommonRefactoringUtil.RefactoringErrorHintException(
            "Caret should be positioned inside a tag");
      }
      XmlElementDescriptor currentTagDescriptor = contextTag.getDescriptor();
      assert currentTagDescriptor != null;
      final XmlElementDescriptor[] descriptors =
          currentTagDescriptor.getElementsDescriptors(contextTag);
      Arrays.sort(
          descriptors,
          new Comparator<XmlElementDescriptor>() {
            @Override
            public int compare(XmlElementDescriptor o1, XmlElementDescriptor o2) {
              return o1.getName().compareTo(o2.getName());
            }
          });
      final JBList list = new JBList(descriptors);
      list.setCellRenderer(new MyListCellRenderer());
      Runnable runnable =
          new Runnable() {
            @Override
            public void run() {
              final XmlElementDescriptor selected = (XmlElementDescriptor) list.getSelectedValue();
              new WriteCommandAction.Simple(project, "Generate XML Tag", file) {
                @Override
                protected void run() {
                  if (selected == null) return;
                  XmlTag newTag = createTag(contextTag, selected);

                  PsiElement anchor = getAnchor(contextTag, editor, selected);
                  if (anchor == null) { // insert it in the cursor position
                    int offset = editor.getCaretModel().getOffset();
                    Document document = editor.getDocument();
                    document.insertString(offset, newTag.getText());
                    PsiDocumentManager.getInstance(project).commitDocument(document);
                    newTag =
                        PsiTreeUtil.getParentOfType(
                            file.findElementAt(offset + 1), XmlTag.class, false);
                  } else {
                    newTag = (XmlTag) contextTag.addAfter(newTag, anchor);
                  }
                  if (newTag != null) {
                    generateTag(newTag, editor);
                  }
                }
              }.execute();
            }
          };
      if (ApplicationManager.getApplication().isUnitTestMode()) {
        XmlElementDescriptor descriptor =
            ContainerUtil.find(
                descriptors,
                new Condition<XmlElementDescriptor>() {
                  @Override
                  public boolean value(XmlElementDescriptor xmlElementDescriptor) {
                    return xmlElementDescriptor.getName().equals(TEST_THREAD_LOCAL.get());
                  }
                });
        list.setSelectedValue(descriptor, false);
        runnable.run();
      } else {
        JBPopupFactory.getInstance()
            .createListPopupBuilder(list)
            .setTitle("Choose Tag Name")
            .setItemChoosenCallback(runnable)
            .setFilteringEnabled(
                new Function<Object, String>() {
                  @Override
                  public String fun(Object o) {
                    return ((XmlElementDescriptor) o).getName();
                  }
                })
            .createPopup()
            .showInBestPositionFor(editor);
      }
    } catch (CommonRefactoringUtil.RefactoringErrorHintException e) {
      HintManager.getInstance().showErrorHint(editor, e.getMessage());
    }
  }
 private static boolean isInsideTagBody(XmlTag contextTag, @NotNull Editor editor) {
   return contextTag.getValue().getTextRange().contains(editor.getCaretModel().getOffset());
 }
 @Override
 public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) {
   final PsiFile containingFile = element.getContainingFile();
   if (!JavaFxFileTypeFactory.isFxml(containingFile)) return;
   if (element instanceof XmlAttributeValue) {
     final String value = ((XmlAttributeValue) element).getValue();
     if (!JavaFxPsiUtil.isExpressionBinding(value)
         && !JavaFxPsiUtil.isIncorrectExpressionBinding(value)) {
       final PsiReference[] references = element.getReferences();
       for (PsiReference reference : references) {
         if (reference instanceof JavaFxColorReference) {
           attachColorIcon(element, holder, StringUtil.unquoteString(element.getText()));
           continue;
         }
         final PsiElement resolve = reference.resolve();
         if (resolve instanceof PsiMember) {
           if (!JavaFxPsiUtil.isVisibleInFxml((PsiMember) resolve)) {
             final String symbolPresentation =
                 "'" + SymbolPresentationUtil.getSymbolPresentableText(resolve) + "'";
             final Annotation annotation =
                 holder.createErrorAnnotation(
                     element,
                     symbolPresentation
                         + (resolve instanceof PsiClass
                             ? " should be public"
                             : " should be public or annotated with @FXML"));
             if (!(resolve instanceof PsiClass)) {
               annotation.registerUniversalFix(
                   new AddAnnotationFix(
                       JavaFxCommonNames.JAVAFX_FXML_ANNOTATION,
                       (PsiMember) resolve,
                       ArrayUtil.EMPTY_STRING_ARRAY),
                   null,
                   null);
             }
           }
         }
       }
     }
   } else if (element instanceof XmlAttribute) {
     final XmlAttribute attribute = (XmlAttribute) element;
     final String attributeName = attribute.getName();
     if (!FxmlConstants.FX_BUILT_IN_ATTRIBUTES.contains(attributeName)
         && !attribute.isNamespaceDeclaration()
         && JavaFxPsiUtil.isReadOnly(attributeName, attribute.getParent())) {
       holder.createErrorAnnotation(
           element.getNavigationElement(), "Property '" + attributeName + "' is read-only");
     }
     if (FxmlConstants.SOURCE.equals(attributeName)) {
       final XmlAttributeValue valueElement = attribute.getValueElement();
       if (valueElement != null) {
         final XmlTag xmlTag = attribute.getParent();
         if (xmlTag != null) {
           final XmlTag referencedTag = JavaFxBuiltInTagDescriptor.getReferencedTag(xmlTag);
           if (referencedTag != null) {
             if (referencedTag.getTextOffset() > xmlTag.getTextOffset()) {
               holder.createErrorAnnotation(
                   valueElement.getValueTextRange(), valueElement.getValue() + " not found");
             } else if (xmlTag.getParentTag() == referencedTag.getParentTag()) {
               final Annotation annotation =
                   holder.createErrorAnnotation(
                       valueElement.getValueTextRange(), "Duplicate child added");
               annotation.registerFix(
                   new JavaFxWrapWithDefineIntention(referencedTag, valueElement.getValue()));
             }
           }
         }
       }
     }
   } else if (element instanceof XmlTag) {
     if (FxmlConstants.FX_SCRIPT.equals(((XmlTag) element).getName())) {
       final XmlTagValue tagValue = ((XmlTag) element).getValue();
       if (!StringUtil.isEmptyOrSpaces(tagValue.getText())) {
         final List<String> langs =
             JavaFxPsiUtil.parseInjectedLanguages((XmlFile) element.getContainingFile());
         if (langs.isEmpty()) {
           final ASTNode openTag = element.getNode().findChildByType(XmlTokenType.XML_NAME);
           final Annotation annotation =
               holder.createErrorAnnotation(
                   openTag != null ? openTag.getPsi() : element, "Page language not specified.");
           annotation.registerFix(new JavaFxInjectPageLanguageIntention());
         }
       }
     }
   }
 }