org.mozilla.javascript.xmlimpl.XmlNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhino-runtime Show documentation
Show all versions of rhino-runtime Show documentation
Rhino JavaScript runtime jar, excludes tools & JSR-223 Script Engine wrapper.
The newest version!
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript.xmlimpl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.mozilla.javascript.Undefined;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.UserDataHandler;
class XmlNode implements Serializable {
private static final long serialVersionUID = 1L;
private static final String XML_NAMESPACES_NAMESPACE_URI = "http://www.w3.org/2000/xmlns/";
private static final String USER_DATA_XMLNODE_KEY = XmlNode.class.getName();
private static final boolean DOM_LEVEL_3 = true;
private static XmlNode getUserData(Node node) {
if (DOM_LEVEL_3) {
return (XmlNode) node.getUserData(USER_DATA_XMLNODE_KEY);
}
return null;
}
private static void setUserData(Node node, XmlNode wrap) {
if (DOM_LEVEL_3) {
node.setUserData(USER_DATA_XMLNODE_KEY, wrap, wrap.events);
}
}
private static XmlNode createImpl(Node node) {
if (node instanceof Document) throw new IllegalArgumentException();
XmlNode rv = null;
if (getUserData(node) == null) {
rv = new XmlNode();
rv.dom = node;
setUserData(node, rv);
} else {
rv = getUserData(node);
}
return rv;
}
static XmlNode newElementWithText(
XmlProcessor processor, XmlNode reference, XmlNode.QName qname, String value) {
if (reference instanceof org.w3c.dom.Document)
throw new IllegalArgumentException("Cannot use Document node as reference");
Document document = null;
if (reference != null) {
document = reference.dom.getOwnerDocument();
} else {
document = processor.newDocument();
}
Node referenceDom = (reference != null) ? reference.dom : null;
Namespace ns = qname.getNamespace();
Element e =
(ns == null || ns.getUri().length() == 0)
? document.createElementNS(null, qname.getLocalName())
: document.createElementNS(ns.getUri(), qname.qualify(referenceDom));
if (value != null) {
e.appendChild(document.createTextNode(value));
}
return XmlNode.createImpl(e);
}
static XmlNode createText(XmlProcessor processor, String value) {
return createImpl(processor.newDocument().createTextNode(value));
}
static XmlNode createElementFromNode(Node node) {
if (node instanceof Document) node = ((Document) node).getDocumentElement();
return createImpl(node);
}
static XmlNode createElement(XmlProcessor processor, String namespaceUri, String xml)
throws org.xml.sax.SAXException {
return createImpl(processor.toXml(namespaceUri, xml));
}
static XmlNode createEmpty(XmlProcessor processor) {
return createText(processor, "");
}
private static XmlNode copy(XmlNode other) {
return createImpl(other.dom.cloneNode(true));
}
private UserDataHandler events = new XmlNodeUserDataHandler();
private Node dom;
private XML xml;
private XmlNode() {}
String debug() {
XmlProcessor raw = new XmlProcessor();
raw.setIgnoreComments(false);
raw.setIgnoreProcessingInstructions(false);
raw.setIgnoreWhitespace(false);
raw.setPrettyPrinting(false);
return raw.ecmaToXmlString(this.dom);
}
@Override
public String toString() {
return "XmlNode: type=" + dom.getNodeType() + " dom=" + dom.toString();
}
XML getXml() {
return xml;
}
void setXml(XML xml) {
this.xml = xml;
}
int getChildCount() {
return this.dom.getChildNodes().getLength();
}
XmlNode parent() {
Node domParent = dom.getParentNode();
if (domParent instanceof Document) return null;
if (domParent == null) return null;
return createImpl(domParent);
}
int getChildIndex() {
if (this.isAttributeType()) return -1;
if (parent() == null) return -1;
org.w3c.dom.NodeList siblings = this.dom.getParentNode().getChildNodes();
for (int i = 0; i < siblings.getLength(); i++) {
if (siblings.item(i) == dom) {
return i;
}
}
// Either the parent is -1 or one of the this node's parent's children is this node.
throw new RuntimeException("Unreachable.");
}
void removeChild(int index) {
this.dom.removeChild(this.dom.getChildNodes().item(index));
}
String toXmlString(XmlProcessor processor) {
return processor.ecmaToXmlString(this.dom);
}
String ecmaValue() {
// TODO See ECMA 357 Section 9.1
if (isTextType()) {
return ((org.w3c.dom.Text) dom).getData();
} else if (isAttributeType()) {
return ((org.w3c.dom.Attr) dom).getValue();
} else if (isProcessingInstructionType()) {
return ((org.w3c.dom.ProcessingInstruction) dom).getData();
} else if (isCommentType()) {
return ((org.w3c.dom.Comment) dom).getNodeValue();
} else if (isElementType()) {
throw new RuntimeException("Unimplemented ecmaValue() for elements.");
} else {
throw new RuntimeException("Unimplemented for node " + dom);
}
}
void deleteMe() {
if (dom instanceof Attr) {
Attr attr = (Attr) this.dom;
attr.getOwnerElement()
.getAttributes()
.removeNamedItemNS(attr.getNamespaceURI(), attr.getLocalName());
} else {
if (this.dom.getParentNode() != null) {
this.dom.getParentNode().removeChild(this.dom);
} else {
// This case can be exercised at least when executing the regression
// tests under https://bugzilla.mozilla.org/show_bug.cgi?id=354145
}
}
}
void normalize() {
this.dom.normalize();
}
void insertChildAt(int index, XmlNode node) {
Node parent = this.dom;
Node child = parent.getOwnerDocument().importNode(node.dom, true);
if (parent.getChildNodes().getLength() < index) {
// TODO Check ECMA for what happens here
throw new IllegalArgumentException(
"index=" + index + " length=" + parent.getChildNodes().getLength());
}
if (parent.getChildNodes().getLength() == index) {
parent.appendChild(child);
} else {
parent.insertBefore(child, parent.getChildNodes().item(index));
}
}
void insertChildrenAt(int index, XmlNode[] nodes) {
for (int i = 0; i < nodes.length; i++) {
insertChildAt(index + i, nodes[i]);
}
}
XmlNode getChild(int index) {
Node child = dom.getChildNodes().item(index);
return createImpl(child);
}
// Helper method for XML.hasSimpleContent()
boolean hasChildElement() {
org.w3c.dom.NodeList nodes = this.dom.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
if (nodes.item(i).getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) return true;
}
return false;
}
boolean isSameNode(XmlNode other) {
// TODO May need to be changed if we allow XmlNode to refer to several Node objects
return this.dom == other.dom;
}
private String toUri(String ns) {
return (ns == null) ? "" : ns;
}
private void addNamespaces(Namespaces rv, Element element) {
if (element == null) throw new RuntimeException("element must not be null");
String myDefaultNamespace = toUri(element.lookupNamespaceURI(null));
String parentDefaultNamespace = "";
if (element.getParentNode() != null) {
parentDefaultNamespace = toUri(element.getParentNode().lookupNamespaceURI(null));
}
if (!myDefaultNamespace.equals(parentDefaultNamespace)
|| !(element.getParentNode() instanceof Element)) {
rv.declare(Namespace.create("", myDefaultNamespace));
}
NamedNodeMap attributes = element.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Attr attr = (Attr) attributes.item(i);
if (attr.getPrefix() != null && attr.getPrefix().equals("xmlns")) {
rv.declare(Namespace.create(attr.getLocalName(), attr.getValue()));
}
}
}
private Namespaces getAllNamespaces() {
Namespaces rv = new Namespaces();
Node target = this.dom;
if (target instanceof Attr) {
target = ((Attr) target).getOwnerElement();
}
while (target != null) {
if (target instanceof Element) {
addNamespaces(rv, (Element) target);
}
target = target.getParentNode();
}
// Fallback in case no namespace was declared
rv.declare(Namespace.create("", ""));
return rv;
}
Namespace[] getInScopeNamespaces() {
Namespaces rv = getAllNamespaces();
return rv.getNamespaces();
}
Namespace[] getNamespaceDeclarations() {
// ECMA357 13.4.4.24
if (this.dom instanceof Element) {
Namespaces rv = new Namespaces();
addNamespaces(rv, (Element) this.dom);
return rv.getNamespaces();
} else {
return new Namespace[0];
}
}
Namespace getNamespaceDeclaration(String prefix) {
if (prefix.equals("") && dom instanceof Attr) {
// Default namespaces do not apply to attributes; see XML Namespaces section 5.2
return Namespace.create("", "");
}
Namespaces rv = getAllNamespaces();
return rv.getNamespace(prefix);
}
Namespace getNamespaceDeclaration() {
if (dom.getPrefix() == null) return getNamespaceDeclaration("");
return getNamespaceDeclaration(dom.getPrefix());
}
static class XmlNodeUserDataHandler implements UserDataHandler, Serializable {
private static final long serialVersionUID = 4666895518900769588L;
@Override
public void handle(short operation, String key, Object data, Node src, Node dest) {}
}
private static class Namespaces {
private Map map = new HashMap();
private Map uriToPrefix = new HashMap();
Namespaces() {}
void declare(Namespace n) {
if (map.get(n.prefix) == null) {
map.put(n.prefix, n.uri);
}
// TODO I think this is analogous to the other way, but have not really thought it
// through ... should local scope
// matter more than outer scope?
if (uriToPrefix.get(n.uri) == null) {
uriToPrefix.put(n.uri, n.prefix);
}
}
Namespace getNamespaceByUri(String uri) {
if (uriToPrefix.get(uri) == null) return null;
return Namespace.create(uri, uriToPrefix.get(uri));
}
Namespace getNamespace(String prefix) {
if (map.get(prefix) == null) return null;
return Namespace.create(prefix, map.get(prefix));
}
Namespace[] getNamespaces() {
ArrayList rv = new ArrayList();
for (Map.Entry e : map.entrySet()) {
Namespace n = Namespace.create(e.getKey(), e.getValue());
if (!n.isEmpty()) {
rv.add(n);
}
}
return rv.toArray(new Namespace[rv.size()]);
}
}
final XmlNode copy() {
return copy(this);
}
// Returns whether this node is capable of being a parent
final boolean isParentType() {
return isElementType();
}
final boolean isTextType() {
return dom.getNodeType() == Node.TEXT_NODE || dom.getNodeType() == Node.CDATA_SECTION_NODE;
}
final boolean isAttributeType() {
return dom.getNodeType() == Node.ATTRIBUTE_NODE;
}
final boolean isProcessingInstructionType() {
return dom.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE;
}
final boolean isCommentType() {
return dom.getNodeType() == Node.COMMENT_NODE;
}
final boolean isElementType() {
return dom.getNodeType() == Node.ELEMENT_NODE;
}
final void renameNode(QName qname) {
this.dom =
dom.getOwnerDocument()
.renameNode(dom, qname.getNamespace().getUri(), qname.qualify(dom));
}
void invalidateNamespacePrefix() {
if (!(dom instanceof Element)) throw new IllegalStateException();
String prefix = this.dom.getPrefix();
QName after = QName.create(this.dom.getNamespaceURI(), this.dom.getLocalName(), null);
renameNode(after);
NamedNodeMap attrs = this.dom.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
if (attrs.item(i).getPrefix().equals(prefix)) {
createImpl(attrs.item(i))
.renameNode(
QName.create(
attrs.item(i).getNamespaceURI(),
attrs.item(i).getLocalName(),
null));
}
}
}
private void declareNamespace(Element e, String prefix, String uri) {
if (prefix.length() > 0) {
e.setAttributeNS(XML_NAMESPACES_NAMESPACE_URI, "xmlns:" + prefix, uri);
} else {
e.setAttribute("xmlns", uri);
}
}
void declareNamespace(String prefix, String uri) {
if (!(dom instanceof Element)) throw new IllegalStateException();
if (dom.lookupNamespaceURI(uri) != null && dom.lookupNamespaceURI(uri).equals(prefix)) {
// do nothing
} else {
Element e = (Element) dom;
declareNamespace(e, prefix, uri);
}
}
private Namespace getDefaultNamespace() {
String prefix = "";
String uri = (dom.lookupNamespaceURI(null) == null) ? "" : dom.lookupNamespaceURI(null);
return Namespace.create(prefix, uri);
}
private String getExistingPrefixFor(Namespace namespace) {
if (getDefaultNamespace().getUri().equals(namespace.getUri())) {
return "";
}
return dom.lookupPrefix(namespace.getUri());
}
private Namespace getNodeNamespace() {
String uri = dom.getNamespaceURI();
String prefix = dom.getPrefix();
if (uri == null) uri = "";
if (prefix == null) prefix = "";
return Namespace.create(prefix, uri);
}
Namespace getNamespace() {
return getNodeNamespace();
}
void removeNamespace(Namespace namespace) {
Namespace current = getNodeNamespace();
// Do not remove in-use namespace
if (namespace.is(current)) return;
NamedNodeMap attrs = this.dom.getAttributes();
for (int i = 0; i < attrs.getLength(); i++) {
XmlNode attr = XmlNode.createImpl(attrs.item(i));
if (namespace.is(attr.getNodeNamespace())) return;
}
// TODO I must confess I am not sure I understand the spec fully. See ECMA357
// 13.4.4.31
String existingPrefix = getExistingPrefixFor(namespace);
if (existingPrefix != null) {
if (namespace.isUnspecifiedPrefix()) {
// we should remove any namespace with this URI from scope; we do this by
// declaring a namespace with the same
// prefix as the existing prefix and setting its URI to the default namespace
declareNamespace(existingPrefix, getDefaultNamespace().getUri());
} else {
if (existingPrefix.equals(namespace.getPrefix())) {
declareNamespace(existingPrefix, getDefaultNamespace().getUri());
}
}
} else {
// the argument namespace is not declared in this scope, so do nothing.
}
}
private void setProcessingInstructionName(String localName) {
org.w3c.dom.ProcessingInstruction pi = (ProcessingInstruction) this.dom;
// We cannot set the node name; Document.renameNode() only supports elements and
// attributes. So we replace it
pi.getParentNode()
.replaceChild(
pi,
pi.getOwnerDocument().createProcessingInstruction(localName, pi.getData()));
}
final void setLocalName(String localName) {
if (dom instanceof ProcessingInstruction) {
setProcessingInstructionName(localName);
} else {
String prefix = dom.getPrefix();
if (prefix == null) prefix = "";
this.dom =
dom.getOwnerDocument()
.renameNode(
dom, dom.getNamespaceURI(), QName.qualify(prefix, localName));
}
}
final QName getQname() {
String uri = (dom.getNamespaceURI()) == null ? "" : dom.getNamespaceURI();
String prefix = (dom.getPrefix() == null) ? "" : dom.getPrefix();
return QName.create(uri, dom.getLocalName(), prefix);
}
void addMatchingChildren(XMLList result, XmlNode.Filter filter) {
Node node = this.dom;
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node childnode = children.item(i);
XmlNode child = XmlNode.createImpl(childnode);
if (filter.accept(childnode)) {
result.addToList(child);
}
}
}
XmlNode[] getMatchingChildren(Filter filter) {
ArrayList rv = new ArrayList();
NodeList nodes = this.dom.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (filter.accept(node)) {
rv.add(createImpl(node));
}
}
return rv.toArray(new XmlNode[rv.size()]);
}
XmlNode[] getAttributes() {
NamedNodeMap attrs = this.dom.getAttributes();
// TODO Or could make callers handle null?
if (attrs == null) throw new IllegalStateException("Must be element.");
XmlNode[] rv = new XmlNode[attrs.getLength()];
for (int i = 0; i < attrs.getLength(); i++) {
rv[i] = createImpl(attrs.item(i));
}
return rv;
}
String getAttributeValue() {
return ((Attr) dom).getValue();
}
void setAttribute(QName name, String value) {
if (!(dom instanceof Element))
throw new IllegalStateException("Can only set attribute on elements.");
name.setAttribute((Element) dom, value);
}
void replaceWith(XmlNode other) {
Node replacement = this.dom.getOwnerDocument().importNode(other.dom, true);
this.dom.getParentNode().replaceChild(replacement, this.dom);
}
String ecmaToXMLString(XmlProcessor processor) {
if (this.isElementType()) {
Element copy = (Element) this.dom.cloneNode(true);
Namespace[] inScope = this.getInScopeNamespaces();
for (int i = 0; i < inScope.length; i++) {
declareNamespace(copy, inScope[i].getPrefix(), inScope[i].getUri());
}
return processor.ecmaToXmlString(copy);
} else {
return processor.ecmaToXmlString(dom);
}
}
static class Namespace implements Serializable {
/** Serial version id for Namespace with fields prefix and uri */
private static final long serialVersionUID = 4073904386884677090L;
static Namespace create(String prefix, String uri) {
if (prefix == null) {
throw new IllegalArgumentException(
"Empty string represents default namespace prefix");
}
if (uri == null) {
throw new IllegalArgumentException("Namespace may not lack a URI");
}
Namespace rv = new Namespace();
rv.prefix = prefix;
rv.uri = uri;
return rv;
}
static Namespace create(String uri) {
Namespace rv = new Namespace();
rv.uri = uri;
// Avoid null prefix for "" namespace
if (uri == null || uri.length() == 0) {
rv.prefix = "";
}
return rv;
}
static final Namespace GLOBAL = create("", "");
private String prefix;
private String uri;
private Namespace() {}
@Override
public String toString() {
if (prefix == null) return "XmlNode.Namespace [" + uri + "]";
return "XmlNode.Namespace [" + prefix + "{" + uri + "}]";
}
boolean isUnspecifiedPrefix() {
return prefix == null;
}
boolean is(Namespace other) {
return this.prefix != null
&& other.prefix != null
&& this.prefix.equals(other.prefix)
&& this.uri.equals(other.uri);
}
boolean isEmpty() {
return prefix != null && prefix.equals("") && uri.equals("");
}
boolean isDefault() {
return prefix != null && prefix.equals("");
}
boolean isGlobal() {
return uri != null && uri.equals("");
}
// Called by QName
// TODO Move functionality from QName lookupPrefix to here
private void setPrefix(String prefix) {
if (prefix == null) throw new IllegalArgumentException();
this.prefix = prefix;
}
String getPrefix() {
return prefix;
}
String getUri() {
return uri;
}
}
// TODO Where is this class used? No longer using it in QName implementation
static class QName implements Serializable {
private static final long serialVersionUID = -6587069811691451077L;
static QName create(Namespace namespace, String localName) {
// A null namespace indicates a wild-card match for any namespace
// A null localName indicates "*" from the point of view of ECMA357
if (localName != null && localName.equals("*"))
throw new RuntimeException("* is not valid localName");
QName rv = new QName();
rv.namespace = namespace;
rv.localName = localName;
return rv;
}
/** @deprecated */
@Deprecated
static QName create(String uri, String localName, String prefix) {
return create(Namespace.create(prefix, uri), localName);
}
static String qualify(String prefix, String localName) {
if (prefix == null) throw new IllegalArgumentException("prefix must not be null");
if (prefix.length() > 0) return prefix + ":" + localName;
return localName;
}
private Namespace namespace;
private String localName;
private QName() {}
@Override
public String toString() {
return "XmlNode.QName [" + localName + "," + namespace + "]";
}
private boolean equals(String one, String two) {
if (one == null && two == null) return true;
if (one == null || two == null) return false;
return one.equals(two);
}
private boolean namespacesEqual(Namespace one, Namespace two) {
if (one == null && two == null) return true;
if (one == null || two == null) return false;
return equals(one.getUri(), two.getUri());
}
final boolean equals(QName other) {
if (!namespacesEqual(this.namespace, other.namespace)) return false;
if (!equals(this.localName, other.localName)) return false;
return true;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof QName)) {
return false;
}
return equals((QName) obj);
}
@Override
public int hashCode() {
return localName == null ? 0 : localName.hashCode();
}
void lookupPrefix(org.w3c.dom.Node node) {
if (node == null) throw new IllegalArgumentException("node must not be null");
String prefix = node.lookupPrefix(namespace.getUri());
if (prefix == null) {
// check to see if we match the default namespace
String defaultNamespace = node.lookupNamespaceURI(null);
if (defaultNamespace == null) defaultNamespace = "";
String nodeNamespace = namespace.getUri();
if (nodeNamespace.equals(defaultNamespace)) {
prefix = "";
}
}
int i = 0;
while (prefix == null) {
String generatedPrefix = "e4x_" + i++;
String generatedUri = node.lookupNamespaceURI(generatedPrefix);
if (generatedUri == null) {
prefix = generatedPrefix;
org.w3c.dom.Node top = node;
while (top.getParentNode() != null
&& top.getParentNode() instanceof org.w3c.dom.Element) {
top = top.getParentNode();
}
((org.w3c.dom.Element) top)
.setAttributeNS(
"http://www.w3.org/2000/xmlns/",
"xmlns:" + prefix,
namespace.getUri());
}
}
namespace.setPrefix(prefix);
}
String qualify(org.w3c.dom.Node node) {
if (namespace.getPrefix() == null) {
if (node != null) {
lookupPrefix(node);
} else {
namespace.setPrefix("");
}
}
return qualify(namespace.getPrefix(), localName);
}
void setAttribute(org.w3c.dom.Element element, String value) {
if (namespace.getPrefix() == null) lookupPrefix(element);
element.setAttributeNS(
namespace.getUri(), qualify(namespace.getPrefix(), localName), value);
}
Namespace getNamespace() {
return namespace;
}
String getLocalName() {
return localName;
}
}
static class InternalList implements Serializable {
private static final long serialVersionUID = -3633151157292048978L;
private List list;
InternalList() {
list = new ArrayList();
}
private void _add(XmlNode n) {
list.add(n);
}
XmlNode item(int index) {
return list.get(index);
}
void remove(int index) {
list.remove(index);
}
void add(InternalList other) {
for (int i = 0; i < other.length(); i++) {
_add(other.item(i));
}
}
void add(InternalList from, int startInclusive, int endExclusive) {
for (int i = startInclusive; i < endExclusive; i++) {
_add(from.item(i));
}
}
void add(XmlNode node) {
_add(node);
}
/* TODO: was marked deprecated by original author */
void add(XML xml) {
_add(xml.getAnnotation());
}
/* TODO: was marked deprecated by original author */
void addToList(Object toAdd) {
if (toAdd instanceof Undefined) {
// Missing argument do nothing...
return;
}
if (toAdd instanceof XMLList) {
XMLList xmlSrc = (XMLList) toAdd;
for (int i = 0; i < xmlSrc.length(); i++) {
this._add((xmlSrc.item(i)).getAnnotation());
}
} else if (toAdd instanceof XML) {
this._add(((XML) (toAdd)).getAnnotation());
} else if (toAdd instanceof XmlNode) {
this._add((XmlNode) toAdd);
}
}
int length() {
return list.size();
}
}
abstract static class Filter {
static final Filter COMMENT =
new Filter() {
@Override
boolean accept(Node node) {
return node.getNodeType() == Node.COMMENT_NODE;
}
};
static final Filter TEXT =
new Filter() {
@Override
boolean accept(Node node) {
return node.getNodeType() == Node.TEXT_NODE;
}
};
static Filter PROCESSING_INSTRUCTION(final XMLName name) {
return new Filter() {
@Override
boolean accept(Node node) {
if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) {
ProcessingInstruction pi = (ProcessingInstruction) node;
return name.matchesLocalName(pi.getTarget());
}
return false;
}
};
}
static Filter ELEMENT =
new Filter() {
@Override
boolean accept(Node node) {
return node.getNodeType() == Node.ELEMENT_NODE;
}
};
static Filter TRUE =
new Filter() {
@Override
boolean accept(Node node) {
return true;
}
};
abstract boolean accept(Node node);
}
// Support experimental Java interface
org.w3c.dom.Node toDomNode() {
return this.dom;
}
}