@Override
  public void visitXmlToken(XmlToken token) {
    IElementType tokenType = token.getTokenType();
    if (tokenType == XmlTokenType.XML_NAME || tokenType == XmlTokenType.XML_TAG_NAME) {
      PsiElement element = token.getPrevSibling();
      while (element instanceof PsiWhiteSpace) element = element.getPrevSibling();

      if (element instanceof XmlToken) {
        if (((XmlToken) element).getTokenType() == XmlTokenType.XML_START_TAG_START) {
          PsiElement parent = element.getParent();

          if (parent instanceof XmlTag
              && !(token.getNextSibling() instanceof OuterLanguageElement)) {
            checkTag((XmlTag) parent);
          }
        }
      } else {
        PsiElement parent = token.getParent();

        if (parent instanceof XmlAttribute
            && !(token.getNextSibling() instanceof OuterLanguageElement)) {
          checkAttribute((XmlAttribute) parent);
        }
      }
    } else if (tokenType == XmlTokenType.XML_DATA_CHARACTERS
        && token.getParent() instanceof XmlText) {
      if (token.textContains(']') && token.textContains('>')) {
        String s = token.getText();
        String marker = "]]>";
        int i = s.indexOf(marker);

        if (i != -1) { // TODO: fix
          TextRange textRange = token.getTextRange();
          int start = textRange.getStartOffset() + i;
          HighlightInfoType type =
              PsiTreeUtil.getParentOfType(token, XmlTag.class) instanceof HtmlTag
                  ? HighlightInfoType.WARNING
                  : HighlightInfoType.ERROR;
          HighlightInfo info =
              HighlightInfo.createHighlightInfo(
                  type,
                  start,
                  start + marker.length(),
                  XmlErrorMessages.message(
                      "cdata.end.should.not.appear.in.content.unless.to.mark.end.of.cdata.section"));
          addToResults(info);
        }
      }
    }
  }
  /** {@inheritDoc} */
  public void skippedEntity(String text) throws SAXException {

    // XmlToken
    XmlToken token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(text);
    token.setType(XmlTokenTypes.SKIPPED_ENTITY);

    // Node
    DetailAST child = new DetailAST();
    child.initialize(token);
    child.setText(token.getText());

    // Parent
    currentNode.addChild(child);
  }
  /** {@inheritDoc} */
  public void ignorableWhitespace(char[] ch, int start, int end) throws SAXException {

    String value = new String(ch, start, end);

    LOG.debug("ignorable whitespace : ..." + value + "...");

    // XmlToken
    XmlToken token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber() + start);
    token.setText(value);
    token.setType(XmlTokenTypes.WHITE_SPACE);

    // Node
    DetailAST child = new DetailAST();
    child.initialize(token);
    child.setText(token.getText());

    // Parent
    currentNode.addChild(child);
  }
  /** {@inheritDoc} */
  public void characters(char[] ch, int start, int end) throws SAXException {

    String value = new String(ch, start, end);

    LOG.debug("#PCDATA : " + value);

    // XmlToken
    XmlToken token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber() + start);
    token.setText(value);
    token.setType(XmlTokenTypes.PCDATA);

    // Node
    DetailAST child = new DetailAST();
    child.initialize(token);
    child.setText(token.getText());

    // Parent
    currentNode.addChild(child);
  }
