summaryrefslogtreecommitdiffstats
path: root/server/src/org/jsoup/nodes/Node.java
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/org/jsoup/nodes/Node.java')
-rw-r--r--server/src/org/jsoup/nodes/Node.java727
1 files changed, 0 insertions, 727 deletions
diff --git a/server/src/org/jsoup/nodes/Node.java b/server/src/org/jsoup/nodes/Node.java
deleted file mode 100644
index 72b8dcbd47..0000000000
--- a/server/src/org/jsoup/nodes/Node.java
+++ /dev/null
@@ -1,727 +0,0 @@
-package org.jsoup.nodes;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.jsoup.helper.StringUtil;
-import org.jsoup.helper.Validate;
-import org.jsoup.parser.Parser;
-import org.jsoup.select.NodeTraversor;
-import org.jsoup.select.NodeVisitor;
-
-/**
- * The base, abstract Node model. Elements, Documents, Comments etc are all Node
- * instances.
- *
- * @author Jonathan Hedley, jonathan@hedley.net
- */
-public abstract class Node implements Cloneable {
- Node parentNode;
- List<Node> childNodes;
- Attributes attributes;
- String baseUri;
- int siblingIndex;
-
- /**
- * Create a new Node.
- *
- * @param baseUri
- * base URI
- * @param attributes
- * attributes (not null, but may be empty)
- */
- protected Node(String baseUri, Attributes attributes) {
- Validate.notNull(baseUri);
- Validate.notNull(attributes);
-
- childNodes = new ArrayList<Node>(4);
- this.baseUri = baseUri.trim();
- this.attributes = attributes;
- }
-
- protected Node(String baseUri) {
- this(baseUri, new Attributes());
- }
-
- /**
- * Default constructor. Doesn't setup base uri, children, or attributes; use
- * with caution.
- */
- protected Node() {
- childNodes = Collections.emptyList();
- attributes = null;
- }
-
- /**
- * Get the node name of this node. Use for debugging purposes and not logic
- * switching (for that, use instanceof).
- *
- * @return node name
- */
- public abstract String nodeName();
-
- /**
- * Get an attribute's value by its key.
- * <p/>
- * To get an absolute URL from an attribute that may be a relative URL,
- * prefix the key with <code><b>abs</b></code>, which is a shortcut to the
- * {@link #absUrl} method. E.g.: <blockquote>
- * <code>String url = a.attr("abs:href");</code></blockquote>
- *
- * @param attributeKey
- * The attribute key.
- * @return The attribute, or empty string if not present (to avoid nulls).
- * @see #attributes()
- * @see #hasAttr(String)
- * @see #absUrl(String)
- */
- public String attr(String attributeKey) {
- Validate.notNull(attributeKey);
-
- if (attributes.hasKey(attributeKey)) {
- return attributes.get(attributeKey);
- } else if (attributeKey.toLowerCase().startsWith("abs:")) {
- return absUrl(attributeKey.substring("abs:".length()));
- } else {
- return "";
- }
- }
-
- /**
- * Get all of the element's attributes.
- *
- * @return attributes (which implements iterable, in same order as presented
- * in original HTML).
- */
- public Attributes attributes() {
- return attributes;
- }
-
- /**
- * Set an attribute (key=value). If the attribute already exists, it is
- * replaced.
- *
- * @param attributeKey
- * The attribute key.
- * @param attributeValue
- * The attribute value.
- * @return this (for chaining)
- */
- public Node attr(String attributeKey, String attributeValue) {
- attributes.put(attributeKey, attributeValue);
- return this;
- }
-
- /**
- * Test if this element has an attribute.
- *
- * @param attributeKey
- * The attribute key to check.
- * @return true if the attribute exists, false if not.
- */
- public boolean hasAttr(String attributeKey) {
- Validate.notNull(attributeKey);
-
- if (attributeKey.toLowerCase().startsWith("abs:")) {
- String key = attributeKey.substring("abs:".length());
- if (attributes.hasKey(key) && !absUrl(key).equals("")) {
- return true;
- }
- }
- return attributes.hasKey(attributeKey);
- }
-
- /**
- * Remove an attribute from this element.
- *
- * @param attributeKey
- * The attribute to remove.
- * @return this (for chaining)
- */
- public Node removeAttr(String attributeKey) {
- Validate.notNull(attributeKey);
- attributes.remove(attributeKey);
- return this;
- }
-
- /**
- * Get the base URI of this node.
- *
- * @return base URI
- */
- public String baseUri() {
- return baseUri;
- }
-
- /**
- * Update the base URI of this node and all of its descendants.
- *
- * @param baseUri
- * base URI to set
- */
- public void setBaseUri(final String baseUri) {
- Validate.notNull(baseUri);
-
- traverse(new NodeVisitor() {
- @Override
- public void head(Node node, int depth) {
- node.baseUri = baseUri;
- }
-
- @Override
- public void tail(Node node, int depth) {
- }
- });
- }
-
- /**
- * Get an absolute URL from a URL attribute that may be relative (i.e. an
- * <code>&lt;a href></code> or <code>&lt;img src></code>).
- * <p/>
- * E.g.: <code>String absUrl = linkEl.absUrl("href");</code>
- * <p/>
- * If the attribute value is already absolute (i.e. it starts with a
- * protocol, like <code>http://</code> or <code>https://</code> etc), and it
- * successfully parses as a URL, the attribute is returned directly.
- * Otherwise, it is treated as a URL relative to the element's
- * {@link #baseUri}, and made absolute using that.
- * <p/>
- * As an alternate, you can use the {@link #attr} method with the
- * <code>abs:</code> prefix, e.g.:
- * <code>String absUrl = linkEl.attr("abs:href");</code>
- *
- * @param attributeKey
- * The attribute key
- * @return An absolute URL if one could be made, or an empty string (not
- * null) if the attribute was missing or could not be made
- * successfully into a URL.
- * @see #attr
- * @see java.net.URL#URL(java.net.URL, String)
- */
- public String absUrl(String attributeKey) {
- Validate.notEmpty(attributeKey);
-
- String relUrl = attr(attributeKey);
- if (!hasAttr(attributeKey)) {
- return ""; // nothing to make absolute with
- } else {
- URL base;
- try {
- try {
- base = new URL(baseUri);
- } catch (MalformedURLException e) {
- // the base is unsuitable, but the attribute may be abs on
- // its own, so try that
- URL abs = new URL(relUrl);
- return abs.toExternalForm();
- }
- // workaround: java resolves '//path/file + ?foo' to
- // '//path/?foo', not '//path/file?foo' as desired
- if (relUrl.startsWith("?")) {
- relUrl = base.getPath() + relUrl;
- }
- URL abs = new URL(base, relUrl);
- return abs.toExternalForm();
- } catch (MalformedURLException e) {
- return "";
- }
- }
- }
-
- /**
- * Get a child node by index
- *
- * @param index
- * index of child node
- * @return the child node at this index.
- */
- public Node childNode(int index) {
- return childNodes.get(index);
- }
-
- /**
- * Get this node's children. Presented as an unmodifiable list: new children
- * can not be added, but the child nodes themselves can be manipulated.
- *
- * @return list of children. If no children, returns an empty list.
- */
- public List<Node> childNodes() {
- return Collections.unmodifiableList(childNodes);
- }
-
- protected Node[] childNodesAsArray() {
- return childNodes.toArray(new Node[childNodes().size()]);
- }
-
- /**
- * Gets this node's parent node.
- *
- * @return parent node; or null if no parent.
- */
- public Node parent() {
- return parentNode;
- }
-
- /**
- * Gets the Document associated with this Node.
- *
- * @return the Document associated with this Node, or null if there is no
- * such Document.
- */
- public Document ownerDocument() {
- if (this instanceof Document) {
- return (Document) this;
- } else if (parentNode == null) {
- return null;
- } else {
- return parentNode.ownerDocument();
- }
- }
-
- /**
- * Remove (delete) this node from the DOM tree. If this node has children,
- * they are also removed.
- */
- public void remove() {
- Validate.notNull(parentNode);
- parentNode.removeChild(this);
- }
-
- /**
- * Insert the specified HTML into the DOM before this node (i.e. as a
- * preceding sibling).
- *
- * @param html
- * HTML to add before this node
- * @return this node, for chaining
- * @see #after(String)
- */
- public Node before(String html) {
- addSiblingHtml(siblingIndex(), html);
- return this;
- }
-
- /**
- * Insert the specified node into the DOM before this node (i.e. as a
- * preceding sibling).
- *
- * @param node
- * to add before this node
- * @return this node, for chaining
- * @see #after(Node)
- */
- public Node before(Node node) {
- Validate.notNull(node);
- Validate.notNull(parentNode);
-
- parentNode.addChildren(siblingIndex(), node);
- return this;
- }
-
- /**
- * Insert the specified HTML into the DOM after this node (i.e. as a
- * following sibling).
- *
- * @param html
- * HTML to add after this node
- * @return this node, for chaining
- * @see #before(String)
- */
- public Node after(String html) {
- addSiblingHtml(siblingIndex() + 1, html);
- return this;
- }
-
- /**
- * Insert the specified node into the DOM after this node (i.e. as a
- * following sibling).
- *
- * @param node
- * to add after this node
- * @return this node, for chaining
- * @see #before(Node)
- */
- public Node after(Node node) {
- Validate.notNull(node);
- Validate.notNull(parentNode);
-
- parentNode.addChildren(siblingIndex() + 1, node);
- return this;
- }
-
- private void addSiblingHtml(int index, String html) {
- Validate.notNull(html);
- Validate.notNull(parentNode);
-
- Element context = parent() instanceof Element ? (Element) parent()
- : null;
- List<Node> nodes = Parser.parseFragment(html, context, baseUri());
- parentNode.addChildren(index, nodes.toArray(new Node[nodes.size()]));
- }
-
- /**
- * Wrap the supplied HTML around this node.
- *
- * @param html
- * HTML to wrap around this element, e.g.
- * {@code <div class="head"></div>}. Can be arbitrarily deep.
- * @return this node, for chaining.
- */
- public Node wrap(String html) {
- Validate.notEmpty(html);
-
- Element context = parent() instanceof Element ? (Element) parent()
- : null;
- List<Node> wrapChildren = Parser
- .parseFragment(html, context, baseUri());
- Node wrapNode = wrapChildren.get(0);
- if (wrapNode == null || !(wrapNode instanceof Element)) {
- return null;
- }
-
- Element wrap = (Element) wrapNode;
- Element deepest = getDeepChild(wrap);
- parentNode.replaceChild(this, wrap);
- deepest.addChildren(this);
-
- // remainder (unbalanced wrap, like <div></div><p></p> -- The <p> is
- // remainder
- if (wrapChildren.size() > 0) {
- for (int i = 0; i < wrapChildren.size(); i++) {
- Node remainder = wrapChildren.get(i);
- remainder.parentNode.removeChild(remainder);
- wrap.appendChild(remainder);
- }
- }
- return this;
- }
-
- /**
- * Removes this node from the DOM, and moves its children up into the node's
- * parent. This has the effect of dropping the node but keeping its
- * children.
- * <p/>
- * For example, with the input html:<br/>
- * {@code <div>One <span>Two <b>Three</b></span></div>}<br/>
- * Calling {@code element.unwrap()} on the {@code span} element will result
- * in the html:<br/>
- * {@code <div>One Two <b>Three</b></div>}<br/>
- * and the {@code "Two "} {@link TextNode} being returned.
- *
- * @return the first child of this node, after the node has been unwrapped.
- * Null if the node had no children.
- * @see #remove()
- * @see #wrap(String)
- */
- public Node unwrap() {
- Validate.notNull(parentNode);
-
- int index = siblingIndex;
- Node firstChild = childNodes.size() > 0 ? childNodes.get(0) : null;
- parentNode.addChildren(index, childNodesAsArray());
- remove();
-
- return firstChild;
- }
-
- private Element getDeepChild(Element el) {
- List<Element> children = el.children();
- if (children.size() > 0) {
- return getDeepChild(children.get(0));
- } else {
- return el;
- }
- }
-
- /**
- * Replace this node in the DOM with the supplied node.
- *
- * @param in
- * the node that will will replace the existing node.
- */
- public void replaceWith(Node in) {
- Validate.notNull(in);
- Validate.notNull(parentNode);
- parentNode.replaceChild(this, in);
- }
-
- protected void setParentNode(Node parentNode) {
- if (this.parentNode != null) {
- this.parentNode.removeChild(this);
- }
- this.parentNode = parentNode;
- }
-
- protected void replaceChild(Node out, Node in) {
- Validate.isTrue(out.parentNode == this);
- Validate.notNull(in);
- if (in.parentNode != null) {
- in.parentNode.removeChild(in);
- }
-
- Integer index = out.siblingIndex();
- childNodes.set(index, in);
- in.parentNode = this;
- in.setSiblingIndex(index);
- out.parentNode = null;
- }
-
- protected void removeChild(Node out) {
- Validate.isTrue(out.parentNode == this);
- int index = out.siblingIndex();
- childNodes.remove(index);
- reindexChildren();
- out.parentNode = null;
- }
-
- protected void addChildren(Node... children) {
- // most used. short circuit addChildren(int), which hits reindex
- // children and array copy
- for (Node child : children) {
- reparentChild(child);
- childNodes.add(child);
- child.setSiblingIndex(childNodes.size() - 1);
- }
- }
-
- protected void addChildren(int index, Node... children) {
- Validate.noNullElements(children);
- for (int i = children.length - 1; i >= 0; i--) {
- Node in = children[i];
- reparentChild(in);
- childNodes.add(index, in);
- }
- reindexChildren();
- }
-
- private void reparentChild(Node child) {
- if (child.parentNode != null) {
- child.parentNode.removeChild(child);
- }
- child.setParentNode(this);
- }
-
- private void reindexChildren() {
- for (int i = 0; i < childNodes.size(); i++) {
- childNodes.get(i).setSiblingIndex(i);
- }
- }
-
- /**
- * Retrieves this node's sibling nodes. Similar to {@link #childNodes()
- * node.parent.childNodes()}, but does not include this node (a node is not
- * a sibling of itself).
- *
- * @return node siblings. If the node has no parent, returns an empty list.
- */
- public List<Node> siblingNodes() {
- if (parentNode == null) {
- return Collections.emptyList();
- }
-
- List<Node> nodes = parentNode.childNodes;
- List<Node> siblings = new ArrayList<Node>(nodes.size() - 1);
- for (Node node : nodes) {
- if (node != this) {
- siblings.add(node);
- }
- }
- return siblings;
- }
-
- /**
- * Get this node's next sibling.
- *
- * @return next sibling, or null if this is the last sibling
- */
- public Node nextSibling() {
- if (parentNode == null) {
- return null; // root
- }
-
- List<Node> siblings = parentNode.childNodes;
- Integer index = siblingIndex();
- Validate.notNull(index);
- if (siblings.size() > index + 1) {
- return siblings.get(index + 1);
- } else {
- return null;
- }
- }
-
- /**
- * Get this node's previous sibling.
- *
- * @return the previous sibling, or null if this is the first sibling
- */
- public Node previousSibling() {
- if (parentNode == null) {
- return null; // root
- }
-
- List<Node> siblings = parentNode.childNodes;
- Integer index = siblingIndex();
- Validate.notNull(index);
- if (index > 0) {
- return siblings.get(index - 1);
- } else {
- return null;
- }
- }
-
- /**
- * Get the list index of this node in its node sibling list. I.e. if this is
- * the first node sibling, returns 0.
- *
- * @return position in node sibling list
- * @see org.jsoup.nodes.Element#elementSiblingIndex()
- */
- public int siblingIndex() {
- return siblingIndex;
- }
-
- protected void setSiblingIndex(int siblingIndex) {
- this.siblingIndex = siblingIndex;
- }
-
- /**
- * Perform a depth-first traversal through this node and its descendants.
- *
- * @param nodeVisitor
- * the visitor callbacks to perform on each node
- * @return this node, for chaining
- */
- public Node traverse(NodeVisitor nodeVisitor) {
- Validate.notNull(nodeVisitor);
- NodeTraversor traversor = new NodeTraversor(nodeVisitor);
- traversor.traverse(this);
- return this;
- }
-
- /**
- * Get the outer HTML of this node.
- *
- * @return HTML
- */
- public String outerHtml() {
- StringBuilder accum = new StringBuilder(128);
- outerHtml(accum);
- return accum.toString();
- }
-
- protected void outerHtml(StringBuilder accum) {
- new NodeTraversor(new OuterHtmlVisitor(accum, getOutputSettings()))
- .traverse(this);
- }
-
- // if this node has no document (or parent), retrieve the default output
- // settings
- private Document.OutputSettings getOutputSettings() {
- return ownerDocument() != null ? ownerDocument().outputSettings()
- : (new Document("")).outputSettings();
- }
-
- /**
- * Get the outer HTML of this node.
- *
- * @param accum
- * accumulator to place HTML into
- */
- abstract void outerHtmlHead(StringBuilder accum, int depth,
- Document.OutputSettings out);
-
- abstract void outerHtmlTail(StringBuilder accum, int depth,
- Document.OutputSettings out);
-
- @Override
- public String toString() {
- return outerHtml();
- }
-
- protected void indent(StringBuilder accum, int depth,
- Document.OutputSettings out) {
- accum.append("\n").append(
- StringUtil.padding(depth * out.indentAmount()));
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- // todo: have nodes hold a child index, compare against that and parent
- // (not children)
- return false;
- }
-
- @Override
- public int hashCode() {
- int result = parentNode != null ? parentNode.hashCode() : 0;
- // not children, or will block stack as they go back up to parent)
- result = 31 * result + (attributes != null ? attributes.hashCode() : 0);
- return result;
- }
-
- /**
- * Create a stand-alone, deep copy of this node, and all of its children.
- * The cloned node will have no siblings or parent node. As a stand-alone
- * object, any changes made to the clone or any of its children will not
- * impact the original node.
- * <p>
- * The cloned node may be adopted into another Document or node structure
- * using {@link Element#appendChild(Node)}.
- *
- * @return stand-alone cloned node
- */
- @Override
- public Node clone() {
- return doClone(null); // splits for orphan
- }
-
- protected Node doClone(Node parent) {
- Node clone;
- try {
- clone = (Node) super.clone();
- } catch (CloneNotSupportedException e) {
- throw new RuntimeException(e);
- }
-
- clone.parentNode = parent; // can be null, to create an orphan split
- clone.siblingIndex = parent == null ? 0 : siblingIndex;
- clone.attributes = attributes != null ? attributes.clone() : null;
- clone.baseUri = baseUri;
- clone.childNodes = new ArrayList<Node>(childNodes.size());
- for (Node child : childNodes) {
- clone.childNodes.add(child.doClone(clone)); // clone() creates
- // orphans, doClone()
- // keeps parent
- }
-
- return clone;
- }
-
- private static class OuterHtmlVisitor implements NodeVisitor {
- private StringBuilder accum;
- private Document.OutputSettings out;
-
- OuterHtmlVisitor(StringBuilder accum, Document.OutputSettings out) {
- this.accum = accum;
- this.out = out;
- }
-
- @Override
- public void head(Node node, int depth) {
- node.outerHtmlHead(accum, depth, out);
- }
-
- @Override
- public void tail(Node node, int depth) {
- if (!node.nodeName().equals("#text")) {
- node.outerHtmlTail(accum, depth, out);
- }
- }
- }
-}