emptyIterator() used; misc warning fixes
[idea/community.git] / plugins / xpath / xpath-view / src / org / intellij / plugins / xpathView / support / jaxen / PsiDocumentNavigator.java
1 /*
2  * Copyright 2002-2005 Sascha Weinreuter
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.intellij.plugins.xpathView.support.jaxen;
17
18 import com.intellij.lang.xml.XMLLanguage;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.util.Ref;
21 import com.intellij.openapi.util.text.StringUtil;
22 import com.intellij.openapi.vfs.VfsUtilCore;
23 import com.intellij.openapi.vfs.VirtualFile;
24 import com.intellij.psi.PsiElement;
25 import com.intellij.psi.PsiFile;
26 import com.intellij.psi.PsiWhiteSpace;
27 import com.intellij.psi.XmlRecursiveElementVisitor;
28 import com.intellij.psi.xml.*;
29 import com.intellij.xml.XmlAttributeDescriptor;
30 import org.intellij.plugins.xpathView.util.MyPsiUtil;
31 import org.jaxen.DefaultNavigator;
32 import org.jaxen.XPath;
33 import org.jaxen.saxpath.SAXPathException;
34 import org.jetbrains.annotations.NotNull;
35
36 import java.util.Collections;
37 import java.util.Iterator;
38
39 /**
40  * <p>Adapter class for IDEA's PSI-tree to Jaxen.</p>
41  * Not all of the required functionality is implemented yet. See the TODO comments...
42  */
43 public class PsiDocumentNavigator extends DefaultNavigator {
44
45     private static final Logger LOG = Logger.getInstance("org.intellij.plugins.xpathView.support.jaxen.PsiDocumentNavigator");
46     private final XmlFile file;
47
48     public PsiDocumentNavigator(XmlFile file) {
49         this.file = file;
50     }
51
52     @Override
53     public Iterator getChildAxisIterator(Object contextNode) {
54         if (!(contextNode instanceof XmlElement)) {
55             return Collections.emptyIterator();
56         }
57         return new PsiChildAxisIterator(contextNode);
58     }
59
60
61     @Override
62     public Iterator getParentAxisIterator(Object contextNode) {
63         if (!(contextNode instanceof XmlElement)) {
64             return Collections.emptyIterator();
65         }
66
67         return new NodeIterator((XmlElement)contextNode) {
68             @Override
69             protected PsiElement getFirstNode(PsiElement n) {
70                 while (n != null) {
71                     n = n.getParent();
72                     if (n instanceof XmlTag) {
73                         return n;
74                     }
75                 }
76                 return null;
77             }
78
79             @Override
80             protected PsiElement getNextNode(PsiElement n) {
81                 return null;
82             }
83         };
84     }
85
86
87   @Override
88   public Object getDocumentNode(Object contextNode) {
89         if (contextNode instanceof XmlDocument) {
90             return contextNode;
91         }
92
93         while (contextNode instanceof PsiElement) {
94             if (contextNode instanceof XmlDocument) {
95                 return contextNode;
96             }
97             contextNode = ((PsiElement)contextNode).getParent();
98         }
99
100         return null;
101     }
102
103     @Override
104     public String translateNamespacePrefixToUri(String prefix, Object element) {
105         if (isElement(element)) {
106             return ((XmlTag)element).getNamespaceByPrefix(prefix);
107         }
108         return super.translateNamespacePrefixToUri(prefix, element);
109     }
110
111     @Override
112     public String getProcessingInstructionTarget(Object obj) {
113         LOG.assertTrue(obj instanceof XmlProcessingInstruction);
114
115         XmlProcessingInstruction pi = (XmlProcessingInstruction)obj;
116         return getProcessingInstructionTarget(pi);
117     }
118
119     public static String getProcessingInstructionTarget(XmlProcessingInstruction pi) {
120         final PsiElement[] children = pi.getChildren();
121         LOG.assertTrue(children[1] instanceof XmlToken && ((XmlToken)children[1]).getTokenType() == XmlTokenType.XML_NAME, "Unknown PI structure");
122
123         String text = children[1].getText();
124         int i;
125         for (i=0; i<text.length() && text.charAt(i) == ' ';) i++; // skip
126         final int pos = text.indexOf(' ', i);
127         if (pos != -1) {
128             text = text.substring(i, pos);
129         } else {
130             text = text.substring(i);
131         }
132
133         return text;
134     }
135
136     @Override
137     @NotNull
138     public String getProcessingInstructionData(Object obj) {
139         LOG.assertTrue(obj instanceof XmlProcessingInstruction);
140
141         XmlProcessingInstruction pi = (XmlProcessingInstruction)obj;
142         int targetLength = getProcessingInstructionTarget(obj).length();
143         int piLength= pi.getText().length();
144         return pi.getText().substring(2 + targetLength, piLength - 2).trim();
145     }
146
147     @Override
148     public Object getParentNode(Object contextNode) {
149         return ((PsiElement)contextNode).getParent();
150     }
151
152     @Override
153     public Object getDocument(String url) {
154         final VirtualFile virtualFile = VfsUtilCore.findRelativeFile(url, file.getVirtualFile());
155         if (virtualFile != null) {
156             final PsiFile file = this.file.getManager().findFile(virtualFile);
157             if (file instanceof XmlFile) {
158                 return ((XmlFile)file).getDocument();
159             }
160         }
161         return null;
162     }
163
164     @Override
165     public Iterator getAttributeAxisIterator(Object contextNode) {
166         if (isElement(contextNode)) {
167             return new AttributeIterator((XmlElement)contextNode);
168         } else {
169             return Collections.emptyIterator();
170         }
171     }
172
173     @Override
174     public String getElementNamespaceUri(Object element) {
175         LOG.assertTrue(element instanceof XmlTag);
176
177         final XmlTag context = (XmlTag)element;
178         final String namespaceUri = context.getNamespace();
179         if (!MyPsiUtil.isInDeclaredNamespace(context, namespaceUri, context.getNamespacePrefix())) {
180           return "";
181         }
182         return namespaceUri;
183     }
184
185     @Override
186     public String getElementName(Object element) {
187         LOG.assertTrue(element instanceof XmlTag);
188         return ((XmlTag)element).getLocalName();
189     }
190
191     @Override
192     public String getElementQName(Object element) {
193         LOG.assertTrue(element instanceof XmlTag);
194         return ((XmlTag)element).getName();
195     }
196
197     @Override
198     public String getAttributeNamespaceUri(Object attr) {
199         LOG.assertTrue(attr instanceof XmlAttribute);
200
201         final XmlAttribute attribute = ((XmlAttribute)attr);
202         final String name = attribute.getName();
203         if (name.indexOf(':') == -1) return "";
204
205         final String uri = attribute.getNamespace();
206         if (!MyPsiUtil.isInDeclaredNamespace(attribute.getParent(), uri, MyPsiUtil.getAttributePrefix(attribute))) {
207             LOG.info("getElementNamespaceUri: not returning implicit attribute-namespace uri: " + uri);
208             return "";
209         }
210         return uri;
211     }
212
213     @Override
214     public String getAttributeName(Object attr) {
215         LOG.assertTrue(attr instanceof XmlAttribute);
216         return ((XmlAttribute)attr).getLocalName();
217     }
218
219     @Override
220     public String getAttributeQName(Object attr) {
221         LOG.assertTrue(attr instanceof XmlAttribute);
222         return ((XmlAttribute)attr).getName();
223     }
224
225     @Override
226     public boolean isDocument(Object object) {
227         return object instanceof XmlDocument;
228     }
229
230     @Override
231     public boolean isElement(Object object) {
232         return object instanceof XmlTag && isSupportedElement((XmlTag)object);
233     }
234
235     private static boolean isSupportedElement(XmlTag object) {
236         // optimization: all tags from XML language are supported, but some from other languages (JSP, see IDEADEV-37939) are not
237         return object.getLanguage() == XMLLanguage.INSTANCE || MyPsiUtil.findNameElement(object) != null;
238     }
239
240     @Override
241     public boolean isAttribute(Object object) {
242         return object instanceof XmlAttribute;
243     }
244
245     @Override
246     public boolean isNamespace(Object object) {
247         // TODO: implement when namespace axis is supported
248         return false;
249     }
250
251     @Override
252     public boolean isComment(Object object) {
253         return object instanceof XmlComment;
254     }
255
256     @Override
257     public boolean isText(Object object) {
258         return object instanceof PsiWhiteSpace ? ((PsiWhiteSpace)object).getParent() instanceof XmlText : object instanceof XmlText;
259     }
260
261     @Override
262     public boolean isProcessingInstruction(Object object) {
263         return object instanceof XmlProcessingInstruction;
264     }
265
266     @Override
267     @NotNull
268     public String getCommentStringValue(Object comment) {
269         LOG.assertTrue(comment instanceof XmlComment);
270
271         PsiElement c = (PsiElement)comment;
272         final PsiElement[] children = c.getChildren();
273         for (PsiElement child : children) {
274             if (child instanceof XmlToken && ((XmlToken)child).getTokenType() == XmlTokenType.XML_COMMENT_CHARACTERS) {
275                 return child.getText();
276             }
277         }
278         return "";
279     }
280
281     @Override
282     @NotNull
283     public String getElementStringValue(Object element) {
284         LOG.assertTrue(element instanceof XmlTag);
285
286         final TextCollector collector = new TextCollector();
287         ((XmlTag)element).accept(collector);
288         return collector.getText();
289     }
290
291     @Override
292     @NotNull
293     public String getAttributeStringValue(Object attr) {
294         LOG.assertTrue(attr instanceof XmlAttribute);
295         return StringUtil.notNullize(((XmlAttribute)attr).getValue());
296     }
297
298     @Override
299     public String getNamespaceStringValue(Object ns) {
300         // TODO: implement when namespace axis is supported
301         return null;
302     }
303
304     @Override
305     public String getNamespacePrefix(Object ns) {
306         // TODO: implement when namespace axis is supported
307         return null;
308     }
309
310     @Override
311     @NotNull
312     public String getTextStringValue(Object txt) {
313
314         if (txt instanceof XmlText) {
315           return ((XmlText)txt).getValue();
316         }
317         return txt instanceof PsiElement ? ((PsiElement)txt).getText() : txt.toString();
318     }
319
320     @Override
321     public XPath parseXPath(String xpath) throws SAXPathException {
322         return new PsiXPath(file, xpath);
323     }
324
325     @Override
326     public Object getElementById(Object object, final String elementId) {
327       final XmlTag rootTag = ((XmlFile)((XmlElement)object).getContainingFile()).getRootTag();
328       if (rootTag == null) {
329         return null;
330       }
331
332       final Ref<XmlTag> ref = new Ref<>();
333       rootTag.accept(new XmlRecursiveElementVisitor() {
334         @Override
335         public void visitElement(PsiElement element) {
336           if (ref.get() == null) {
337             super.visitElement(element);
338           }
339         }
340
341         @Override
342         public void visitXmlAttribute(XmlAttribute attribute) {
343           final XmlAttributeDescriptor descriptor = attribute.getDescriptor();
344           final String value = attribute.getValue();
345           if ((value != null &&
346                (descriptor != null && descriptor.hasIdType()))) {
347             if (elementId.equals(value)) {
348               ref.set(attribute.getParent());
349             }
350           }
351         }
352       });
353       return ref.get();
354     }
355
356     static class TextCollector extends XmlRecursiveElementVisitor {
357         private final StringBuffer builder = new StringBuffer();
358
359         @Override
360         public void visitXmlText(XmlText text) {
361             builder.append(text.getValue());
362         }
363
364         public String getText() {
365             return builder.toString();
366         }
367     }
368 }