Пример #5
0
  public Result beforeCharTyped(
      final char c,
      final Project project,
      final Editor editor,
      final PsiFile editedFile,
      final FileType fileType) {
    final XmlEditorOptions xmlEditorOptions = XmlEditorOptions.getInstance();
    if (c == '>'
        && xmlEditorOptions != null
        && xmlEditorOptions.isAutomaticallyInsertClosingTag()
        && (editedFile.getLanguage() instanceof XMLLanguage
            || editedFile.getViewProvider().getBaseLanguage() instanceof XMLLanguage)) {
      PsiDocumentManager.getInstance(project).commitAllDocuments();

      PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
      FileViewProvider provider = editedFile.getViewProvider();
      int offset = editor.getCaretModel().getOffset();

      PsiElement element, elementAtCaret = null;

      if (offset < editor.getDocument().getTextLength()) {
        elementAtCaret = element = provider.findElementAt(offset, XMLLanguage.class);

        if (!(element instanceof PsiWhiteSpace)) {
          boolean nonAcceptableDelimiter = true;

          if (element instanceof XmlToken) {
            IElementType tokenType = ((XmlToken) element).getTokenType();

            if (tokenType == XmlTokenType.XML_START_TAG_START
                || tokenType == XmlTokenType.XML_END_TAG_START) {
              if (offset > 0) {
                PsiElement previousElement = provider.findElementAt(offset - 1, XMLLanguage.class);

                if (previousElement instanceof XmlToken) {
                  tokenType = ((XmlToken) previousElement).getTokenType();
                  element = previousElement;
                  nonAcceptableDelimiter = false;
                }
              }
            } else if (tokenType == XmlTokenType.XML_NAME) {
              if (element.getNextSibling() instanceof PsiErrorElement) {
                nonAcceptableDelimiter = false;
              }
            }

            if (tokenType == XmlTokenType.XML_TAG_END
                || tokenType == XmlTokenType.XML_EMPTY_ELEMENT_END
                    && element.getTextOffset() == offset - 1) {
              editor.getCaretModel().moveToOffset(offset + 1);
              editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
              return Result.STOP;
            }
          }
          if (nonAcceptableDelimiter) return Result.CONTINUE;
        } else {
          // check if right after empty end
          PsiElement previousElement = provider.findElementAt(offset - 1, XMLLanguage.class);
          if (previousElement instanceof XmlToken) {
            final IElementType tokenType = ((XmlToken) previousElement).getTokenType();

            if (tokenType == XmlTokenType.XML_EMPTY_ELEMENT_END) {
              return Result.STOP;
            }
          }
        }

        PsiElement parent = element.getParent();
        if (parent instanceof XmlText) {
          final String text = parent.getText();
          // check /
          final int index = offset - parent.getTextOffset() - 1;

          if (index >= 0 && text.charAt(index) == '/') {
            return Result.CONTINUE; // already seen /
          }
          element = parent.getPrevSibling();
        } else if (parent instanceof XmlTag && !(element.getPrevSibling() instanceof XmlTag)) {
          element = parent;
        } else if (parent instanceof XmlAttributeValue) {
          element = parent;
        }
      } else {
        element =
            provider.findElementAt(editor.getDocument().getTextLength() - 1, XMLLanguage.class);
        if (element == null) return Result.CONTINUE;
        element = element.getParent();
      }

      if (offset > 0 && offset <= editor.getDocument().getTextLength()) {
        if (editor.getDocument().getCharsSequence().charAt(offset - 1)
            == '/') { // Some languages (e.g. GSP) allow character '/' in tag name.
          return Result.CONTINUE;
        }
      }

      if (element instanceof XmlAttributeValue) {
        element = element.getParent().getParent();
      }

      while (element instanceof PsiWhiteSpace) element = element.getPrevSibling();
      if (element instanceof XmlDocument) { // hack for closing tags in RHTML
        element = element.getLastChild();
      }
      if (element == null) return Result.CONTINUE;
      if (!(element instanceof XmlTag)) {
        if (element instanceof XmlTokenImpl
            && element.getPrevSibling() != null
            && element.getPrevSibling().getText().equals("<")) {
          // tag is started and there is another text in the end
          editor.getDocument().insertString(offset, "</" + element.getText() + ">");
        }
        return Result.CONTINUE;
      }

      XmlTag tag = (XmlTag) element;
      if (XmlUtil.getTokenOfType(tag, XmlTokenType.XML_TAG_END) != null) return Result.CONTINUE;
      if (XmlUtil.getTokenOfType(tag, XmlTokenType.XML_EMPTY_ELEMENT_END) != null)
        return Result.CONTINUE;
      final XmlToken startToken = XmlUtil.getTokenOfType(tag, XmlTokenType.XML_START_TAG_START);
      if (startToken == null || !startToken.getText().equals("<")) return Result.CONTINUE;

      String name = tag.getName();
      if (elementAtCaret instanceof XmlToken
          && ((XmlToken) elementAtCaret).getTokenType() == XmlTokenType.XML_NAME) {
        name = name.substring(0, offset - elementAtCaret.getTextOffset());
      }
      if (tag instanceof HtmlTag && HtmlUtil.isSingleHtmlTag(name)) return Result.CONTINUE;
      if ("".equals(name)) return Result.CONTINUE;

      int tagOffset = tag.getTextRange().getStartOffset();

      final XmlToken nameToken = XmlUtil.getTokenOfType(tag, XmlTokenType.XML_NAME);
      if (nameToken != null && nameToken.getTextRange().getStartOffset() > offset)
        return Result.CONTINUE;

      HighlighterIterator iterator = ((EditorEx) editor).getHighlighter().createIterator(tagOffset);
      if (BraceMatchingUtil.matchBrace(
          editor.getDocument().getCharsSequence(),
          editedFile.getFileType(),
          iterator,
          true,
          true)) {
        PsiElement parent = tag.getParent();
        boolean hasBalance = true;

        while (parent instanceof XmlTag && name.equals(((XmlTag) parent).getName())) {
          ASTNode astNode = XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(parent.getNode());
          if (astNode == null) {
            hasBalance = false;
            break;
          }

          parent = parent.getParent();
        }

        if (hasBalance) {
          hasBalance = false;
          for (ASTNode node = parent.getNode().getLastChildNode();
              node != null;
              node = node.getTreePrev()) {
            ASTNode leaf = node;
            if (leaf.getElementType() == TokenType.ERROR_ELEMENT) {
              ASTNode firstChild = leaf.getFirstChildNode();
              if (firstChild != null) leaf = firstChild;
              else {
                PsiElement psiElement = PsiTreeUtil.nextLeaf(leaf.getPsi());
                leaf = psiElement != null ? psiElement.getNode() : null;
              }
              if (leaf != null && leaf.getElementType() == TokenType.WHITE_SPACE) {
                PsiElement psiElement = PsiTreeUtil.nextLeaf(leaf.getPsi());
                if (psiElement != null) leaf = psiElement.getNode();
              }
            }

            if (leaf != null && leaf.getElementType() == XmlTokenType.XML_END_TAG_START) {
              ASTNode treeNext = leaf.getTreeNext();
              IElementType treeNextType;
              if (treeNext != null
                  && ((treeNextType = treeNext.getElementType()) == XmlTokenType.XML_NAME
                      || treeNextType == XmlTokenType.XML_TAG_NAME)) {
                if (name.equals(treeNext.getText())) {
                  ASTNode parentEndName =
                      parent instanceof XmlTag
                          ? XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(parent.getNode())
                          : null;
                  hasBalance =
                      !(parent instanceof XmlTag)
                          || parentEndName != null && !parentEndName.getText().equals(name);
                  break;
                }
              }
            }
          }
        }

        if (hasBalance) return Result.CONTINUE;
      }

      TextRange cdataReformatRange = null;
      final XmlElementDescriptor descriptor = tag.getDescriptor();

      if (descriptor instanceof XmlElementDescriptorWithCDataContent) {
        final XmlElementDescriptorWithCDataContent cDataContainer =
            (XmlElementDescriptorWithCDataContent) descriptor;

        if (cDataContainer.requiresCdataBracesInContext(tag)) {
          int rangeStart = offset;
          @NonNls final String cDataStart = "><![CDATA[";
          final String inserted = cDataStart + "\n]]>";
          editor.getDocument().insertString(offset, inserted);
          final int newoffset = offset + cDataStart.length();
          editor.getCaretModel().moveToOffset(newoffset);
          offset += inserted.length();
          cdataReformatRange = new TextRange(rangeStart, offset + 1);
        }
      }

      editor.getDocument().insertString(offset, "</" + name + ">");

      if (cdataReformatRange != null) {
        PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
        try {
          CodeStyleManager.getInstance(project)
              .reformatText(
                  file, cdataReformatRange.getStartOffset(), cdataReformatRange.getEndOffset());
        } catch (IncorrectOperationException e) {
          LOG.error(e);
        }
      }
      return cdataReformatRange != null ? Result.STOP : Result.CONTINUE;
    }
    return Result.CONTINUE;
  }
  /** {@inheritDoc} */
  @Override
  public void startDocument() throws SAXException {
    LOG.debug("Starting XML parsing");

    // XmlToken
    XmlToken token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setType(XmlTokenTypes.DOCUMENT);
    token.setText(file.getName());

    // Node
    root = new DetailAST();
    root.initialize(token);

    currentNode = root;

    // Path = package
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setType(XmlTokenTypes.PATH);
    token.setText(file.getParent());
    DetailAST path = new DetailAST();
    path.initialize(token);
    root.addChild(path);
    DetailAST pathIdent = new DetailAST();
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setType(XmlTokenTypes.IDENT);
    token.setText(file.getParent());
    pathIdent.initialize(token);
    path.addChild(pathIdent);

    // Fake child to match Java grammar structure
    pathIdent = new DetailAST();
    pathIdent.initialize(token);
    path.addChild(pathIdent);

    // Name = Type
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setType(XmlTokenTypes.IDENT);
    token.setText(file.getName());
    DetailAST name = new DetailAST();
    name.initialize(token);
    root.addChild(name);
    DetailAST nameIdent = new DetailAST();
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setType(XmlTokenTypes.IDENT);
    token.setText(file.getName());
    nameIdent.initialize(token);
    name.addChild(nameIdent);
  }
  /** {@inheritDoc} */
  public void processingInstruction(String target, String data) throws SAXException {
    LOG.debug("Processing instruction : " + target);
    LOG.debug("  Args : " + data);

    // XmlToken
    XmlToken token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(target);
    token.setType(XmlTokenTypes.PROCESSING_INSTRUCTION);

    // Node
    DetailAST child = new DetailAST();
    child.initialize(token);
    child.setText(token.getText());

    // Target
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(target);
    token.setType(XmlTokenTypes.PROCESSING_TARGET);

    DetailAST targetAST = new DetailAST();
    targetAST.initialize(token);
    targetAST.setText(token.getText());
    child.addChild(targetAST);

    // Data
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(data);
    token.setType(XmlTokenTypes.PROCESSING_DATA);

    DetailAST dataAST = new DetailAST();
    dataAST.initialize(token);
    dataAST.setText(token.getText());
    child.addChild(dataAST);

    // Parent
    currentNode.addChild(child);
  }
  /** {@inheritDoc} */
  public void startElement(
      String nameSpaceURI, String localName, String rawName, Attributes attributs)
      throws SAXException {
    LOG.debug("Opening element : " + localName);

    int col = locator.getColumnNumber();

    // XmlToken
    XmlToken token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(rawName);
    token.setType(XmlTokenTypes.ELEMENT);

    // Node
    DetailAST child = new DetailAST();
    child.initialize(token);

    // Parent
    currentNode.addChild(child);

    // Ident
    DetailAST ident = new DetailAST();
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    col++;
    token.setColumn(col);
    token.setText(localName);
    token.setType(XmlTokenTypes.IDENT);
    ident.initialize(token);
    child.addChild(ident);

    // Attributes
    LOG.debug("  Attributes : ");
    DetailAST attrs = new DetailAST();
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    col += rawName.length() + 1;
    token.setColumn(col);
    token.setText(rawName);
    token.setType(XmlTokenTypes.ATTRIBUTES);
    attrs.initialize(token);
    child.addChild(attrs);

    for (int index = 0; index < attributs.getLength(); index++) {

      // Attribut
      DetailAST attr = new DetailAST();
      token = new XmlToken();
      token.setLine(locator.getLineNumber());
      token.setColumn(index);
      token.setText(rawName);
      token.setType(XmlTokenTypes.ATTRIBUTE);
      attr.initialize(token);
      attrs.addChild(attr);
      LOG.debug("     - " + attributs.getLocalName(index) + " = " + attributs.getValue(index));

      // Ident
      ident = new DetailAST();
      token = new XmlToken();
      token.setLine(locator.getLineNumber());
      token.setColumn(index);
      token.setText(attributs.getLocalName(index));
      token.setType(XmlTokenTypes.IDENT);
      ident.initialize(token);
      attr.addChild(ident);

      // Value
      DetailAST value = new DetailAST();
      token = new XmlToken();
      token.setLine(locator.getLineNumber());
      token.setColumn(index + attributs.getLocalName(index).length() + 1);
      token.setText(attributs.getValue(index));
      token.setType(XmlTokenTypes.STRING_LITERAL);
      value.initialize(token);
      attr.addChild(value);

      // Go forward
      col += attributs.getLocalName(index).length() + attributs.getValue(index).length() + 2;
    }

    // This node is now he current node
    currentNode = child;
  }
  /** {@inheritDoc} */
  public void startPrefixMapping(String prefix, String URI) throws SAXException {
    LOG.debug("Prefix mapping : " + URI + ", chosen prefix : " + prefix);

    // XmlToken
    XmlToken token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(prefix);
    token.setType(XmlTokenTypes.PREFIX_MAPPING);

    // Node
    DetailAST child = new DetailAST();
    child.initialize(token);

    // Prefix
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(prefix);
    token.setType(XmlTokenTypes.IDENT);

    DetailAST prefixAST = new DetailAST();
    prefixAST.initialize(token);
    prefixAST.setText(token.getText());

    child.addChild(prefixAST);

    // URI
    token = new XmlToken();
    token.setLine(locator.getLineNumber());
    token.setColumn(locator.getColumnNumber());
    token.setText(URI);
    token.setType(XmlTokenTypes.STRING_LITERAL);

    DetailAST uri = new DetailAST();
    uri.initialize(token);

    child.addChild(uri);

    // Parent
    currentNode.addChild(child);
  }