blob: 82731a089443b1a9aceafaf9972ee26016d5aa6d [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 com.intellij.lang.xml;
import com.intellij.codeInsight.daemon.Validator;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.lang.annotation.Annotation;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.openapi.util.Trinity;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlToken;
import com.intellij.xml.XmlNSDescriptor;
import com.intellij.xml.util.XmlTagUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author ven
*/
public class XMLExternalAnnotator extends ExternalAnnotator<XMLExternalAnnotator.MyHost, XMLExternalAnnotator.MyHost> {
@Nullable
@Override
public MyHost collectInformation(@NotNull PsiFile file) {
if (!(file instanceof XmlFile)) return null;
final XmlDocument document = ((XmlFile)file).getDocument();
if (document == null) return null;
XmlTag rootTag = document.getRootTag();
XmlNSDescriptor nsDescriptor = rootTag == null ? null : rootTag.getNSDescriptor(rootTag.getNamespace(), false);
if (nsDescriptor instanceof Validator) {
//noinspection unchecked
MyHost host = new MyHost();
((Validator<XmlDocument>)nsDescriptor).validate(document, host);
return host;
}
return null;
}
@Nullable
@Override
public MyHost doAnnotate(MyHost collectedInfo) {
return collectedInfo;
}
@Override
public void apply(@NotNull PsiFile file, MyHost annotationResult, @NotNull AnnotationHolder holder) {
annotationResult.apply(holder);
}
private static void appendFixes(final Annotation annotation, final IntentionAction... actions) {
if (actions != null) {
for (IntentionAction action : actions) annotation.registerFix(action);
}
}
static class MyHost implements Validator.ValidationHost {
private final List<Trinity<PsiElement, String, ErrorType>> messages = new ArrayList<Trinity<PsiElement, String, ErrorType>>();
@Override
public void addMessage(PsiElement context, String message, int type) {
throw new UnsupportedOperationException();
}
@Override
public void addMessage(PsiElement context, String message, @NotNull ErrorType type) {
messages.add(Trinity.create(context, message, type));
}
void apply (AnnotationHolder holder) {
for (Trinity<PsiElement, String, ErrorType> message : messages) {
addMessageWithFixes(message.first, message.second, message.third, holder);
}
}
}
public static void addMessageWithFixes(final PsiElement context,
final String message,
@NotNull final Validator.ValidationHost.ErrorType type,
AnnotationHolder myHolder,
@NotNull final IntentionAction... fixes) {
if (message != null && !message.isEmpty()) {
if (context instanceof XmlTag) {
addMessagesForTag((XmlTag)context, message, type, myHolder, fixes);
}
else {
if (type == Validator.ValidationHost.ErrorType.ERROR) {
appendFixes(myHolder.createErrorAnnotation(context, message), fixes);
}
else {
appendFixes(myHolder.createWarningAnnotation(context, message), fixes);
}
}
}
}
private static void addMessagesForTag(XmlTag tag, String message, Validator.ValidationHost.ErrorType type, AnnotationHolder myHolder, IntentionAction... actions) {
XmlToken childByRole = XmlTagUtil.getStartTagNameElement(tag);
addMessagesForTreeChild(childByRole, type, message, myHolder, actions);
childByRole = XmlTagUtil.getEndTagNameElement(tag);
addMessagesForTreeChild(childByRole, type, message, myHolder, actions);
}
private static void addMessagesForTreeChild(final XmlToken childByRole,
final Validator.ValidationHost.ErrorType type,
final String message,
AnnotationHolder myHolder, IntentionAction... actions) {
if (childByRole != null) {
Annotation annotation;
if (type == Validator.ValidationHost.ErrorType.ERROR) {
annotation = myHolder.createErrorAnnotation(childByRole, message);
}
else {
annotation = myHolder.createWarningAnnotation(childByRole, message);
}
appendFixes(annotation, actions);
}
}
}