| /* |
| * Copyright 2007 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.relaxNG; |
| |
| import com.intellij.lang.documentation.DocumentationProvider; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.xml.XmlAttribute; |
| import com.intellij.psi.xml.XmlElement; |
| import com.intellij.psi.xml.XmlTag; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.xml.XmlAttributeDescriptor; |
| import com.intellij.xml.XmlElementDescriptor; |
| import com.intellij.xml.util.XmlStringUtil; |
| import gnu.trove.THashSet; |
| import org.intellij.plugins.relaxNG.model.descriptors.CompositeDescriptor; |
| import org.intellij.plugins.relaxNG.model.descriptors.RngElementDescriptor; |
| import org.intellij.plugins.relaxNG.model.descriptors.RngXmlAttributeDescriptor; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.Nullable; |
| import org.kohsuke.rngom.digested.DElementPattern; |
| |
| import java.util.Collection; |
| import java.util.List; |
| |
| /* |
| * Created by IntelliJ IDEA. |
| * User: sweinreuter |
| * Date: 19.11.2007 |
| */ |
| public class RngDocumentationProvider implements DocumentationProvider { |
| private static final Logger LOG = Logger.getInstance(RngDocumentationProvider.class); |
| |
| @NonNls |
| private static final String COMPATIBILITY_ANNOTATIONS_1_0 = "http://relaxng.org/ns/compatibility/annotations/1.0"; |
| |
| @Override |
| @Nullable |
| public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { |
| final XmlElement c = PsiTreeUtil.getParentOfType(originalElement, XmlTag.class, XmlAttribute.class); |
| if (c != null && c.getManager() == null) { |
| LOG.warn("Invalid context element passed to generateDoc()", new Throwable("<stack trace>")); |
| return null; |
| } |
| if (c instanceof XmlTag) { |
| final XmlTag xmlElement = (XmlTag)c; |
| final XmlElementDescriptor descriptor = xmlElement.getDescriptor(); |
| if (descriptor instanceof CompositeDescriptor) { |
| final StringBuilder sb = new StringBuilder(); |
| final CompositeDescriptor d = (CompositeDescriptor)descriptor; |
| final DElementPattern[] patterns = d.getElementPatterns(); |
| final THashSet<PsiElement> elements = ContainerUtil.newIdentityTroveSet(); |
| for (DElementPattern pattern : patterns) { |
| final PsiElement psiElement = d.getDeclaration(pattern.getLocation()); |
| if (psiElement instanceof XmlTag && elements.add(psiElement)) { |
| if (sb.length() > 0) { |
| sb.append("<hr>"); |
| } |
| sb.append(getDocumentationFromTag((XmlTag)psiElement, xmlElement.getLocalName(), "Element")); |
| } |
| } |
| return makeDocumentation(sb); |
| } else if (descriptor instanceof RngElementDescriptor) { |
| final RngElementDescriptor d = (RngElementDescriptor)descriptor; |
| final PsiElement declaration = d.getDeclaration(); |
| if (declaration instanceof XmlTag) { |
| return makeDocumentation(getDocumentationFromTag((XmlTag)declaration, xmlElement.getLocalName(), "Element")); |
| } |
| } |
| } else if (c instanceof XmlAttribute) { |
| final XmlAttribute attribute = (XmlAttribute)c; |
| final XmlAttributeDescriptor descriptor = attribute.getDescriptor(); |
| if (descriptor instanceof RngXmlAttributeDescriptor) { |
| final RngXmlAttributeDescriptor d = (RngXmlAttributeDescriptor)descriptor; |
| final StringBuilder sb = new StringBuilder(); |
| final Collection<PsiElement> declaration = ContainerUtil.newIdentityTroveSet(d.getDeclarations()); |
| for (PsiElement psiElement : declaration) { |
| if (psiElement instanceof XmlTag) { |
| if (sb.length() > 0) { |
| sb.append("<hr>"); |
| } |
| sb.append(getDocumentationFromTag((XmlTag)psiElement, d.getName(), "Attribute")); |
| } |
| } |
| } |
| } else if (element instanceof XmlTag) { |
| return makeDocumentation(getDocumentationFromTag((XmlTag)element, ((XmlTag)element).getLocalName(), "Element")); |
| } |
| return null; |
| } |
| |
| private static String makeDocumentation(StringBuilder sb) { |
| if (sb == null) return null; |
| String s = sb.toString().replaceAll("\n", "<br>"); |
| if (!s.startsWith("<html>")) { |
| s = XmlStringUtil.wrapInHtml(s); |
| } |
| return s; |
| } |
| |
| private static StringBuilder getDocumentationFromTag(XmlTag tag, String localName, String kind) { |
| if (tag.getNamespace().equals(ApplicationLoader.RNG_NAMESPACE)) { |
| final StringBuilder sb = new StringBuilder(); |
| sb.append(kind).append(": <b>").append(localName).append("</b><br>"); |
| final XmlTag[] docTags = tag.findSubTags("documentation", COMPATIBILITY_ANNOTATIONS_1_0); |
| for (XmlTag docTag : docTags) { |
| sb.append(docTag.getValue().getTrimmedText()); |
| sb.append("\n"); |
| } |
| final XmlTag nextTag = PsiTreeUtil.getNextSiblingOfType(tag, XmlTag.class); |
| if (nextTag != null) { |
| if ("documentation".equals(nextTag.getLocalName()) && COMPATIBILITY_ANNOTATIONS_1_0.equals(nextTag.getNamespace())) { |
| sb.append(nextTag.getValue().getTrimmedText()); |
| } |
| } |
| return sb; |
| } |
| return null; |
| } |
| |
| @Override |
| @Nullable |
| public PsiElement getDocumentationElementForLink(PsiManager psiManager, String link, PsiElement context) { |
| return null; |
| } |
| |
| @Override |
| @Nullable |
| public PsiElement getDocumentationElementForLookupItem(PsiManager psiManager, Object object, PsiElement element) { |
| return null; |
| } |
| |
| @Override |
| @Nullable |
| public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { |
| return null; |
| } |
| |
| @Override |
| public List<String> getUrlFor(PsiElement element, PsiElement originalElement) { |
| return null; |
| } |
| |
| public int hashCode() { |
| return 0; // CompositeDocumentationProvider uses a HashSet that doesn't preserve order. We want to be the first one. |
| } |
| } |