blob: caa75413bb8343c113c4210a499f473ba6257f5a [file] [log] [blame]
/*
* 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
}
}
}