/*
 * Copyright 2002-2005 Sascha Weinreuter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.intellij.plugins.xpathView.util;

import com.intellij.codeInsight.daemon.impl.AnnotationHolderImpl;
import com.intellij.lang.ASTNode;
import com.intellij.lang.LanguageAnnotators;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationSession;
import com.intellij.lang.annotation.Annotator;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.xml.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class MyPsiUtil {
    private static final Logger LOG = Logger.getInstance(MyPsiUtil.class);

    private MyPsiUtil() {
    }

    @Nullable
    public static XmlElement findContextNode(@NotNull PsiFile psiFile, @NotNull Editor editor) {
        PsiElement contextNode = psiFile.findElementAt(editor.getCaretModel().getOffset());
        while (contextNode != null && !isValidContextNode(contextNode)) {
            contextNode = contextNode.getParent();
        }

        assert contextNode == null || contextNode instanceof XmlElement;
        return (XmlElement)contextNode;
    }

    public static boolean isValidContextNode(@Nullable PsiElement contextNode) {
        return contextNode instanceof XmlTag || contextNode instanceof XmlDocument;
    }

    @NotNull
    public static PsiElement getNameElement(@NotNull XmlTag tag) {
        final PsiElement element = findNameElement(tag);
        if (element != null) {
            return element;
        }
        LOG.error("Name element not found for " + tag);
        return tag;
    }

    @Nullable
    public static PsiElement findNameElement(@NotNull XmlTag tag) {
        PsiElement[] children = tag.getChildren();
        for (PsiElement child : children) {
            if (isNameElement(child)) {
                return child;
            }
        }
        return null;
    }

    public static boolean isNameElement(@Nullable PsiElement child) {
      if (child != null) {
        if (child.getParent() instanceof XmlTag) {
          if (child instanceof XmlToken) {
            if (((XmlToken)child).getTokenType() == XmlTokenType.XML_NAME) {
              return true;
            }
          } else if (child instanceof ASTNode) {
            return ((ASTNode)child).getElementType() == XmlTokenType.XML_NAME;
          }
        }
      }
      return false;
    }

    public static String getAttributePrefix(@NotNull XmlAttribute attribute) {
        final String name = attribute.getName();
        if (name.indexOf(':') == -1) {
            return "";
        } else {
            return name.substring(0, name.indexOf(':'));
        }
    }

    public static boolean isStartTag(PsiElement contextNode) {
        if (contextNode instanceof PsiWhiteSpace) {
            PsiElement sibling = contextNode.getPrevSibling();
            while (sibling != null && !isNameElement(sibling)) {
                sibling = sibling.getPrevSibling();
            }
            return sibling != null;
        } else if (contextNode instanceof XmlToken) {
            if (((XmlToken)contextNode).getTokenType() == XmlTokenType.XML_START_TAG_START) return true;
            if (((XmlToken)contextNode).getTokenType() == XmlTokenType.XML_TAG_END) return true;
            if (((XmlToken)contextNode).getTokenType() == XmlTokenType.XML_EMPTY_ELEMENT_END) return true;
        }
        return false;
    }
    public static boolean isEndTag(PsiElement contextNode) {
        if (contextNode instanceof PsiWhiteSpace) {
            PsiElement sibling = contextNode.getPrevSibling();
            while (sibling != null && !isNameElement(sibling)) {
                sibling = sibling.getPrevSibling();
            }
            return sibling != null;
        } else if (contextNode instanceof XmlToken) {
            if (((XmlToken)contextNode).getTokenType() == XmlTokenType.XML_END_TAG_START) return true;
        }
        return false;
    }

    /**
     * This method checks if the passed element's namespace is actually declared in the document or if has an
     * implicit namespace URI which as of late, IDEA assigns to Ant files, Web descriptors and anything that has a
     * DTD defined. For XPath queries this is very inconvenient when having to enter a namespace-prefix with every
     * element-step. For XPath-Expression generation this results in more complex expressions than necessary.
     */
    public static boolean isInDeclaredNamespace(XmlTag context, String nsUri, String nsPrefix) {

        if (nsUri == null || nsUri.length() == 0 || nsPrefix != null && nsPrefix.length() > 0) {
            return true;
        }

        do {
            if (context.getLocalNamespaceDeclarations().containsValue(nsUri)) return true;
            context = (XmlTag)(context.getParent() instanceof XmlTag ? context.getParent() : null);
        } while (context != null);

        return false;
    }

    public static String checkFile(final PsiFile file) {
        final String[] error = new String[1];
        file.accept(new PsiRecursiveElementVisitor() {
            @Override
            public void visitErrorElement(PsiErrorElement element) {
                error[0] = element.getErrorDescription();
            }
        });
        if (error[0] != null) return error[0];

        final Annotator annotator = LanguageAnnotators.INSTANCE.forLanguage(file.getLanguage());
        file.accept(new PsiRecursiveElementVisitor() {
            @Override
            public void visitElement(PsiElement element) {
                annotator.annotate(element, new AnnotationHolderImpl(new AnnotationSession(file)) {
                    @Override
                    public Annotation createErrorAnnotation(@NotNull ASTNode astNode, String string) {
                        error[0] = string;
                        return super.createErrorAnnotation(astNode, string);
                    }

                    @Override
                    public Annotation createErrorAnnotation(@NotNull PsiElement element, String string) {
                        error[0] = string;
                        return super.createErrorAnnotation(element, string);
                    }

                    @Override
                    public Annotation createErrorAnnotation(@NotNull TextRange textRange, String string) {
                        error[0] = string;
                        return super.createErrorAnnotation(textRange, string);
                    }
                });
                super.visitElement(element);
            }
        });
        return error[0];
    }
}

@@ -932,24 +932,79 @@
 .Annotator;%0A
+import com.intellij.lang.annotation.HighlightSeverity;%0A
 import com.i
@@ -1085,52 +1085,8 @@
 or;%0A
-import com.intellij.openapi.util.TextRange;%0A
 impo
@@ -6238,35 +6238,37 @@
     
-a
+A
 nnotat
-or.annotate(element,
+ionHolderImpl holder =
  new
@@ -6317,18 +6317,17 @@
 n(file))
- %7B
+;
 %0A       
@@ -6339,400 +6339,96 @@
     
-    @Override%0A                    public Annotation createErrorAnnotation(@NotNull ASTNode astNode, String string) %7B%0A                        error%5B0%5D = string;%0A                        return super.createErrorAnnotation(astNode, string);%0A                    %7D%0A%0A                    @Override%0A                    public Annotation createErrorAnnotation(@NotNull PsiElement element, String string
+annotator.annotate(element, holder);%0A                for (Annotation annotation : holder
 ) %7B%0A
@@ -6451,264 +6451,63 @@
     
- 
-   error%5B0%5D = string;%0A                        return super.createErrorAnnotation(element, string);%0A                    %7D%0A%0A                    @Override%0A                    public Annotation createErrorAnnotation(@NotNull TextRange textRange, String string
+if (annotation.getSeverity() == HighlightSeverity.ERROR
 ) %7B%0A
@@ -6541,22 +6541,39 @@
 or%5B0%5D = 
-string
+annotation.getMessage()
 ;%0A      
@@ -6594,61 +6594,13 @@
     
-return super.createErrorAnnotation(textRange, string)
+break
 ;%0A  
@@ -6628,34 +6628,32 @@
                %7D
-);
 %0A               

remove custom unnecessary AnnotationHolder implementation