| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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.jetbrains.android.inspections.lint; |
| |
| import com.intellij.openapi.application.Application; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.progress.ProcessCanceledException; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiElement; |
| import com.intellij.psi.xml.*; |
| import com.intellij.util.containers.HashMap; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.w3c.dom.*; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Converter which takes a PSI hierarchy for an XML file or document, and |
| * creates a corresponding W3C DOM tree. It attempts to delegate as much |
| * as possible to the original PSI tree. Note also that the {@link #getTextRange(Node)}} |
| * method allows us to look up source offsets for the DOM nodes (which plain XML |
| * DOM parsers do not). |
| * <p> |
| * NOTE: The tree may not be semantically equivalent to the XML PSI structure; this |
| * converter only attempts to make the DOM correct as far as Lint cares (meaning that it |
| * only worries about the details Lint cares about; currently this means it only wraps elements, |
| * text and comment nodes.) |
| */ |
| class DomPsiConverter { |
| private DomPsiConverter() { |
| } |
| |
| /** |
| * Convert the given {@link XmlFile} to a DOM tree |
| * |
| * @param xmlFile the file to be converted |
| * @return a corresponding W3C DOM tree |
| */ |
| @Nullable |
| public static Document convert(@NotNull XmlFile xmlFile) { |
| try { |
| XmlDocument xmlDocument = xmlFile.getDocument(); |
| if (xmlDocument == null) { |
| return null; |
| } |
| |
| return convert(xmlDocument); |
| } |
| catch (ProcessCanceledException e) { |
| // Ignore: common occurrence, e.g. we're running lint as part of an editor background |
| // and while lint is running the user switches files: the inspections framework will |
| // then cancel the process from within the PSI machinery (which asks the progress manager |
| // periodically whether the operation is cancelled) and we find ourselves here |
| return null; |
| } |
| catch (Exception e) { |
| String path = xmlFile.getName(); |
| VirtualFile virtualFile = xmlFile.getVirtualFile(); |
| if (virtualFile != null) { |
| path = virtualFile.getPath(); |
| } |
| throw new RuntimeException("Could not convert file " + path, e); |
| } |
| } |
| |
| /** |
| * Convert the given {@link XmlDocument} to a DOM tree |
| * |
| * @param document the document to be converted |
| * @return a corresponding W3C DOM tree |
| */ |
| @Nullable |
| private static Document convert(@NotNull XmlDocument document) { |
| return new DomDocument(document); |
| } |
| |
| /** Gets the {@link TextRange} for a {@link Node} created with this converter */ |
| @NotNull |
| public static TextRange getTextRange(@NotNull Node node) { |
| assert node instanceof DomNode; |
| DomNode domNode = (DomNode)node; |
| XmlElement element = domNode.myElement; |
| |
| // For elements, don't highlight the entire element range; instead, just |
| // highlight the element name |
| if (node.getNodeType() == Node.ELEMENT_NODE) { |
| return getTextNameRange(node); |
| } |
| |
| return element.getTextRange(); |
| } |
| |
| /** Gets the {@link TextRange} for a {@link Node} created with this converter */ |
| @NotNull |
| public static TextRange getTextNameRange(@NotNull Node node) { |
| assert node instanceof DomNode; |
| DomNode domNode = (DomNode)node; |
| XmlElement element = domNode.myElement; |
| |
| // For elements and attributes, don't highlight the entire element range; instead, just |
| // highlight the element name |
| if (node.getNodeType() == Node.ELEMENT_NODE && element instanceof XmlTag) { |
| String tag = node.getNodeName(); |
| int index = element.getText().indexOf(tag); |
| if (index != -1) { |
| TextRange textRange = element.getTextRange(); |
| int start = textRange.getStartOffset() + index; |
| return new TextRange(start, start + tag.length()); |
| } |
| } else if (node.getNodeType() == Node.ATTRIBUTE_NODE && element instanceof XmlAttribute) { |
| XmlElement nameElement = ((XmlAttribute)element).getNameElement(); |
| if (nameElement != null) { |
| return nameElement.getTextRange(); |
| } |
| } |
| |
| return element.getTextRange(); |
| } |
| |
| /** Gets the {@link TextRange} for the value region of a {@link Node} created with this converter */ |
| @NotNull |
| public static TextRange getTextValueRange(@NotNull Node node) { |
| assert node instanceof DomNode; |
| DomNode domNode = (DomNode)node; |
| XmlElement element = domNode.myElement; |
| TextRange textRange = element.getTextRange(); |
| |
| // For attributes, don't highlight the entire element range; instead, just |
| // highlight the value range |
| if (node.getNodeType() == Node.ATTRIBUTE_NODE && element instanceof XmlAttribute) { |
| XmlAttributeValue valueElement = ((XmlAttribute)element).getValueElement(); |
| if (valueElement != null) { |
| return valueElement.getValueTextRange(); |
| } |
| } |
| |
| return textRange; |
| } |
| |
| private static final NodeList EMPTY = new NodeList() { |
| @NotNull |
| @Override |
| public Node item(int i) { |
| throw new IllegalArgumentException(); |
| } |
| |
| @Override |
| public int getLength() { |
| return 0; |
| } |
| }; |
| |
| @Nullable |
| private static final NamedNodeMap EMPTY_ATTRIBUTES = new NamedNodeMap() { |
| @Override |
| public int getLength() { |
| return 0; |
| } |
| |
| @Nullable |
| @Override |
| public Node getNamedItem(String s) { |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public Node getNamedItemNS(String s, String s2) throws DOMException { |
| return null; |
| } |
| |
| @NotNull |
| @Override |
| public Node setNamedItem(Node node) throws DOMException { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Node removeNamedItem(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Node item(int i) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Nullable |
| @Override |
| public Node setNamedItemNS(Node node) throws DOMException { |
| return null; |
| } |
| |
| @NotNull |
| @Override |
| public Node removeNamedItemNS(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| }; |
| |
| private static class DomNodeList implements NodeList { |
| protected final List<DomNode> myChildren = new ArrayList<DomNode>(); |
| |
| @NotNull |
| @Override |
| public Node item(int i) { |
| return myChildren.get(i); |
| } |
| |
| @Override |
| public int getLength() { |
| return myChildren.size(); |
| } |
| |
| void add(@NotNull DomNode node) { |
| int size = myChildren.size(); |
| if (size > 0) { |
| DomNode last = myChildren.get(size - 1); |
| node.myPrevious = last; |
| last.myNext = node; |
| } |
| myChildren.add(node); |
| } |
| } |
| |
| private static class DomNamedNodeMap implements NamedNodeMap { |
| @NotNull protected final Map<String, DomNode> myMap; |
| @NotNull protected final Map<String, Map<String, DomNode>> myNsMap; |
| @NotNull protected final List<DomNode> mItems; |
| |
| private DomNamedNodeMap(@NotNull DomElement element, @NotNull XmlAttribute[] attributes) { |
| int count = attributes.length; |
| int namespaceCount = 0; |
| for (XmlAttribute attribute : attributes) { |
| if (!attribute.getNamespace().isEmpty()) { |
| namespaceCount++; |
| } |
| } |
| myMap = new HashMap<String, DomNode>(count - namespaceCount); |
| myNsMap = new HashMap<String, Map<String, DomNode>>(namespaceCount); |
| mItems = new ArrayList<DomNode>(count); |
| |
| assert element.myOwner != null; // True for elements, not true for non-Element nodes |
| for (XmlAttribute attribute : attributes) { |
| DomAttr attr = new DomAttr(element.myOwner, element, attribute); |
| mItems.add(attr); |
| String namespace = attribute.getNamespace(); |
| if (!namespace.isEmpty()) { |
| Map<String, DomNode> map = myNsMap.get(namespace); |
| if (map == null) { |
| map = new HashMap<String, DomNode>(); |
| myNsMap.put(namespace, map); |
| } |
| map.put(attribute.getLocalName(), attr); |
| } else { |
| myMap.put(attribute.getName(), attr); |
| } |
| } |
| } |
| |
| @Override |
| public Node item(int i) { |
| return mItems.get(i); |
| } |
| |
| @Override |
| public int getLength() { |
| return mItems.size(); |
| } |
| |
| @Override |
| public Node getNamedItem(@NotNull String s) { |
| return myMap.get(s); |
| } |
| |
| @Nullable |
| @Override |
| public Node getNamedItemNS(@NotNull String namespace, @NotNull String name) throws DOMException { |
| Map<String, DomNode> map = myNsMap.get(namespace); |
| if (map != null) { |
| return map.get(name); |
| } |
| return null; |
| } |
| |
| @NotNull |
| @Override |
| public Node setNamedItem(Node node) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node removeNamedItem(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node setNamedItemNS(Node node) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node removeNamedItemNS(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| } |
| |
| @SuppressWarnings({"UnusedParameters", "UnusedDeclaration"}) // Specifies methods shared by children |
| private static abstract class DomNode implements Node { |
| @Nullable protected final Document myOwner; |
| @Nullable protected final DomNode myParent; |
| @NotNull protected final XmlElement myElement; |
| @Nullable protected NodeList myChildren; |
| @Nullable protected DomNode myNext; |
| @Nullable protected DomNode myPrevious; |
| |
| protected DomNode(@Nullable Document owner, @Nullable DomNode parent, @NotNull XmlElement element) { |
| myOwner = owner; |
| myParent = parent; |
| myElement = element; |
| } |
| |
| @Nullable |
| @Override |
| public Node getParentNode() { |
| return myParent; |
| } |
| |
| @NotNull |
| @Override |
| public NodeList getChildNodes() { |
| if (myChildren == null) { |
| PsiElement[] children = myElement.getChildren(); |
| if (children.length > 0) { |
| DomNodeList list = new DomNodeList(); |
| myChildren = list; |
| // True except for in DomDocument, which has custom getChildNodes |
| assert myOwner != null; |
| |
| for (PsiElement child : children) { |
| if (child instanceof XmlTag) { |
| list.add(new DomElement(myOwner, this, (XmlTag) child)); |
| } else if (child instanceof XmlText) { |
| list.add(new DomText(myOwner, this, (XmlText) child)); |
| } else if (child instanceof XmlComment) { |
| list.add(new DomComment(myOwner, this, (XmlComment) child)); |
| } else { |
| // Skipping other types for now; lint doesn't care about them. |
| // TODO: Consider whether we need CDATA. |
| } |
| } |
| } else { |
| myChildren = EMPTY; |
| } |
| } |
| return myChildren; |
| } |
| |
| @Nullable |
| @Override |
| public Node getFirstChild() { |
| NodeList childNodes = getChildNodes(); |
| if (childNodes.getLength() > 0) { |
| return childNodes.item(0); |
| } |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public Node getLastChild() { |
| NodeList childNodes = getChildNodes(); |
| if (childNodes.getLength() > 0) { |
| return childNodes.item(0); |
| } |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public Node getPreviousSibling() { |
| return myPrevious; |
| } |
| |
| @Nullable |
| @Override |
| public Node getNextSibling() { |
| return myNext; |
| } |
| |
| @Nullable |
| @Override |
| public NamedNodeMap getAttributes() { |
| throw new UnsupportedOperationException(); // Only supported on elements |
| } |
| |
| @Nullable |
| @Override |
| public Document getOwnerDocument() { |
| return myOwner; |
| } |
| |
| @Override |
| public void setNodeValue(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node insertBefore(Node node, Node node2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node replaceChild(Node node, Node node2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node removeChild(Node node) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node appendChild(Node node) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public boolean hasChildNodes() { |
| return getChildNodes().getLength() > 0; |
| } |
| |
| @NotNull |
| @Override |
| public Node cloneNode(boolean b) { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public void normalize() { |
| } |
| |
| @Override |
| public boolean isSupported(String s, String s2) { |
| return false; |
| } |
| |
| @NotNull |
| @Override |
| public String getNamespaceURI() { |
| throw new UnsupportedOperationException(); // Only supported on elements in lint |
| } |
| |
| @NotNull |
| @Override |
| public String getPrefix() { |
| throw new UnsupportedOperationException(); // Only supported on elements in lint |
| } |
| |
| @Override |
| public void setPrefix(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Nullable |
| @Override |
| public String getLocalName() { |
| return null; |
| } |
| |
| @Override |
| public boolean hasAttributes() { |
| return false; |
| } |
| |
| @Nullable |
| @Override |
| public String getBaseURI() { |
| return null; |
| } |
| |
| @Override |
| public short compareDocumentPosition(Node node) throws DOMException { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public String getTextContent() throws DOMException { |
| return myElement.getText(); |
| } |
| |
| @Override |
| public void setTextContent(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public boolean isSameNode(Node node) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public String lookupPrefix(String s) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public boolean isDefaultNamespace(String s) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public String lookupNamespaceURI(String s) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public boolean isEqualNode(Node node) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Object getFeature(String s, String s2) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Object setUserData(String s, Object o, UserDataHandler userDataHandler) { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Object getUserData(String s) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| // From CharacterData |
| |
| @NotNull |
| public String getData() throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| public void setData(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| public int getLength() { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| public String substringData(int i, int i2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| public void appendData(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| public void insertData(int i, String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| public void deleteData(int i, int i2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| public void replaceData(int i, int i2, String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| } |
| |
| private static class DomDocument extends DomNode implements Document { |
| @NotNull private final XmlDocument myPsiDocument; |
| @Nullable private DomElement myRoot; |
| |
| private DomDocument(@NotNull XmlDocument document) { |
| super(null, null, document); |
| myPsiDocument = document; |
| } |
| |
| // From org.w3c.dom.Node: |
| |
| @Nullable |
| @Override |
| public String getNodeName() { |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public String getNodeValue() throws DOMException { |
| return null; |
| } |
| |
| @Override |
| public short getNodeType() { |
| return Node.DOCUMENT_NODE; |
| } |
| |
| @NotNull |
| @Override |
| public NodeList getChildNodes() { |
| if (myChildren == null) { |
| DomNodeList list = new DomNodeList(); |
| myChildren = list; |
| DomNode documentElement = (DomNode)getDocumentElement(); |
| if (documentElement != null) { |
| list.add(documentElement); |
| } |
| } |
| |
| return myChildren; |
| } |
| |
| // From org.w3c.dom.Document: |
| |
| @NotNull |
| @Override |
| public DocumentType getDoctype() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public DOMImplementation getImplementation() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Nullable |
| @Override |
| public Element getDocumentElement() { |
| if (myRoot == null) { |
| XmlTag rootTag = myPsiDocument.getRootTag(); |
| if (rootTag == null) { |
| return null; |
| } |
| myRoot = new DomElement(this, this, rootTag); |
| } |
| |
| return myRoot; |
| } |
| |
| @NotNull |
| @Override |
| public NodeList getElementsByTagName(String s) { |
| Element root = getDocumentElement(); |
| if (root != null) { |
| return root.getElementsByTagName(s); |
| } |
| return EMPTY; |
| } |
| |
| @NotNull |
| @Override |
| public NodeList getElementsByTagNameNS(String s, String s2) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Element createElement(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public DocumentFragment createDocumentFragment() { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Text createTextNode(String s) { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Comment createComment(String s) { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public CDATASection createCDATASection(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public ProcessingInstruction createProcessingInstruction(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Attr createAttribute(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public EntityReference createEntityReference(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Node importNode(Node node, boolean b) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Element createElementNS(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Attr createAttributeNS(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Element getElementById(String s) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public String getInputEncoding() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public String getXmlEncoding() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public boolean getXmlStandalone() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public void setXmlStandalone(boolean b) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public String getXmlVersion() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public void setXmlVersion(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public boolean getStrictErrorChecking() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public void setStrictErrorChecking(boolean b) { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public String getDocumentURI() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public void setDocumentURI(String s) { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Node adoptNode(Node node) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public DOMConfiguration getDomConfig() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public void normalizeDocument() { |
| } |
| |
| @NotNull |
| @Override |
| public Node renameNode(Node node, String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| } |
| |
| private static class DomElement extends DomNode implements Element { |
| private final XmlTag myTag; |
| @Nullable private NamedNodeMap myAttributes; |
| |
| private DomElement(@NotNull Document owner, @NotNull DomNode parent, @NotNull XmlTag tag) { |
| super(owner, parent, tag); |
| myTag = tag; |
| } |
| |
| // From org.w3c.dom.Node: |
| |
| @NotNull |
| @Override |
| public String getNodeName() { |
| return getTagName(); |
| } |
| |
| @Nullable |
| @Override |
| public String getNodeValue() throws DOMException { |
| return null; |
| } |
| |
| @Override |
| public short getNodeType() { |
| return Node.ELEMENT_NODE; |
| } |
| |
| @NotNull |
| @Override |
| public NamedNodeMap getAttributes() { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<NamedNodeMap>() { |
| @Override |
| public NamedNodeMap compute() { |
| return getAttributes(); |
| } |
| }); |
| } |
| |
| if (myAttributes == null) { |
| XmlAttribute[] attributes = myTag.getAttributes(); |
| if (attributes.length == 0) { |
| myAttributes = EMPTY_ATTRIBUTES; |
| } else { |
| myAttributes = new DomNamedNodeMap(this, attributes); |
| } |
| } |
| |
| return myAttributes; |
| } |
| |
| // From org.w3c.dom.Element: |
| |
| @NotNull |
| @Override |
| public String getTagName() { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getTagName(); |
| } |
| }); |
| } |
| |
| return myTag.getName(); |
| } |
| |
| @NotNull |
| @Override |
| public String getAttribute(@NotNull String name) { |
| Node node = getAttributes().getNamedItem(name); |
| if (node != null) { |
| return node.getNodeValue(); |
| } |
| return ""; |
| } |
| |
| @NotNull |
| @Override |
| public String getAttributeNS(@NotNull String namespace, @NotNull String name) throws DOMException { |
| Node node = getAttributes().getNamedItemNS(namespace, name); |
| if (node != null) { |
| return node.getNodeValue(); |
| } |
| return ""; |
| } |
| |
| @Nullable |
| @Override |
| public Attr getAttributeNodeNS(@NotNull String namespace, @NotNull String name) throws DOMException { |
| Node node = getAttributes().getNamedItemNS(namespace, name); |
| if (node != null) { |
| return (Attr)node; |
| } |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public Attr getAttributeNode(@NotNull String name) { |
| Node node = getAttributes().getNamedItem(name); |
| if (node != null) { |
| return (Attr)node; |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean hasAttribute(@NotNull String name) { |
| return getAttributes().getNamedItem(name) != null; |
| } |
| |
| @Override |
| public boolean hasAttributeNS(@NotNull String namespace, @NotNull String name) throws DOMException { |
| return getAttributes().getNamedItemNS(namespace, name) != null; |
| } |
| |
| @NotNull |
| @Override |
| public NodeList getElementsByTagName(@NotNull String s) { |
| NodeList childNodes = getChildNodes(); |
| if (childNodes == EMPTY) { |
| return EMPTY; |
| } |
| DomNodeList matches = new DomNodeList(); |
| for (int i = 0, n = childNodes.getLength(); i < n; i++) { |
| Node node = childNodes.item(i); |
| if (s.equals(node.getNodeName())) { |
| matches.add((DomNode)node); |
| } |
| } |
| |
| return matches; |
| } |
| |
| @NotNull |
| @Override |
| public NodeList getElementsByTagNameNS(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Attr setAttributeNode(Attr attr) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Attr removeAttributeNode(Attr attr) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public void setAttributeNS(String s, String s2, String s3) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public void removeAttributeNS(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public void setAttribute(String s, String s2) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public void removeAttribute(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Attr setAttributeNodeNS(Attr attr) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public TypeInfo getSchemaTypeInfo() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public void setIdAttribute(String s, boolean b) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public void setIdAttributeNS(String s, String s2, boolean b) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public void setIdAttributeNode(Attr attr, boolean b) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| } |
| |
| private static class DomText extends DomNode implements Text { |
| @NotNull private final XmlText myText; |
| |
| private DomText(@NotNull Document owner, @NotNull DomNode parent, @NotNull XmlText text) { |
| super(owner, parent, text); |
| myText = text; |
| } |
| |
| // From org.w3c.dom.Node: |
| |
| @Nullable |
| @Override |
| public String getNodeName() { |
| return null; |
| } |
| |
| @NotNull |
| @Override |
| public String getNodeValue() throws DOMException { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getNodeValue(); |
| } |
| }); |
| } |
| |
| return myText.getText(); |
| } |
| |
| @Override |
| public short getNodeType() { |
| return Node.TEXT_NODE; |
| } |
| |
| // From org.w3c.dom.Text: |
| |
| @NotNull |
| @Override |
| public Text splitText(int i) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @Override |
| public boolean isElementContentWhitespace() { |
| String s = myText.getText(); |
| for (int i = 0, n = s.length(); i < n; i++) { |
| if (!Character.isWhitespace(s.charAt(i))) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| @NotNull |
| @Override |
| public String getWholeText() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public Text replaceWholeText(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| } |
| |
| private static class DomComment extends DomNode implements Comment { |
| @NotNull private final XmlComment myComment; |
| |
| private DomComment(@NotNull Document owner, @NotNull DomNode parent, @NotNull XmlComment comment) { |
| super(owner, parent, comment); |
| myComment = comment; |
| } |
| |
| // From org.w3c.dom.Node: |
| |
| @Nullable |
| @Override |
| public String getNodeName() { |
| return null; |
| } |
| |
| @NotNull |
| @Override |
| public String getNodeValue() throws DOMException { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getNodeValue(); |
| } |
| }); |
| } |
| |
| return myComment.getText(); |
| } |
| |
| @Override |
| public short getNodeType() { |
| return Node.COMMENT_NODE; |
| } |
| |
| @NotNull |
| @Override |
| public String getTextContent() throws DOMException { |
| return getNodeValue(); |
| } |
| } |
| |
| private static class DomAttr extends DomNode implements Attr { |
| @NotNull private final DomElement myOwner; |
| @NotNull private final XmlAttribute myAttribute; |
| |
| private DomAttr(@NotNull Document document, @NotNull DomElement owner, @NotNull XmlAttribute attribute) { |
| super(document, null, attribute); |
| myOwner = owner; |
| myAttribute = attribute; |
| } |
| |
| // From org.w3c.dom.Node: |
| |
| @NotNull |
| @Override |
| public String getNodeName() { |
| return getName(); |
| } |
| |
| @NotNull |
| @Override |
| public String getNodeValue() throws DOMException { |
| return getValue(); |
| } |
| |
| @Override |
| public short getNodeType() { |
| return Node.ATTRIBUTE_NODE; |
| } |
| |
| // From org.w3c.dom.Attr: |
| |
| @NotNull |
| @Override |
| public String getName() { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getName(); |
| } |
| }); |
| } |
| return myAttribute.getName(); |
| } |
| |
| @Override |
| public boolean getSpecified() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @NotNull |
| @Override |
| public String getValue() { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getValue(); |
| } |
| }); |
| } |
| |
| String value = myAttribute.getValue(); |
| if (value == null) { |
| value = ""; |
| } |
| return value; |
| } |
| |
| @NotNull |
| @Override |
| public String getLocalName() { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getLocalName(); |
| } |
| }); |
| } |
| |
| return myAttribute.getLocalName(); |
| } |
| |
| @NotNull |
| @Override |
| public String getPrefix() { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getPrefix(); |
| } |
| }); |
| } |
| |
| return myAttribute.getNamespacePrefix(); |
| } |
| |
| @NotNull |
| @Override |
| public String getNamespaceURI() { |
| Application application = ApplicationManager.getApplication(); |
| if (!application.isReadAccessAllowed()) { |
| return application.runReadAction(new Computable<String>() { |
| @Override |
| public String compute() { |
| return getNamespaceURI(); |
| } |
| }); |
| } |
| |
| return myAttribute.getNamespace(); |
| } |
| |
| @Override |
| public void setValue(String s) throws DOMException { |
| throw new UnsupportedOperationException(); // Read-only bridge |
| } |
| |
| @NotNull |
| @Override |
| public Element getOwnerElement() { |
| return myOwner; |
| } |
| |
| @NotNull |
| @Override |
| public TypeInfo getSchemaTypeInfo() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| |
| @Override |
| public boolean isId() { |
| throw new UnsupportedOperationException(); // Not supported |
| } |
| } |
| } |