Browse files

If a node has no parent, return null on previousSibling and nextSibli…

…ng instead of throwing a null pointer exception.

Fixes #184
  • Loading branch information...
1 parent 5d56ace commit 4b0dde413a3c38a77461ac64ad278a042d6eea68 @jhy committed Apr 14, 2012
View
5 CHANGES
@@ -4,6 +4,11 @@ jsoup changelog
* Fixed parsing of group-or commas in CSS selectors, to correctly handle sub-queries containing commas.
<https://github.com/jhy/jsoup/issues/179>
+ * If a node has no parent, return null on previousSibling and nextSibling instead of throwing a null pointer exception.
+ <https://github.com/jhy/jsoup/issues/184>
+
+ * Updated Node.siblingNodes() and Element.siblingElements() to exclude the current node (a node is not its own sibling).
+
*** Release 1.6.2 [2011-Mar-27]
* Added a simplified XML parsing mode, which can usefully parse valid and invalid XML, but does not enforce any HTML
document structure or special tag behaviour.
View
15 src/main/java/org/jsoup/nodes/Element.java
@@ -426,11 +426,20 @@ public Element wrap(String html) {
}
/**
- * Get sibling elements.
+ * Get sibling elements. If the element has no sibling elements, returns an empty list. An element is not a sibling
+ * of itself, so will not be included in the returned list.
* @return sibling elements
*/
public Elements siblingElements() {
- return parent().children();
+ if (parentNode == null)
+ return new Elements(0);
+
+ List<Element> elements = parent().children();
+ Elements siblings = new Elements(elements.size() - 1);
+ for (Element el: elements)
+ if (el != this)
+ siblings.add(el);
+ return siblings;
}
/**
@@ -442,6 +451,7 @@ public Elements siblingElements() {
* @see #previousElementSibling()
*/
public Element nextElementSibling() {
+ if (parentNode == null) return null;
List<Element> siblings = parent().children();
Integer index = indexInList(this, siblings);
Validate.notNull(index);
@@ -457,6 +467,7 @@ public Element nextElementSibling() {
* @see #nextElementSibling()
*/
public Element previousElementSibling() {
+ if (parentNode == null) return null;
List<Element> siblings = parent().children();
Integer index = indexInList(this, siblings);
Validate.notNull(index);
View
18 src/main/java/org/jsoup/nodes/Node.java
@@ -436,11 +436,20 @@ private void reindexChildren() {
}
/**
- Retrieves this node's sibling nodes. Effectively, {@link #childNodes() node.parent.childNodes()}.
- @return node siblings, including this node
+ 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() {
- return parent().childNodes(); // TODO: should this strip out this node? i.e. not a sibling of self?
+ 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;
}
/**
@@ -465,6 +474,9 @@ public Node nextSibling() {
@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);
View
4 src/main/java/org/jsoup/select/Elements.java
@@ -19,6 +19,10 @@ public Elements() {
contents = new ArrayList<Element>();
}
+ public Elements(int initialCapacity) {
+ contents = new ArrayList<Element>(initialCapacity);
+ }
+
public Elements(Collection<Element> elements) {
contents = new ArrayList<Element>(elements);
}
View
11 src/test/java/org/jsoup/nodes/ElementTest.java
@@ -528,4 +528,15 @@
List<DataNode> pData = p.dataNodes();
assertEquals(0, pData.size());
}
+
+ @Test public void elementIsNotASiblingOfItself() {
+ Document doc = Jsoup.parse("<div><p>One<p>Two<p>Three</div>");
+ Element p2 = doc.select("p").get(1);
+
+ assertEquals("Two", p2.text());
+ Elements els = p2.siblingElements();
+ assertEquals(2, els.size());
+ assertEquals("<p>One</p>", els.get(0).outerHtml());
+ assertEquals("<p>Three</p>", els.get(1).outerHtml());
+ }
}
View
29 src/test/java/org/jsoup/nodes/NodeTest.java
@@ -3,9 +3,12 @@
import org.jsoup.Jsoup;
import org.jsoup.TextUtil;
import org.jsoup.parser.Tag;
+import org.jsoup.select.Elements;
import org.jsoup.select.NodeVisitor;
import org.junit.Test;
+import java.util.List;
+
import static org.junit.Assert.*;
/**
Tests Nodes
@@ -178,4 +181,30 @@ public void tail(Node node, int depth) {
});
assertEquals("<div><p><#text></#text></p></div>", accum.toString());
}
+
+ @Test public void orphanNodeReturnsNullForSiblingElements() {
+ Node node = new Element(Tag.valueOf("p"), "");
+ Element el = new Element(Tag.valueOf("p"), "");
+
+ assertEquals(0, node.siblingIndex());
+ assertEquals(0, node.siblingNodes().size());
+
+ assertNull(node.previousSibling());
+ assertNull(node.nextSibling());
+
+ assertEquals(0, el.siblingElements().size());
+ assertNull(el.previousElementSibling());
+ assertNull(el.nextElementSibling());
+ }
+
+ @Test public void nodeIsNotASiblingOfItself() {
+ Document doc = Jsoup.parse("<div><p>One<p>Two<p>Three</div>");
+ Element p2 = doc.select("p").get(1);
+
+ assertEquals("Two", p2.text());
+ List<Node> nodes = p2.siblingNodes();
+ assertEquals(2, nodes.size());
+ assertEquals("<p>One</p>", nodes.get(0).outerHtml());
+ assertEquals("<p>Three</p>", nodes.get(1).outerHtml());
+ }
}

0 comments on commit 4b0dde4

Please sign in to comment.