diff --git a/CHANGES.md b/CHANGES.md index 854c7c120a..2ff4d495b8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,9 @@ * Updated the minimum Android API Level validation from 10 to **21**. As with previous jsoup versions, Android developers need to enable core library desugaring. The minimum Java version remains Java 8. [2173](https://github.com/jhy/jsoup/pull/2173) +* Removed previously deprecated class: `org.jsoup.UncheckedIOException` (replace with `java.io.UncheckedIOException`); + method `Element Element#forEach(Consumer)` to + `void Element#forEach(Consumer())`. [2246](https://github.com/jhy/jsoup/pull/2246) ### Improvements @@ -16,6 +19,7 @@ * Added `Element#selectStream(String query)` and `Element#selectStream(Evaluator )` methods, that return a `Stream` of matching elements. Elements are evaluated and returned as they are found, and the stream can be terminated early. [2092](https://github.com/jhy/jsoup/pull/2092) +* `Element` objects now implement `Iterable`, enabling them to be used in enhanced for loops. ### Bug Fixes diff --git a/pom.xml b/pom.xml index 859aab0d31..ba818d3873 100644 --- a/pom.xml +++ b/pom.xml @@ -260,7 +260,6 @@ true @java.lang.Deprecated - org.jsoup.UncheckedIOException diff --git a/src/main/java/org/jsoup/UncheckedIOException.java b/src/main/java/org/jsoup/UncheckedIOException.java deleted file mode 100644 index a3b4fa31b6..0000000000 --- a/src/main/java/org/jsoup/UncheckedIOException.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.jsoup; - -import java.io.IOException; - -/** - * @deprecated Use {@link java.io.UncheckedIOException} instead. This class acted as a compatibility shim for Java - * versions prior to 1.8. - */ -@Deprecated -public class UncheckedIOException extends java.io.UncheckedIOException { - public UncheckedIOException(IOException cause) { - super(cause); - } - - public UncheckedIOException(String message) { - super(new IOException(message)); - } - - public IOException ioException() { - return getCause(); - } -} diff --git a/src/main/java/org/jsoup/helper/HttpConnection.java b/src/main/java/org/jsoup/helper/HttpConnection.java index a33bcd9110..501cce58bd 100644 --- a/src/main/java/org/jsoup/helper/HttpConnection.java +++ b/src/main/java/org/jsoup/helper/HttpConnection.java @@ -3,7 +3,6 @@ import org.jsoup.Connection; import org.jsoup.HttpStatusException; import org.jsoup.Progress; -import org.jsoup.UncheckedIOException; import org.jsoup.UnsupportedMimeTypeException; import org.jsoup.internal.ControllableInputStream; import org.jsoup.internal.Functions; @@ -11,7 +10,6 @@ import org.jsoup.nodes.Document; import org.jsoup.parser.Parser; import org.jsoup.parser.StreamParser; -import org.jsoup.parser.TokenQueue; import org.jspecify.annotations.Nullable; import javax.net.ssl.HttpsURLConnection; @@ -25,6 +23,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; import java.net.CookieManager; import java.net.CookieStore; import java.net.HttpURLConnection; diff --git a/src/main/java/org/jsoup/nodes/Element.java b/src/main/java/org/jsoup/nodes/Element.java index 1a1538edd6..1536cda431 100644 --- a/src/main/java/org/jsoup/nodes/Element.java +++ b/src/main/java/org/jsoup/nodes/Element.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -44,7 +45,7 @@ An HTML Element consists of a tag name, attributes, and child nodes (including t

From an Element, you can extract data, traverse the node graph, and manipulate the HTML. */ -public class Element extends Node { +public class Element extends Node implements Iterable { private static final List EmptyChildren = Collections.emptyList(); private static final Pattern ClassSplit = Pattern.compile("\\s+"); private static final String BaseUriKey = Attributes.internalKey("baseUri"); @@ -1911,15 +1912,20 @@ public Element forEachNode(Consumer action) { Perform the supplied action on this Element and each of its descendant Elements, during a depth-first traversal. Elements may be inspected, changed, added, replaced, or removed. @param action the function to perform on the element - @return this Element, for chaining @see Node#forEachNode(Consumer) - @deprecated use {@link #stream()}.{@link Stream#forEach(Consumer) forEach(Consumer)} instead. (Removing this method - so Element can implement Iterable, which this signature conflicts with due to the non-void return.) */ - @Deprecated - public Element forEach(Consumer action) { + @Override + public void forEach(Consumer action) { stream().forEach(action); - return this; + } + + /** + Returns an Iterator that iterates this Element and each of its descendant Elements, in document order. + @return an Iterator + */ + @Override + public Iterator iterator() { + return new NodeIterator<>(this, Element.class); } @Override diff --git a/src/main/java/org/jsoup/parser/CharacterReader.java b/src/main/java/org/jsoup/parser/CharacterReader.java index 9c16d44da1..80359393c6 100644 --- a/src/main/java/org/jsoup/parser/CharacterReader.java +++ b/src/main/java/org/jsoup/parser/CharacterReader.java @@ -1,11 +1,11 @@ package org.jsoup.parser; -import org.jsoup.UncheckedIOException; import org.jsoup.helper.Validate; import org.jsoup.internal.SoftPool; import org.jspecify.annotations.Nullable; import java.io.IOException; +import java.io.UncheckedIOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; diff --git a/src/test/java/org/jsoup/nodes/ElementTest.java b/src/test/java/org/jsoup/nodes/ElementTest.java index 2c2fd50d9b..b53444aeaa 100644 --- a/src/test/java/org/jsoup/nodes/ElementTest.java +++ b/src/test/java/org/jsoup/nodes/ElementTest.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -26,6 +27,8 @@ import java.util.regex.Pattern; import java.util.stream.Stream; +import static org.jsoup.nodes.NodeIteratorTest.assertIterates; +import static org.jsoup.nodes.NodeIteratorTest.trackSeen; import static org.jsoup.select.SelectorTest.assertSelectedOwnText; import static org.junit.jupiter.api.Assertions.*; @@ -2992,4 +2995,22 @@ void prettySerializationRoundTrips(Document.OutputSettings settings) { assertEquals("Hello world", div.text()); } + + @Test void elementIsIterable() { + Document doc = Jsoup.parse("

One Two ThreeFourFive
"); + String expect = "div;a#1;a#2;b;b;a#3;"; // elements only, in doc order + Element div = doc.expectFirst("div"); + + // for each pattern + StringBuilder seen = new StringBuilder(); + for (Element el: div) { + trackSeen(el, seen); + } + assertEquals(expect, seen.toString()); + + // iterator + seen = new StringBuilder(); + Iterator iterator = div.iterator(); + assertIterates(iterator, expect); + } } diff --git a/src/test/java/org/jsoup/nodes/NodeIteratorTest.java b/src/test/java/org/jsoup/nodes/NodeIteratorTest.java index ab7e9345e5..b5b77fbc83 100644 --- a/src/test/java/org/jsoup/nodes/NodeIteratorTest.java +++ b/src/test/java/org/jsoup/nodes/NodeIteratorTest.java @@ -3,6 +3,7 @@ import org.jsoup.Jsoup; import org.junit.jupiter.api.Test; +import java.util.Iterator; import java.util.NoSuchElementException; import static org.junit.jupiter.api.Assertions.*; @@ -230,7 +231,7 @@ class NodeIteratorTest { assertContents(doc, "#root;html;head;body;div#1;p;One++;p;Two++;div#2;p;Three++;p;Four++;"); } - static void assertIterates(NodeIterator it, String expected) { + static void assertIterates(Iterator it, String expected) { Node previous = null; StringBuilder actual = new StringBuilder(); while (it.hasNext()) { @@ -263,4 +264,4 @@ else if (node instanceof TextNode) actual.append(";"); } -} \ No newline at end of file +}