| /* |
| * Copyright 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.lang.xpath.xslt.impl; |
| |
| import com.intellij.lang.documentation.DocumentationProvider; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.light.LightElement; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.xml.*; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.xml.util.XmlUtil; |
| import org.intellij.lang.xpath.completion.ElementProvider; |
| import org.intellij.lang.xpath.completion.FunctionLookup; |
| import org.intellij.lang.xpath.psi.XPathFunction; |
| import org.intellij.lang.xpath.xslt.XsltSupport; |
| import org.intellij.lang.xpath.xslt.psi.XsltElement; |
| import org.intellij.lang.xpath.xslt.psi.impl.XsltLanguage; |
| import org.jdom.Document; |
| import org.jdom.Element; |
| import org.jdom.JDOMException; |
| import org.jdom.input.SAXBuilder; |
| import org.jdom.transform.JDOMSource; |
| import org.jdom.xpath.XPath; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.xml.transform.Templates; |
| import javax.xml.transform.Transformer; |
| import javax.xml.transform.TransformerConfigurationException; |
| import javax.xml.transform.TransformerFactory; |
| import javax.xml.transform.stream.StreamResult; |
| import javax.xml.transform.stream.StreamSource; |
| import java.io.IOException; |
| import java.io.StringWriter; |
| import java.lang.ref.SoftReference; |
| import java.net.URL; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| public class XsltDocumentationProvider implements DocumentationProvider { |
| private SoftReference<Templates> myTemplates; |
| private SoftReference<Document> myDocument; |
| |
| @Nullable |
| public List<String> getUrlFor(PsiElement psiElement, PsiElement psiElement1) { |
| if (psiElement instanceof XsltElement) return null; |
| |
| final String category; |
| final String name; |
| final String tagName = getTagName(psiElement); |
| if (tagName != null) { |
| name = tagName; |
| category = "element"; |
| } else if (psiElement instanceof XPathFunction) { |
| name = ((XPathFunction)psiElement).getName(); |
| category = "function"; |
| } else if (psiElement instanceof DocElement) { |
| name = ((DocElement)psiElement).getName(); |
| category = ((DocElement)psiElement).getCategory(); |
| } else { |
| return null; |
| } |
| |
| try { |
| final Document document = getDocumentationDocument(); |
| final XPath xPath = XPath.newInstance("//x:" + category + "[@name = '" + name + "']"); |
| xPath.addNamespace("x", document.getRootElement().getNamespaceURI()); |
| final Element e = (Element)xPath.selectSingleNode(document); |
| if (e != null) { |
| return Collections.singletonList(e.getParentElement().getAttributeValue("base") + e.getAttributeValue("href")); |
| } |
| } catch (Exception e) { |
| Logger.getInstance(getClass().getName()).error(e); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public String generateDoc(PsiElement psiElement, PsiElement psiElement1) { |
| if (psiElement instanceof DocElement) { |
| final DocElement element = (DocElement)psiElement; |
| return getDocumentation(element.getName(), element.getCategory()); |
| } |
| |
| if (psiElement instanceof XsltElement) { |
| final XmlTag t = ((XsltElement)psiElement).getTag(); |
| PsiElement p = t.getPrevSibling(); |
| while (p instanceof PsiWhiteSpace || p instanceof XmlText) { |
| p = p.getPrevSibling(); |
| } |
| if (p instanceof XmlComment) { |
| final String commentText = XmlUtil.getCommentText((XmlComment)p); |
| return commentText != null ? commentText.replaceAll("&", "&").replaceAll("<", "<") : null; |
| } else { |
| return null; |
| } |
| } |
| |
| final String name = getTagName(psiElement); |
| if (name != null) { |
| return getDocumentation(name, "element"); |
| } else if (psiElement instanceof XPathFunction) { |
| return getDocumentation(((XPathFunction)psiElement).getName(), "function"); |
| } |
| |
| return null; |
| } |
| |
| private static final Pattern check = Pattern.compile("x:found=\"(true|false)\""); |
| |
| @Nullable |
| private String getDocumentation(String name, String type) { |
| try { |
| final Transformer transformer = getTemplate().newTransformer(); |
| transformer.setParameter("element", name); |
| transformer.setParameter("type", type); |
| final StringWriter writer = new StringWriter(); |
| transformer.transform(new JDOMSource(getDocumentationDocument()), new StreamResult(writer)); |
| |
| final String s = writer.toString(); |
| final Matcher matcher = check.matcher(s); |
| if (matcher.find()) { |
| if (matcher.group(1).equals("true")) { |
| return s.replaceFirst("<META.+?>", ""); |
| } |
| } |
| } catch (Exception e) { |
| Logger.getInstance(getClass().getName()).error(e); |
| } |
| return null; |
| } |
| |
| @Nullable |
| private static String getTagName(@Nullable PsiElement psiElement1) { |
| XmlTag xmlTag = PsiTreeUtil.getParentOfType(psiElement1, XmlTag.class, false); |
| if (xmlTag != null) { |
| if (XsltSupport.isXsltTag(xmlTag)) { |
| return xmlTag.getLocalName(); |
| } |
| else if (XmlUtil.ourSchemaUrisList.contains(xmlTag.getNamespace())) { |
| PsiFile file = xmlTag.getContainingFile(); |
| if (file instanceof XmlFile) { |
| XmlTag tag = ((XmlFile)file).getRootTag(); |
| if (tag != null && XsltSupport.XSLT_NS.equals(tag.getAttributeValue("targetNamespace"))) { |
| return xmlTag.getAttributeValue("name"); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| private Document getDocumentationDocument() throws IOException, JDOMException { |
| Document d = com.intellij.reference.SoftReference.dereference(myDocument); |
| if (d == null) { |
| d = new SAXBuilder().build(XsltSupport.class.getResource("resources/documentation.xml")); |
| myDocument = new SoftReference<Document>(d); |
| } |
| return d; |
| } |
| |
| private Templates getTemplate() throws TransformerConfigurationException, IOException { |
| Templates t = com.intellij.reference.SoftReference.dereference(myTemplates); |
| if (t == null) { |
| t = TransformerFactory.newInstance().newTemplates(makeSource("resources/documentation.xsl")); |
| myTemplates = new SoftReference<Templates>(t); |
| } |
| return t; |
| } |
| |
| private static StreamSource makeSource(String name) throws IOException { |
| final URL resource = XsltSupport.class.getResource(name); |
| return new StreamSource(resource.openStream(), resource.toExternalForm()); |
| } |
| |
| @Nullable |
| public PsiElement getDocumentationElementForLookupItem(PsiManager mgr, Object object, PsiElement psiElement) { |
| if (object instanceof String) { |
| if (psiElement instanceof XmlElement) { |
| final XmlTag tag = PsiTreeUtil.getParentOfType(psiElement, XmlTag.class); |
| if (tag != null && XsltSupport.XSLT_NS.equals(tag.getNamespace())) { |
| final String prefix = tag.getNamespacePrefix(); |
| if (prefix.length() == 0) { |
| return new DocElement(mgr, psiElement, "element", (String)object); |
| } else if (StringUtil.startsWithConcatenation(((String)object), prefix, ":")) { |
| return new DocElement(mgr, psiElement, "element", ((String)object).substring(prefix.length() + 1)); |
| } |
| } |
| } |
| } |
| if (object instanceof FunctionLookup) { |
| final FunctionLookup lookup = ((FunctionLookup)object); |
| return new DocElement(mgr, psiElement, "function", lookup.getName()); |
| } else if (object instanceof ElementProvider) { |
| return ((ElementProvider)object).getElement(); |
| } if (object instanceof XsltElement) { |
| return (PsiElement)object; |
| } |
| return null; |
| } |
| |
| @Nullable |
| public PsiElement getDocumentationElementForLink(PsiManager mgr, String string, PsiElement psiElement) { |
| final String[] strings = string.split("\\$"); |
| if (strings.length == 2) { |
| return new DocElement(mgr, psiElement, strings[0], strings[1]); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { |
| return null; |
| } |
| |
| static class DocElement extends LightElement implements PsiNamedElement { |
| private final PsiElement myElement; |
| private final String myCategory; |
| private final String myName; |
| |
| public DocElement(PsiManager mgr, PsiElement element, String category, String name) { |
| super(mgr, XsltLanguage.INSTANCE); |
| myElement = element; |
| myCategory = category; |
| myName = name; |
| } |
| |
| public String getCategory() { |
| return myCategory; |
| } |
| |
| public PsiElement setName(@NotNull @NonNls String name) throws IncorrectOperationException { |
| throw new IncorrectOperationException("Unsupported"); |
| } |
| |
| public String getName() { |
| return myName; |
| } |
| |
| public String toString() { |
| return "DocElement"; |
| } |
| |
| public PsiElement copy() { |
| return this; |
| } |
| |
| @Override |
| public boolean isValid() { |
| return myElement != null && myElement.isValid(); |
| } |
| |
| @Nullable |
| public PsiFile getContainingFile() { |
| if (!isValid()) { |
| return null; |
| } |
| |
| PsiFile file = myElement.getContainingFile(); |
| final PsiElement context = myElement.getContext(); |
| if (file == null && context != null) { |
| file = context.getContainingFile(); |
| } |
| PsiFile f; |
| if ((f = PsiTreeUtil.getContextOfType(file, PsiFile.class, true)) instanceof XmlFile) { |
| return f; |
| } |
| return file; |
| } |
| } |
| } |