From 40e13164a1e654126ad7bc0eefd17b43c0940c4d Mon Sep 17 00:00:00 2001
From: Willie Scholtz <williescholtz@gmail.com>
Date: Thu, 9 Jan 2014 13:10:19 +0200
Subject: [PATCH 1/3]  * added prototypejs style dom navigation methods to the
 Element class     * up/down/next and previous, which all take a CSS selector
 as parameter

---
 src/main/java/org/jsoup/nodes/Element.java   | 217 +++++++++++++------
 src/main/java/org/jsoup/select/Selector.java |  20 ++
 2 files changed, 174 insertions(+), 63 deletions(-)

diff --git a/src/main/java/org/jsoup/nodes/Element.java b/src/main/java/org/jsoup/nodes/Element.java
index 15c6a9d006..230d9cb0a3 100644
--- a/src/main/java/org/jsoup/nodes/Element.java
+++ b/src/main/java/org/jsoup/nodes/Element.java
@@ -13,18 +13,18 @@
 /**
  * A HTML element consists of a tag name, attributes, and child nodes (including text nodes and
  * other elements).
- * 
+ *
  * From an Element, you can extract data, traverse the node graph, and manipulate the HTML.
- * 
+ *
  * @author Jonathan Hedley, jonathan@hedley.net
  */
 public class Element extends Node {
     private Tag tag;
     private Set<String> classNames;
-    
+
     /**
      * Create a new, standalone Element. (Standalone in that is has no parent.)
-     * 
+     *
      * @param tag tag of this element
      * @param baseUri the base URI
      * @param attributes initial attributes
@@ -33,14 +33,14 @@ public class Element extends Node {
      */
     public Element(Tag tag, String baseUri, Attributes attributes) {
         super(baseUri, attributes);
-        
-        Validate.notNull(tag);    
+
+        Validate.notNull(tag);
         this.tag = tag;
     }
-    
+
     /**
      * Create a new Element from a tag and a base URI.
-     * 
+     *
      * @param tag element tag
      * @param baseUri the base URI of this element. It is acceptable for the base URI to be an empty
      *            string, but not null.
@@ -57,7 +57,7 @@ public String nodeName() {
 
     /**
      * Get the name of the tag for this element. E.g. {@code div}
-     * 
+     *
      * @return the tag name
      */
     public String tagName() {
@@ -79,17 +79,17 @@ public Element tagName(String tagName) {
 
     /**
      * Get the Tag for this element.
-     * 
+     *
      * @return the tag object
      */
     public Tag tag() {
         return tag;
     }
-    
+
     /**
      * Test if this element is a block-level element. (E.g. {@code <div> == true} or an inline element
      * {@code <p> == false}).
-     * 
+     *
      * @return true if block, false if not (and thus inline)
      */
     public boolean isBlock() {
@@ -98,7 +98,7 @@ public boolean isBlock() {
 
     /**
      * Get the {@code id} attribute of this element.
-     * 
+     *
      * @return The id attribute, if present, or an empty string if not.
      */
     public String id() {
@@ -109,7 +109,7 @@ public String id() {
     /**
      * Set an attribute value on this element. If this element already has an attribute with the
      * key, its value is updated; otherwise, a new attribute is added.
-     * 
+     *
      * @return this element
      */
     public Element attr(String attributeKey, String attributeValue) {
@@ -162,7 +162,7 @@ private static void accumulateParents(Element el, Elements parents) {
      * <p/>
      * Note that an element can have both mixed Nodes and Elements as children. This method inspects
      * a filtered list of children that are elements, and the index is based on that filtered list.
-     * 
+     *
      * @param index the index number of the element to retrieve
      * @return the child element, if it exists, otherwise throws an {@code IndexOutOfBoundsException}
      * @see #childNode(int)
@@ -189,6 +189,97 @@ public Elements children() {
         return new Elements(elements);
     }
 
+    /**
+     * traverse up the document tree from the current element
+     * until a match to the selector is found.
+     * <p/>
+     * @param cssQuery the CSS Selector
+     * @return the first element found or null if none are found.
+     */
+    public Element up(String cssQuery) {
+        Validate.notEmpty(cssQuery);
+
+        Element element = this.parent();
+        while (element != null) {
+            if (Selector.matches(cssQuery, element)) {
+                break;
+            }
+
+            element = element.parent();
+        }
+
+        return element;
+    }
+
+    /**
+     * traverse down the document tree from the current element
+     * until a match to the selector is found.
+     * <p/>
+     * @param cssQuery the CSS Selector
+     * @return the first element found or null if none are found.
+     */
+    public Element down(final String cssQuery) {
+        Validate.notEmpty(cssQuery);
+
+        final Elements elements = new Elements();
+        new NodeTraversor(new NodeVisitor() {
+            public void head(Node node, int depth) {
+                if (node instanceof Element) {
+                    Element el = (Element) node;
+                    if (Selector.matches(cssQuery, el)) {
+                        elements.add(el);
+                    }
+                }
+            }
+
+            public void tail(Node node, int depth) {
+                // no-op
+            }
+        }).traverse(this);
+
+        return elements.isEmpty() ? null : elements.first();
+    }
+
+    /**
+     * returns the specified element's first following
+     * sibling matching the supplied selector.
+     * <p/>
+     * @param cssQuery the CSS Selector
+     * @return the first element found or null if none are found.
+     */
+    public Element next(final String cssQuery) {
+        Element element = this.nextElementSibling();
+        while (element != null) {
+            if (Selector.matches(cssQuery, element)) {
+                break;
+            }
+
+            element = element.nextElementSibling();
+        }
+
+        return element;
+    }
+
+    /**
+     * returns the specified element's first previous
+     * sibling matching the supplied selector.
+     * <p/>
+     * @param cssQuery the CSS Selector
+     * @return the first element found or null if none are found.
+     */
+    public Element previous(final String cssQuery) {
+        Element element = this.previousElementSibling();
+        while (element != null) {
+            if (Selector.matches(cssQuery, element)) {
+                break;
+            }
+
+            element = element.previousElementSibling();
+        }
+
+        return element;
+    }
+
     /**
      * Get this element's child text nodes. The list is unmodifiable but the text nodes may be manipulated.
      * <p/>
@@ -251,29 +342,29 @@ public List<DataNode> dataNodes() {
     public Elements select(String cssQuery) {
         return Selector.select(cssQuery, this);
     }
-    
+
     /**
      * Add a node child node to this element.
-     * 
+     *
      * @param child node to add.
      * @return this element, so that you can add more child nodes or elements.
      */
     public Element appendChild(Node child) {
         Validate.notNull(child);
-        
+
         addChildren(child);
         return this;
     }
 
     /**
      * Add a node to the start of this element's children.
-     * 
+     *
      * @param child node to add.
      * @return this element, so that you can add more child nodes or elements.
      */
     public Element prependChild(Node child) {
         Validate.notNull(child);
-        
+
         addChildren(0, child);
         return this;
     }
@@ -299,10 +390,10 @@ public Element insertChildren(int index, Collection<? extends Node> children) {
         addChildren(index, nodeArray);
         return this;
     }
-    
+
     /**
      * Create a new element by tag name, and add it as the last child.
-     * 
+     *
      * @param tagName the name of the tag (e.g. {@code div}).
      * @return the new element, to allow you to add content to it, e.g.:
      *  {@code parent.appendElement("h1").attr("id", "header").text("Welcome");}
@@ -312,10 +403,10 @@ public Element appendElement(String tagName) {
         appendChild(child);
         return child;
     }
-    
+
     /**
      * Create a new element by tag name, and add it as the first child.
-     * 
+     *
      * @param tagName the name of the tag (e.g. {@code div}).
      * @return the new element, to allow you to add content to it, e.g.:
      *  {@code parent.prependElement("h1").attr("id", "header").text("Welcome");}
@@ -325,10 +416,10 @@ public Element prependElement(String tagName) {
         prependChild(child);
         return child;
     }
-    
+
     /**
      * Create and append a new TextNode to this element.
-     * 
+     *
      * @param text the unencoded text to add
      * @return this element
      */
@@ -337,10 +428,10 @@ public Element appendText(String text) {
         appendChild(node);
         return this;
     }
-    
+
     /**
      * Create and prepend a new TextNode to this element.
-     * 
+     *
      * @param text the unencoded text to add
      * @return this element
      */
@@ -349,7 +440,7 @@ public Element prependText(String text) {
         prependChild(node);
         return this;
     }
-    
+
     /**
      * Add inner HTML to this element. The supplied HTML will be parsed, and each node appended to the end of the children.
      * @param html HTML to add inside this element, after the existing HTML
@@ -363,7 +454,7 @@ public Element append(String html) {
         addChildren(nodes.toArray(new Node[nodes.size()]));
         return this;
     }
-    
+
     /**
      * Add inner HTML into this element. The supplied HTML will be parsed, and each node prepended to the start of the element's children.
      * @param html HTML to add inside this element, before the existing HTML
@@ -372,7 +463,7 @@ public Element append(String html) {
      */
     public Element prepend(String html) {
         Validate.notNull(html);
-        
+
         List<Node> nodes = Parser.parseFragment(html, this, baseUri());
         addChildren(0, nodes.toArray(new Node[nodes.size()]));
         return this;
@@ -462,7 +553,7 @@ public Elements siblingElements() {
     }
 
     /**
-     * Gets the next sibling element of this element. E.g., if a {@code div} contains two {@code p}s, 
+     * Gets the next sibling element of this element. E.g., if a {@code div} contains two {@code p}s,
      * the {@code nextElementSibling} of the first {@code p} is the second {@code p}.
      * <p/>
      * This is similar to {@link #nextSibling()}, but specifically finds only Elements
@@ -498,14 +589,14 @@ public Element previousElementSibling() {
 
     /**
      * Gets the first element sibling of this element.
-     * @return the first sibling that is an element (aka the parent's first element child) 
+     * @return the first sibling that is an element (aka the parent's first element child)
      */
     public Element firstElementSibling() {
         // todo: should firstSibling() exclude this?
         List<Element> siblings = parent().children();
         return siblings.size() > 1 ? siblings.get(0) : null;
     }
-    
+
     /**
      * Get the list index of this element in its element sibling list. I.e. if this is the first element
      * sibling, returns 0.
@@ -513,18 +604,18 @@ public Element firstElementSibling() {
      */
     public Integer elementSiblingIndex() {
        if (parent() == null) return 0;
-       return indexInList(this, parent().children()); 
+       return indexInList(this, parent().children());
     }
 
     /**
      * Gets the last element sibling of this element
-     * @return the last sibling that is an element (aka the parent's last element child) 
+     * @return the last sibling that is an element (aka the parent's last element child)
      */
     public Element lastElementSibling() {
         List<Element> siblings = parent().children();
         return siblings.size() > 1 ? siblings.get(siblings.size() - 1) : null;
     }
-    
+
     private static <E extends Element> Integer indexInList(Element search, List<E> elements) {
         Validate.notNull(search);
         Validate.notNull(elements);
@@ -562,7 +653,7 @@ public Elements getElementsByTag(String tagName) {
      */
     public Element getElementById(String id) {
         Validate.notEmpty(id);
-        
+
         Elements elements = Collector.collect(new Evaluator.Id(id), this);
         if (elements.size() > 0)
             return elements.get(0);
@@ -575,7 +666,7 @@ public Element getElementById(String id) {
      * <p>
      * Elements can have multiple classes (e.g. {@code <div class="header round first">}. This method
      * checks each class, so you can find the above with {@code el.getElementsByClass("header");}.
-     * 
+     *
      * @param className the name of the class to search for.
      * @return elements with the supplied class name, empty if none
      * @see #hasClass(String)
@@ -615,7 +706,7 @@ public Elements getElementsByAttributeStarting(String keyPrefix) {
 
     /**
      * Find elements that have an attribute with the specific value. Case insensitive.
-     * 
+     *
      * @param key name of the attribute
      * @param value value of the attribute
      * @return elements that have this attribute with this value, empty if none
@@ -626,7 +717,7 @@ public Elements getElementsByAttributeValue(String key, String value) {
 
     /**
      * Find elements that either do not have this attribute, or have it with a different value. Case insensitive.
-     * 
+     *
      * @param key name of the attribute
      * @param value value of the attribute
      * @return elements that do not have a matching attribute
@@ -637,7 +728,7 @@ public Elements getElementsByAttributeValueNot(String key, String value) {
 
     /**
      * Find elements that have attributes that start with the value prefix. Case insensitive.
-     * 
+     *
      * @param key name of the attribute
      * @param valuePrefix start of attribute value
      * @return elements that have attributes that start with the value prefix
@@ -648,7 +739,7 @@ public Elements getElementsByAttributeValueStarting(String key, String valuePref
 
     /**
      * Find elements that have attributes that end with the value suffix. Case insensitive.
-     * 
+     *
      * @param key name of the attribute
      * @param valueSuffix end of the attribute value
      * @return elements that have attributes that end with the value suffix
@@ -659,7 +750,7 @@ public Elements getElementsByAttributeValueEnding(String key, String valueSuffix
 
     /**
      * Find elements that have attributes whose value contains the match string. Case insensitive.
-     * 
+     *
      * @param key name of the attribute
      * @param match substring of value to search for
      * @return elements that have attributes containing this text
@@ -667,7 +758,7 @@ public Elements getElementsByAttributeValueEnding(String key, String valueSuffix
     public Elements getElementsByAttributeValueContaining(String key, String match) {
         return Collector.collect(new Evaluator.AttributeWithValueContaining(key, match), this);
     }
-    
+
     /**
      * Find elements that have attributes whose values match the supplied regular expression.
      * @param key name of the attribute
@@ -676,9 +767,9 @@ public Elements getElementsByAttributeValueContaining(String key, String match)
      */
     public Elements getElementsByAttributeValueMatching(String key, Pattern pattern) {
         return Collector.collect(new Evaluator.AttributeWithValueMatching(key, pattern), this);
-        
+
     }
-    
+
     /**
      * Find elements that have attributes whose values match the supplied regular expression.
      * @param key name of the attribute
@@ -694,7 +785,7 @@ public Elements getElementsByAttributeValueMatching(String key, String regex) {
         }
         return getElementsByAttributeValueMatching(key, pattern);
     }
-    
+
     /**
      * Find elements whose sibling index is less than the supplied index.
      * @param index 0-based index
@@ -703,7 +794,7 @@ public Elements getElementsByAttributeValueMatching(String key, String regex) {
     public Elements getElementsByIndexLessThan(int index) {
         return Collector.collect(new Evaluator.IndexLessThan(index), this);
     }
-    
+
     /**
      * Find elements whose sibling index is greater than the supplied index.
      * @param index 0-based index
@@ -712,7 +803,7 @@ public Elements getElementsByIndexLessThan(int index) {
     public Elements getElementsByIndexGreaterThan(int index) {
         return Collector.collect(new Evaluator.IndexGreaterThan(index), this);
     }
-    
+
     /**
      * Find elements whose sibling index is equal to the supplied index.
      * @param index 0-based index
@@ -721,7 +812,7 @@ public Elements getElementsByIndexGreaterThan(int index) {
     public Elements getElementsByIndexEquals(int index) {
         return Collector.collect(new Evaluator.IndexEquals(index), this);
     }
-    
+
     /**
      * Find elements that contain the specified string. The search is case insensitive. The text may appear directly
      * in the element, or in any of its descendants.
@@ -732,7 +823,7 @@ public Elements getElementsByIndexEquals(int index) {
     public Elements getElementsContainingText(String searchText) {
         return Collector.collect(new Evaluator.ContainsText(searchText), this);
     }
-    
+
     /**
      * Find elements that directly contain the specified string. The search is case insensitive. The text must appear directly
      * in the element, not in any of its descendants.
@@ -743,7 +834,7 @@ public Elements getElementsContainingText(String searchText) {
     public Elements getElementsContainingOwnText(String searchText) {
         return Collector.collect(new Evaluator.ContainsOwnText(searchText), this);
     }
-    
+
     /**
      * Find elements whose text matches the supplied regular expression.
      * @param pattern regular expression to match text against
@@ -753,7 +844,7 @@ public Elements getElementsContainingOwnText(String searchText) {
     public Elements getElementsMatchingText(Pattern pattern) {
         return Collector.collect(new Evaluator.Matches(pattern), this);
     }
-    
+
     /**
      * Find elements whose text matches the supplied regular expression.
      * @param regex regular expression to match text against. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as (?i) and (?m) to control regex options.
@@ -769,7 +860,7 @@ public Elements getElementsMatchingText(String regex) {
         }
         return getElementsMatchingText(pattern);
     }
-    
+
     /**
      * Find elements whose own text matches the supplied regular expression.
      * @param pattern regular expression to match text against
@@ -779,7 +870,7 @@ public Elements getElementsMatchingText(String regex) {
     public Elements getElementsMatchingOwnText(Pattern pattern) {
         return Collector.collect(new Evaluator.MatchesOwn(pattern), this);
     }
-    
+
     /**
      * Find elements whose text matches the supplied regular expression.
      * @param regex regular expression to match text against. You can use <a href="http://java.sun.com/docs/books/tutorial/essential/regex/pattern.html#embedded">embedded flags</a> (such as (?i) and (?m) to control regex options.
@@ -795,10 +886,10 @@ public Elements getElementsMatchingOwnText(String regex) {
         }
         return getElementsMatchingOwnText(pattern);
     }
-    
+
     /**
      * Find all elements under this element (including self, and children of children).
-     * 
+     *
      * @return all elements
      */
     public Elements getAllElements() {
@@ -942,7 +1033,7 @@ public String data() {
             }
         }
         return sb.toString();
-    }   
+    }
 
     /**
      * Gets the literal value of this element's "class" attribute, which may include multiple class names, space
@@ -1039,7 +1130,7 @@ public Element toggleClass(String className) {
 
         return this;
     }
-    
+
     /**
      * Get the value of a form element (input, textarea, etc).
      * @return the value of the form element, or empty string if not set.
@@ -1050,7 +1141,7 @@ public String val() {
         else
             return attr("value");
     }
-    
+
     /**
      * Set the value of a form element (input, textarea, etc).
      * @param value value to set
@@ -1096,7 +1187,7 @@ void outerHtmlTail(StringBuilder accum, int depth, Document.OutputSettings out)
     /**
      * Retrieves the element's inner HTML. E.g. on a {@code <div>} with one empty {@code <p>}, would return
      * {@code <p></p>}. (Whereas {@link #outerHtml()} would return {@code <div><p></p></div>}.)
-     * 
+     *
      * @return String of HTML.
      * @see #outerHtml()
      */
@@ -1110,7 +1201,7 @@ private void html(StringBuilder accum) {
         for (Node node : childNodes)
             node.outerHtml(accum);
     }
-    
+
     /**
      * Set this element's inner HTML. Clears the existing HTML first.
      * @param html HTML to parse and set into this element
diff --git a/src/main/java/org/jsoup/select/Selector.java b/src/main/java/org/jsoup/select/Selector.java
index 53fdd16abf..3d6c3b1db2 100644
--- a/src/main/java/org/jsoup/select/Selector.java
+++ b/src/main/java/org/jsoup/select/Selector.java
@@ -111,10 +111,30 @@ public static Elements select(String query, Iterable<Element> roots) {
         return new Elements(elements);
     }
 
+    /**
+     * Tests if the supplied element matches the selector
+     * <p>
+     * :root not supported
+     * @param query CSS selector
+     * @param element the element to test
+     * @return true if the specified element matches the selector
+     */
+    public static boolean matches(String query, Element element) {
+        Validate.notEmpty(query);
+        Validate.notNull(element);
+
+        return new Selector(query, element).match();
+    }
+
     private Elements select() {
         return Collector.collect(evaluator, root);
     }
 
+    private boolean match() {
+        // root acts as the element here
+        return this.evaluator.matches(null, this.root);
+    }
+
     // exclude set. package open so that Elements can implement .not() selector.
     static Elements filterOut(Collection<Element> elements, Collection<Element> outs) {
         Elements output = new Elements();

From 1ad72d3e0accf4f5f05e9a4ef3ee8e9597ac2185 Mon Sep 17 00:00:00 2001
From: Willie Scholtz <williescholtz@gmail.com>
Date: Thu, 9 Jan 2014 13:27:32 +0200
Subject: [PATCH 2/3] * tests for dom navigation methods
 (up/down/previous/next)

---
 .../java/org/jsoup/nodes/ElementTest.java     | 96 +++++++++++++++----
 1 file changed, 78 insertions(+), 18 deletions(-)

diff --git a/src/test/java/org/jsoup/nodes/ElementTest.java b/src/test/java/org/jsoup/nodes/ElementTest.java
index cb9b1e067c..6ff89e6ba1 100644
--- a/src/test/java/org/jsoup/nodes/ElementTest.java
+++ b/src/test/java/org/jsoup/nodes/ElementTest.java
@@ -42,7 +42,67 @@ public class ElementTest {
         List<Element> empty = doc.getElementsByTag("wtf");
         assertEquals(0, empty.size());
     }
-    
+
+    @Test public void up() {
+        Document doc = Jsoup.parse(reference);
+        Element div2 = doc.getElementById("div2");
+        assertNotNull(div2);
+
+        Element div1 = div2.up("div#div1");
+        assertNotNull(div1);
+        assertEquals("div1", div1.id());
+
+        Element invalid = div2.up("img.invalid");
+        assertNull(invalid);
+    }
+
+    @Test public void down() {
+        Document doc = Jsoup.parse(reference);
+        Element div1 = doc.getElementById("div1");
+        assertNotNull(div1);
+
+        Element el = div1.down("div#div2 > img[src^=foo]");
+        assertNotNull(el);
+        assertEquals("img", el.tagName().toLowerCase());
+        assertEquals("foo.png", el.attr("src"));
+    }
+
+    @Test public void next() {
+        Document doc = Jsoup.parse(reference);
+        Elements ps = doc.getElementsByTag("p");
+        assertEquals(2, ps.size());
+
+        Element el = ps.first().next("div#div2");
+        assertNotNull(el);
+        assertEquals("div2", el.id());
+    }
+
+    @Test public void previous() {
+        Document doc = Jsoup.parse(reference);
+        Element div2 = doc.getElementById("div2");
+        assertNotNull(div2);
+
+        Element el = div2.previous("p + p");
+        assertNotNull(el);
+        assertEquals("Another element", el.text());
+    }
+
+    @Test public void chained() {
+        Document doc = Jsoup.parse(reference);
+        Element div1 = doc.getElementById("div1");
+        assertNotNull(div1);
+
+        Element el = div1
+                .down("img")
+                .up("div")
+                .previous("p")
+                .previous("p")
+                .next("p")
+                .down("b");
+        assertNotNull(el);
+        assertEquals("element", el.text());
+    }
+
     @Test public void getNamespacedElementsByTag() {
         Document doc = Jsoup.parse("<div><abc:def id=1>Hello</abc:def></div>");
         Elements els = doc.getElementsByTag("abc:def");
@@ -63,7 +123,7 @@ public class ElementTest {
         Element span = div2.child(0).getElementById("2"); // called from <p> context should be span
         assertEquals("span", span.tagName());
     }
-    
+
     @Test public void testGetText() {
         Document doc = Jsoup.parse(reference);
         assertEquals("Hello Another element", doc.text());
@@ -127,7 +187,7 @@ public class ElementTest {
         assertEquals("body", parents.get(2).tagName());
         assertEquals("html", parents.get(3).tagName());
     }
-    
+
     @Test public void testElementSiblingIndex() {
         Document doc = Jsoup.parse("<div><p>One</p>...<p>Two</p>...<p>Three</p>");
         Elements ps = doc.select("p");
@@ -180,7 +240,7 @@ public class ElementTest {
         List<Element> none = doc.getElementsByAttributeValue("style", "none");
         assertEquals(0, none.size());
     }
-    
+
     @Test public void testClassDomMethods() {
         Document doc = Jsoup.parse("<div><span class='mellow yellow'>Hello <b>Yellow</b></span></div>");
         List<Element> els = doc.getElementsByAttribute("class");
@@ -225,7 +285,7 @@ public class ElementTest {
         Document doc = Jsoup.parse("<title>Format test</title><div><p>Hello <span>jsoup <span>users</span></span></p><p>Good.</p></div>");
         assertEquals("<html>\n <head>\n  <title>Format test</title>\n </head>\n <body>\n  <div>\n   <p>Hello <span>jsoup <span>users</span></span></p>\n   <p>Good.</p>\n  </div>\n </body>\n</html>", doc.html());
     }
-    
+
     @Test public void testFormatOutline() {
         Document doc = Jsoup.parse("<title>Format test</title><div><p>Hello <span>jsoup <span>users</span></span></p><p>Good.</p></div>");
         doc.outputSettings().outline(true);
@@ -246,7 +306,7 @@ public class ElementTest {
         Element div = doc.select("div").first();
         assertEquals("   \n<p>Hello\n there\n</p>", div.html());
     }
-    
+
     @Test public void testEmptyElementFormatHtml() {
         // don't put newlines into empty blocks
         Document doc = Jsoup.parse("<section><div></div></section>");
@@ -276,7 +336,7 @@ public class ElementTest {
         assertEquals("Gone", div.text());
         assertEquals(0, doc.select("p").size());
     }
-    
+
     @Test public void testAddNewElement() {
         Document doc = Jsoup.parse("<div id=1><p>Hello</p></div>");
         Element div = doc.getElementById("1");
@@ -313,7 +373,7 @@ public class ElementTest {
             assertEquals(i, ps.get(i).siblingIndex);
         }
     }
-    
+
     @Test public void testPrependElement() {
         Document doc = Jsoup.parse("<div id=1><p>Hello</p></div>");
         Element div = doc.getElementById("1");
@@ -321,14 +381,14 @@ public class ElementTest {
         assertEquals("Before", div.child(0).text());
         assertEquals("Hello", div.child(1).text());
     }
-    
+
     @Test public void testAddNewText() {
         Document doc = Jsoup.parse("<div id=1><p>Hello</p></div>");
         Element div = doc.getElementById("1");
         div.appendText(" there & now >");
         assertEquals("<p>Hello</p> there &amp; now &gt;", TextUtil.stripNewlines(div.html()));
     }
-    
+
     @Test public void testPrependText() {
         Document doc = Jsoup.parse("<div id=1><p>Hello</p></div>");
         Element div = doc.getElementById("1");
@@ -336,7 +396,7 @@ public class ElementTest {
         assertEquals("there & now > Hello", div.text());
         assertEquals("there &amp; now &gt; <p>Hello</p>", TextUtil.stripNewlines(div.html()));
     }
-    
+
     @Test public void testAddNewHtml() {
         Document doc = Jsoup.parse("<div id=1><p>Hello</p></div>");
         Element div = doc.getElementById("1");
@@ -349,7 +409,7 @@ public class ElementTest {
             assertEquals(i, ps.get(i).siblingIndex);
         }
     }
-    
+
     @Test public void testPrependNewHtml() {
         Document doc = Jsoup.parse("<div id=1><p>Hello</p></div>");
         Element div = doc.getElementById("1");
@@ -362,7 +422,7 @@ public class ElementTest {
             assertEquals(i, ps.get(i).siblingIndex);
         }
     }
-    
+
     @Test public void testSetHtml() {
         Document doc = Jsoup.parse("<div id=1><p>Hello</p></div>");
         Element div = doc.getElementById("1");
@@ -377,28 +437,28 @@ public class ElementTest {
         assertEquals("<div><div class=\"head\"><p>Hello</p></div><p>There</p></div>", TextUtil.stripNewlines(doc.body().html()));
 
         Element ret = p.wrap("<div><div class=foo></div><p>What?</p></div>");
-        assertEquals("<div><div class=\"head\"><div><div class=\"foo\"><p>Hello</p></div><p>What?</p></div></div><p>There</p></div>", 
+        assertEquals("<div><div class=\"head\"><div><div class=\"foo\"><p>Hello</p></div><p>What?</p></div></div><p>There</p></div>",
                 TextUtil.stripNewlines(doc.body().html()));
 
         assertEquals(ret, p);
     }
-    
+
     @Test public void before() {
         Document doc = Jsoup.parse("<div><p>Hello</p><p>There</p></div>");
         Element p1 = doc.select("p").first();
         p1.before("<div>one</div><div>two</div>");
         assertEquals("<div><div>one</div><div>two</div><p>Hello</p><p>There</p></div>", TextUtil.stripNewlines(doc.body().html()));
-        
+
         doc.select("p").last().before("<p>Three</p><!-- four -->");
         assertEquals("<div><div>one</div><div>two</div><p>Hello</p><p>Three</p><!-- four --><p>There</p></div>", TextUtil.stripNewlines(doc.body().html()));
     }
-    
+
     @Test public void after() {
         Document doc = Jsoup.parse("<div><p>Hello</p><p>There</p></div>");
         Element p1 = doc.select("p").first();
         p1.after("<div>one</div><div>two</div>");
         assertEquals("<div><p>Hello</p><div>one</div><div>two</div><p>There</p></div>", TextUtil.stripNewlines(doc.body().html()));
-        
+
         doc.select("p").last().after("<p>Three</p><!-- four -->");
         assertEquals("<div><p>Hello</p><div>one</div><div>two</div><p>There</p><p>Three</p><!-- four --></div>", TextUtil.stripNewlines(doc.body().html()));
     }

From 1906d313c2b2c9cebe2ee14b42b7e768a39ce0f4 Mon Sep 17 00:00:00 2001
From: Willie Scholtz <williescholtz@gmail.com>
Date: Mon, 24 Aug 2015 13:12:22 +0200
Subject: [PATCH 3/3] JDK8 Build Compatibility

added `-Xdoclint:none` to ensure JDK8 can build jsoup
converted star imports to explicit imports
---
 pom.xml                                    |  7 ++---
 src/main/java/org/jsoup/nodes/Element.java | 31 +++++++++++++++-------
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/pom.xml b/pom.xml
index 6e0677ce0a..8b56523219 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,7 +18,7 @@
   		<name>The MIT License</name>
   		<url>http://jsoup.org/license</url>
   		<distribution>repo</distribution>
-  	</license>  
+  	</license>
   </licenses>
   <scm>
   	<url>http://github.com/jhy/jsoup</url>
@@ -70,6 +70,7 @@
         <artifactId>maven-javadoc-plugin</artifactId>
         <version>2.6.1</version>
         <configuration>
+            <additionalparam>-Xdoclint:none</additionalparam>
         </configuration>
         <executions>
           <execution>
@@ -187,7 +188,7 @@
       </build>
     </profile>
   </profiles>
- 
+
   <dependencies>
 
     <dependency>
@@ -199,7 +200,7 @@
     </dependency>
 
   </dependencies>
-    
+
   <dependencyManagement>
   	<dependencies>
   	</dependencies>
diff --git a/src/main/java/org/jsoup/nodes/Element.java b/src/main/java/org/jsoup/nodes/Element.java
index 26f7b5bd6d..34c4faf2fe 100644
--- a/src/main/java/org/jsoup/nodes/Element.java
+++ b/src/main/java/org/jsoup/nodes/Element.java
@@ -1,14 +1,25 @@
 package org.jsoup.nodes;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 import org.jsoup.helper.StringUtil;
 import org.jsoup.helper.Validate;
 import org.jsoup.parser.Parser;
 import org.jsoup.parser.Tag;
-import org.jsoup.select.*;
-
-import java.util.*;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
+import org.jsoup.select.Collector;
+import org.jsoup.select.Elements;
+import org.jsoup.select.Evaluator;
+import org.jsoup.select.NodeTraversor;
+import org.jsoup.select.NodeVisitor;
+import org.jsoup.select.Selector;
 
 /**
  * A HTML element consists of a tag name, attributes, and child nodes (including text nodes and
@@ -116,15 +127,15 @@ public Element attr(String attributeKey, String attributeValue) {
         super.attr(attributeKey, attributeValue);
         return this;
     }
-    
+
     /**
      * Set a boolean attribute value on this element. Setting to <code>true</code> sets the attribute value to "" and
      * marks the attribute as boolean so no value is written out. Setting to <code>false</code> removes the attribute
      * with the same key if it exists.
-     * 
+     *
      * @param attributeKey the attribute key
      * @param attributeValue the attribute value
-     * 
+     *
      * @return this element
      */
     public Element attr(String attributeKey, boolean attributeValue) {
@@ -178,7 +189,7 @@ private static void accumulateParents(Element el, Elements parents) {
      * Note that an element can have both mixed Nodes and Elements as children. This method inspects
      * a filtered list of children that are elements, and the index is based on that filtered list.
      * </p>
-     * 
+     *
      * @param index the index number of the element to retrieve
      * @return the child element, if it exists, otherwise throws an {@code IndexOutOfBoundsException}
      * @see #childNode(int)
@@ -354,7 +365,7 @@ public List<DataNode> dataNodes() {
      * <p>
      * See the query syntax documentation in {@link org.jsoup.select.Selector}.
      * </p>
-     * 
+     *
      * @param cssQuery a {@link Selector} CSS-like query
      * @return elements that match the query (empty if none match)
      * @see org.jsoup.select.Selector
