diff options
Diffstat (limited to 'server/src/org/jsoup/nodes/Element.java')
-rw-r--r-- | server/src/org/jsoup/nodes/Element.java | 790 |
1 files changed, 513 insertions, 277 deletions
diff --git a/server/src/org/jsoup/nodes/Element.java b/server/src/org/jsoup/nodes/Element.java index 5c1894c934..ff9e68b962 100644 --- a/server/src/org/jsoup/nodes/Element.java +++ b/server/src/org/jsoup/nodes/Element.java @@ -1,5 +1,15 @@ package org.jsoup.nodes; +import java.util.ArrayList; +import java.util.Arrays; +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; @@ -9,44 +19,46 @@ import org.jsoup.select.Elements; import org.jsoup.select.Evaluator; import org.jsoup.select.Selector; -import java.util.*; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - /** - * A HTML element consists of a tag name, attributes, and child nodes (including text nodes and - * other elements). + * 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. + * 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 + * @param tag + * tag of this element + * @param baseUri + * the base URI + * @param attributes + * initial attributes * @see #appendChild(Node) * @see #appendElement(String) */ 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. + * @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. * @see Tag#valueOf(String) */ public Element(Tag tag, String baseUri) { @@ -68,10 +80,11 @@ public class Element extends Node { } /** - * Change the tag of this element. For example, convert a {@code <span>} to a {@code <div>} with - * {@code el.tagName("div");}. - * - * @param tagName new tag name for this element + * Change the tag of this element. For example, convert a {@code <span>} to + * a {@code <div>} with {@code el.tagName("div");}. + * + * @param tagName + * new tag name for this element * @return this element, for chaining */ public Element tagName(String tagName) { @@ -88,10 +101,10 @@ public class Element extends Node { 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}). + * 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) */ @@ -110,27 +123,32 @@ public class Element extends Node { } /** - * 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. + * 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 */ + @Override public Element attr(String attributeKey, String attributeValue) { super.attr(attributeKey, attributeValue); return this; } /** - * Get this element's HTML5 custom data attributes. Each attribute in the element that has a key - * starting with "data-" is included the dataset. + * Get this element's HTML5 custom data attributes. Each attribute in the + * element that has a key starting with "data-" is included the dataset. * <p> - * E.g., the element {@code <div data-package="jsoup" data-language="Java" class="group">...} has the dataset - * {@code package=jsoup, language=java}. + * E.g., the element + * {@code <div data-package="jsoup" data-language="Java" class="group">...} + * has the dataset {@code package=jsoup, language=java}. * <p> - * This map is a filtered view of the element's attribute map. Changes to one map (add, remove, update) are reflected - * in the other map. + * This map is a filtered view of the element's attribute map. Changes to + * one map (add, remove, update) are reflected in the other map. * <p> - * You can find elements that have data attributes using the {@code [^data-]} attribute key prefix selector. + * You can find elements that have data attributes using the + * {@code [^data-]} attribute key prefix selector. + * * @return a map of {@code key=value} custom data attributes. */ public Map<String, String> dataset() { @@ -144,6 +162,7 @@ public class Element extends Node { /** * Get this element's parent and ancestors, up to the document root. + * * @return this element's stack of parents, closest first. */ public Elements parents() { @@ -163,10 +182,12 @@ public class Element extends Node { /** * Get a child element of this element, by its 0-based index number. * <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. + * 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 + * @param index + * the index number of the element to retrieve * @return the child element, if it exists, or {@code null} if absent. * @see #childNode(int) */ @@ -177,140 +198,165 @@ public class Element extends Node { /** * Get this element's child elements. * <p/> - * This is effectively a filter on {@link #childNodes()} to get Element nodes. - * @return child elements. If this element has no children, returns an - * empty list. + * This is effectively a filter on {@link #childNodes()} to get Element + * nodes. + * + * @return child elements. If this element has no children, returns an empty + * list. * @see #childNodes() */ public Elements children() { - // create on the fly rather than maintaining two lists. if gets slow, memoize, and mark dirty on change + // create on the fly rather than maintaining two lists. if gets slow, + // memoize, and mark dirty on change List<Element> elements = new ArrayList<Element>(); for (Node node : childNodes) { - if (node instanceof Element) + if (node instanceof Element) { elements.add((Element) node); + } } return new Elements(elements); } /** - * Get this element's child text nodes. The list is unmodifiable but the text nodes may be manipulated. + * Get this element's child text nodes. The list is unmodifiable but the + * text nodes may be manipulated. * <p/> * This is effectively a filter on {@link #childNodes()} to get Text nodes. + * * @return child text nodes. If this element has no text nodes, returns an - * empty list. - * <p/> - * For example, with the input HTML: {@code <p>One <span>Two</span> Three <br> Four</p>} with the {@code p} element selected: - * <ul> - * <li>{@code p.text()} = {@code "One Two Three Four"}</li> - * <li>{@code p.ownText()} = {@code "One Three Four"}</li> - * <li>{@code p.children()} = {@code Elements[<span>, <br>]}</li> - * <li>{@code p.childNodes()} = {@code List<Node>["One ", <span>, " Three ", <br>, " Four"]}</li> - * <li>{@code p.textNodes()} = {@code List<TextNode>["One ", " Three ", " Four"]}</li> - * </ul> + * empty list. + * <p/> + * For example, with the input HTML: + * {@code <p>One <span>Two</span> Three <br> Four</p>} with the + * {@code p} element selected: <ul> + * <li>{@code p.text()} = {@code "One Two Three Four"}</li> + * <li>{@code p.ownText()} = {@code "One Three Four"}</li> + * <li>{@code p.children()} = {@code Elements[<span>, <br> + * ]}</li> + * <li>{@code p.childNodes()} = {@code List<Node>["One ", <span>, " Three ", + * <br> + * , " Four"]}</li> + * <li>{@code p.textNodes()} = + * {@code List<TextNode>["One ", " Three ", " Four"]}</li> + * </ul> */ public List<TextNode> textNodes() { List<TextNode> textNodes = new ArrayList<TextNode>(); for (Node node : childNodes) { - if (node instanceof TextNode) + if (node instanceof TextNode) { textNodes.add((TextNode) node); + } } return Collections.unmodifiableList(textNodes); } /** - * Get this element's child data nodes. The list is unmodifiable but the data nodes may be manipulated. + * Get this element's child data nodes. The list is unmodifiable but the + * data nodes may be manipulated. * <p/> * This is effectively a filter on {@link #childNodes()} to get Data nodes. + * * @return child data nodes. If this element has no data nodes, returns an - * empty list. + * empty list. * @see #data() */ public List<DataNode> dataNodes() { List<DataNode> dataNodes = new ArrayList<DataNode>(); for (Node node : childNodes) { - if (node instanceof DataNode) + if (node instanceof DataNode) { dataNodes.add((DataNode) node); + } } return Collections.unmodifiableList(dataNodes); } /** - * Find elements that match the {@link Selector} CSS query, with this element as the starting context. Matched elements - * may include this element, or any of its children. + * Find elements that match the {@link Selector} CSS query, with this + * element as the starting context. Matched elements may include this + * element, or any of its children. * <p/> - * This method is generally more powerful to use than the DOM-type {@code getElementBy*} methods, because - * multiple filters can be combined, e.g.: + * This method is generally more powerful to use than the DOM-type + * {@code getElementBy*} methods, because multiple filters can be combined, + * e.g.: * <ul> - * <li>{@code el.select("a[href]")} - finds links ({@code a} tags with {@code href} attributes) - * <li>{@code el.select("a[href*=example.com]")} - finds links pointing to example.com (loosely) + * <li>{@code el.select("a[href]")} - finds links ({@code a} tags with + * {@code href} attributes) + * <li>{@code el.select("a[href*=example.com]")} - finds links pointing to + * example.com (loosely) * </ul> * <p/> * See the query syntax documentation in {@link org.jsoup.select.Selector}. - * - * @param cssQuery a {@link Selector} CSS-like query + * + * @param cssQuery + * a {@link Selector} CSS-like query * @return elements that match the query (empty if none match) * @see org.jsoup.select.Selector */ public Elements select(String cssQuery) { return Selector.select(cssQuery, this); } - + /** * Add a node child node to this element. * - * @param child node to add. Must not already have a parent. + * @param child + * node to add. Must not already have a parent. * @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. Must not already have a parent. + * @param child + * node to add. Must not already have a parent. * @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; } - + /** * 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}). + * @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");} + * {@code parent.appendElement("h1").attr("id", "header").text("Welcome");} */ public Element appendElement(String tagName) { Element child = new Element(Tag.valueOf(tagName), baseUri()); 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}). + * @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");} + * {@code parent.prependElement("h1").attr("id", "header").text("Welcome");} */ public Element prependElement(String tagName) { Element child = new Element(Tag.valueOf(tagName), baseUri()); prependChild(child); return child; } - + /** * Create and append a new TextNode to this element. * - * @param text the unencoded text to add + * @param text + * the unencoded text to add * @return this element */ public Element appendText(String text) { @@ -318,11 +364,12 @@ public class Element extends Node { appendChild(node); return this; } - + /** * Create and prepend a new TextNode to this element. * - * @param text the unencoded text to add + * @param text + * the unencoded text to add * @return this element */ public Element prependText(String text) { @@ -330,10 +377,13 @@ public class Element extends Node { 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 + * 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 * @return this element * @see #html(String) */ @@ -344,25 +394,30 @@ public class Element extends Node { 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 + * 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 * @return this element * @see #html(String) */ 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; } /** - * Insert the specified HTML into the DOM before this element (i.e. as a preceding sibling). - * - * @param html HTML to add before this element + * Insert the specified HTML into the DOM before this element (i.e. as a + * preceding sibling). + * + * @param html + * HTML to add before this element * @return this element, for chaining * @see #after(String) */ @@ -372,8 +427,11 @@ public class Element extends Node { } /** - * Insert the specified node into the DOM before this node (i.e. as a preceding sibling). - * @param node to add before this element + * Insert the specified node into the DOM before this node (i.e. as a + * preceding sibling). + * + * @param node + * to add before this element * @return this Element, for chaining * @see #after(Node) */ @@ -383,9 +441,11 @@ public class Element extends Node { } /** - * Insert the specified HTML into the DOM after this element (i.e. as a following sibling). - * - * @param html HTML to add after this element + * Insert the specified HTML into the DOM after this element (i.e. as a + * following sibling). + * + * @param html + * HTML to add after this element * @return this element, for chaining * @see #before(String) */ @@ -395,8 +455,11 @@ public class Element extends Node { } /** - * Insert the specified node into the DOM after this node (i.e. as a following sibling). - * @param node to add after this element + * Insert the specified node into the DOM after this node (i.e. as a + * following sibling). + * + * @param node + * to add after this element * @return this element, for chaining * @see #before(Node) */ @@ -407,6 +470,7 @@ public class Element extends Node { /** * Remove all of the element's child nodes. Any attributes are left as-is. + * * @return this element */ public Element empty() { @@ -416,8 +480,10 @@ public class Element extends Node { /** * Wrap the supplied HTML around this element. - * - * @param html HTML to wrap around this element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep. + * + * @param html + * HTML to wrap around this element, e.g. + * {@code <div class="head"></div>}. Can be arbitrarily deep. * @return this element, for chaining. */ @Override @@ -426,94 +492,118 @@ public class Element extends Node { } /** - * 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. + * 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() { - if (parentNode == null) + 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) + for (Element el : elements) { + if (el != this) { siblings.add(el); + } + } return siblings; } /** - * 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}. + * 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 + * This is similar to {@link #nextSibling()}, but specifically finds only + * Elements + * * @return the next element, or null if there is no next element * @see #previousElementSibling() */ public Element nextElementSibling() { - if (parentNode == null) return null; + if (parentNode == null) { + return null; + } List<Element> siblings = parent().children(); Integer index = indexInList(this, siblings); Validate.notNull(index); - if (siblings.size() > index+1) - return siblings.get(index+1); - else + if (siblings.size() > index + 1) { + return siblings.get(index + 1); + } else { return null; + } } /** * Gets the previous element sibling of this element. + * * @return the previous element, or null if there is no previous element * @see #nextElementSibling() */ public Element previousElementSibling() { - if (parentNode == null) return null; + if (parentNode == null) { + return null; + } List<Element> siblings = parent().children(); Integer index = indexInList(this, siblings); Validate.notNull(index); - if (index > 0) - return siblings.get(index-1); - else + if (index > 0) { + return siblings.get(index - 1); + } else { return null; + } } /** * 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. + * Get the list index of this element in its element sibling list. I.e. if + * this is the first element sibling, returns 0. + * * @return position in element sibling list */ public Integer elementSiblingIndex() { - if (parent() == null) return 0; - return indexInList(this, parent().children()); + if (parent() == null) { + return 0; + } + 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) { + + private static <E extends Element> Integer indexInList(Element search, + List<E> elements) { Validate.notNull(search); Validate.notNull(elements); for (int i = 0; i < elements.size(); i++) { E element = elements.get(i); - if (element.equals(search)) + if (element.equals(search)) { return i; + } } return null; } @@ -521,9 +611,13 @@ public class Element extends Node { // DOM type methods /** - * Finds elements, including and recursively under this element, with the specified tag name. - * @param tagName The tag name to search for (case insensitively). - * @return a matching unmodifiable list of elements. Will be empty if this element and none of its children match. + * Finds elements, including and recursively under this element, with the + * specified tag name. + * + * @param tagName + * The tag name to search for (case insensitively). + * @return a matching unmodifiable list of elements. Will be empty if this + * element and none of its children match. */ public Elements getElementsByTag(String tagName) { Validate.notEmpty(tagName); @@ -535,29 +629,37 @@ public class Element extends Node { /** * Find an element by ID, including or under this element. * <p> - * Note that this finds the first matching ID, starting with this element. If you search down from a different - * starting point, it is possible to find a different element by ID. For unique element by ID within a Document, - * use {@link Document#getElementById(String)} - * @param id The ID to search for. - * @return The first matching element by ID, starting with this element, or null if none found. + * Note that this finds the first matching ID, starting with this element. + * If you search down from a different starting point, it is possible to + * find a different element by ID. For unique element by ID within a + * Document, use {@link Document#getElementById(String)} + * + * @param id + * The ID to search for. + * @return The first matching element by ID, starting with this element, or + * null if none found. */ public Element getElementById(String id) { Validate.notEmpty(id); - + Elements elements = Collector.collect(new Evaluator.Id(id), this); - if (elements.size() > 0) + if (elements.size() > 0) { return elements.get(0); - else + } else { return null; + } } /** - * Find elements that have this class, including or under this element. Case insensitive. + * Find elements that have this class, including or under this element. Case + * insensitive. * <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");}. + * 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. + * @param className + * the name of the class to search for. * @return elements with the supplied class name, empty if none * @see #hasClass(String) * @see #classNames() @@ -570,8 +672,9 @@ public class Element extends Node { /** * Find elements that have a named attribute set. Case insensitive. - * - * @param key name of the attribute, e.g. {@code href} + * + * @param key + * name of the attribute, e.g. {@code href} * @return elements that have this attribute, empty if none */ public Elements getElementsByAttribute(String key) { @@ -582,88 +685,129 @@ public class Element extends Node { } /** - * Find elements that have an attribute name starting with the supplied prefix. Use {@code data-} to find elements - * that have HTML5 datasets. - * @param keyPrefix name prefix of the attribute e.g. {@code data-} - * @return elements that have attribute names that start with with the prefix, empty if none. + * Find elements that have an attribute name starting with the supplied + * prefix. Use {@code data-} to find elements that have HTML5 datasets. + * + * @param keyPrefix + * name prefix of the attribute e.g. {@code data-} + * @return elements that have attribute names that start with with the + * prefix, empty if none. */ public Elements getElementsByAttributeStarting(String keyPrefix) { Validate.notEmpty(keyPrefix); keyPrefix = keyPrefix.trim().toLowerCase(); - return Collector.collect(new Evaluator.AttributeStarting(keyPrefix), this); + return Collector.collect(new Evaluator.AttributeStarting(keyPrefix), + this); } /** - * Find elements that have an attribute with the specific value. Case insensitive. + * Find elements that have an attribute with the specific value. Case + * insensitive. * - * @param key name of the attribute - * @param value value of the attribute + * @param key + * name of the attribute + * @param value + * value of the attribute * @return elements that have this attribute with this value, empty if none */ public Elements getElementsByAttributeValue(String key, String value) { - return Collector.collect(new Evaluator.AttributeWithValue(key, value), this); + return Collector.collect(new Evaluator.AttributeWithValue(key, value), + this); } /** - * Find elements that either do not have this attribute, or have it with a different value. Case insensitive. + * 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 + * @param key + * name of the attribute + * @param value + * value of the attribute * @return elements that do not have a matching attribute */ public Elements getElementsByAttributeValueNot(String key, String value) { - return Collector.collect(new Evaluator.AttributeWithValueNot(key, value), this); + return Collector.collect( + new Evaluator.AttributeWithValueNot(key, value), this); } /** - * Find elements that have attributes that start with the value prefix. Case insensitive. + * 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 + * @param key + * name of the attribute + * @param valuePrefix + * start of attribute value * @return elements that have attributes that start with the value prefix */ - public Elements getElementsByAttributeValueStarting(String key, String valuePrefix) { - return Collector.collect(new Evaluator.AttributeWithValueStarting(key, valuePrefix), this); + public Elements getElementsByAttributeValueStarting(String key, + String valuePrefix) { + return Collector.collect(new Evaluator.AttributeWithValueStarting(key, + valuePrefix), this); } /** - * Find elements that have attributes that end with the value suffix. Case insensitive. + * 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 + * @param key + * name of the attribute + * @param valueSuffix + * end of the attribute value * @return elements that have attributes that end with the value suffix */ - public Elements getElementsByAttributeValueEnding(String key, String valueSuffix) { - return Collector.collect(new Evaluator.AttributeWithValueEnding(key, valueSuffix), this); + public Elements getElementsByAttributeValueEnding(String key, + String valueSuffix) { + return Collector.collect(new Evaluator.AttributeWithValueEnding(key, + valueSuffix), this); } /** - * Find elements that have attributes whose value contains the match string. Case insensitive. + * 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 + * @param key + * name of the attribute + * @param match + * substring of value to search for * @return elements that have attributes containing this text */ - public Elements getElementsByAttributeValueContaining(String key, String match) { - return Collector.collect(new Evaluator.AttributeWithValueContaining(key, match), this); + 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 - * @param pattern compiled regular expression to match against attribute values + * Find elements that have attributes whose values match the supplied + * regular expression. + * + * @param key + * name of the attribute + * @param pattern + * compiled regular expression to match against attribute values * @return elements that have attributes matching this regular expression */ - public Elements getElementsByAttributeValueMatching(String key, Pattern pattern) { - return Collector.collect(new Evaluator.AttributeWithValueMatching(key, pattern), this); - + 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 - * @param regex regular expression to match against attribute values. 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. + * Find elements that have attributes whose values match the supplied + * regular expression. + * + * @param key + * name of the attribute + * @param regex + * regular expression to match against attribute values. 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. * @return elements that have attributes matching this regular expression */ public Elements getElementsByAttributeValueMatching(String key, String regex) { @@ -671,73 +815,94 @@ public class Element extends Node { try { pattern = Pattern.compile(regex); } catch (PatternSyntaxException e) { - throw new IllegalArgumentException("Pattern syntax error: " + regex, e); + throw new IllegalArgumentException( + "Pattern syntax error: " + regex, e); } return getElementsByAttributeValueMatching(key, pattern); } - + /** * Find elements whose sibling index is less than the supplied index. - * @param index 0-based index + * + * @param index + * 0-based index * @return elements less than index */ 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 + * + * @param index + * 0-based index * @return elements greater than 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 + * + * @param index + * 0-based index * @return elements equal to 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. - * @param searchText to look for in the element's text + * 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. + * + * @param searchText + * to look for in the element's text * @return elements that contain the string, case insensitive. * @see Element#text() */ 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. - * @param searchText to look for in the element's own text + * 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. + * + * @param searchText + * to look for in the element's own text * @return elements that contain the string, case insensitive. * @see Element#ownText() */ public Elements getElementsContainingOwnText(String searchText) { - return Collector.collect(new Evaluator.ContainsOwnText(searchText), this); + 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 + * + * @param pattern + * regular expression to match text against * @return elements matching the supplied regular expression. * @see Element#text() */ 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. + * + * @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. * @return elements matching the supplied regular expression. * @see Element#text() */ @@ -746,24 +911,32 @@ public class Element extends Node { try { pattern = Pattern.compile(regex); } catch (PatternSyntaxException e) { - throw new IllegalArgumentException("Pattern syntax error: " + regex, e); + throw new IllegalArgumentException( + "Pattern syntax error: " + regex, e); } return getElementsMatchingText(pattern); } - + /** * Find elements whose own text matches the supplied regular expression. - * @param pattern regular expression to match text against + * + * @param pattern + * regular expression to match text against * @return elements matching the supplied regular expression. * @see Element#ownText() */ 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. + * + * @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. * @return elements matching the supplied regular expression. * @see Element#ownText() */ @@ -772,13 +945,15 @@ public class Element extends Node { try { pattern = Pattern.compile(regex); } catch (PatternSyntaxException e) { - throw new IllegalArgumentException("Pattern syntax error: " + regex, e); + throw new IllegalArgumentException( + "Pattern syntax error: " + regex, e); } return getElementsMatchingOwnText(pattern); } - + /** - * Find all elements under this element (including self, and children of children). + * Find all elements under this element (including self, and children of + * children). * * @return all elements */ @@ -789,8 +964,9 @@ public class Element extends Node { /** * Gets the combined text of this element and all its children. * <p> - * For example, given HTML {@code <p>Hello <b>there</b> now!</p>}, {@code p.text()} returns {@code "Hello there now!"} - * + * For example, given HTML {@code <p>Hello <b>there</b> now!</p>}, + * {@code p.text()} returns {@code "Hello there now!"} + * * @return unencoded text, or empty string if none. * @see #ownText() * @see #textNodes() @@ -803,27 +979,32 @@ public class Element extends Node { private void text(StringBuilder accum) { appendWhitespaceIfBr(this, accum); - + for (Node child : childNodes) { if (child instanceof TextNode) { TextNode textNode = (TextNode) child; appendNormalisedText(accum, textNode); } else if (child instanceof Element) { Element element = (Element) child; - if (accum.length() > 0 && element.isBlock() && !TextNode.lastCharIsWhitespace(accum)) + if (accum.length() > 0 && element.isBlock() + && !TextNode.lastCharIsWhitespace(accum)) { accum.append(" "); + } element.text(accum); } } } /** - * Gets the text owned by this element only; does not get the combined text of all children. + * Gets the text owned by this element only; does not get the combined text + * of all children. * <p> - * For example, given HTML {@code <p>Hello <b>there</b> now!</p>}, {@code p.ownText()} returns {@code "Hello now!"}, - * whereas {@code p.text()} returns {@code "Hello there now!"}. - * Note that the text within the {@code b} element is not returned, as it is not a direct child of the {@code p} element. - * + * For example, given HTML {@code <p>Hello <b>there</b> now!</p>}, + * {@code p.ownText()} returns {@code "Hello now!"}, whereas + * {@code p.text()} returns {@code "Hello there now!"}. Note that the text + * within the {@code b} element is not returned, as it is not a direct child + * of the {@code p} element. + * * @return unencoded text, or empty string if none. * @see #text() * @see #textNodes() @@ -850,24 +1031,32 @@ public class Element extends Node { if (!preserveWhitespace()) { text = TextNode.normaliseWhitespace(text); - if (TextNode.lastCharIsWhitespace(accum)) + if (TextNode.lastCharIsWhitespace(accum)) { text = TextNode.stripLeadingWhitespace(text); + } } accum.append(text); } - private static void appendWhitespaceIfBr(Element element, StringBuilder accum) { - if (element.tag.getName().equals("br") && !TextNode.lastCharIsWhitespace(accum)) + private static void appendWhitespaceIfBr(Element element, + StringBuilder accum) { + if (element.tag.getName().equals("br") + && !TextNode.lastCharIsWhitespace(accum)) { accum.append(" "); + } } boolean preserveWhitespace() { - return tag.preserveWhitespace() || parent() != null && parent().preserveWhitespace(); + return tag.preserveWhitespace() || parent() != null + && parent().preserveWhitespace(); } /** - * Set the text of this element. Any existing contents (text or elements) will be cleared - * @param text unencoded text + * Set the text of this element. Any existing contents (text or elements) + * will be cleared + * + * @param text + * unencoded text * @return this element */ public Element text(String text) { @@ -881,28 +1070,33 @@ public class Element extends Node { } /** - Test if this element has any text content (that is not just whitespace). - @return true if element has non-blank text content. + * Test if this element has any text content (that is not just whitespace). + * + * @return true if element has non-blank text content. */ public boolean hasText() { - for (Node child: childNodes) { + for (Node child : childNodes) { if (child instanceof TextNode) { TextNode textNode = (TextNode) child; - if (!textNode.isBlank()) + if (!textNode.isBlank()) { return true; + } } else if (child instanceof Element) { Element el = (Element) child; - if (el.hasText()) + if (el.hasText()) { return true; + } } } return false; } /** - * Get the combined data of this element. Data is e.g. the inside of a {@code script} tag. + * Get the combined data of this element. Data is e.g. the inside of a + * {@code script} tag. + * * @return the data, or empty string if none - * + * * @see #dataNodes() */ public String data() { @@ -919,21 +1113,28 @@ public class Element extends Node { } } return sb.toString(); - } + } /** - * Gets the literal value of this element's "class" attribute, which may include multiple class names, space - * separated. (E.g. on <code><div class="header gray"></code> returns, "<code>header gray</code>") - * @return The literal class attribute, or <b>empty string</b> if no class attribute set. + * Gets the literal value of this element's "class" attribute, which may + * include multiple class names, space separated. (E.g. on + * <code><div class="header gray"></code> returns, " + * <code>header gray</code>") + * + * @return The literal class attribute, or <b>empty string</b> if no class + * attribute set. */ public String className() { return attr("class"); } /** - * Get all of the element's class names. E.g. on element {@code <div class="header gray"}>}, - * returns a set of two elements {@code "header", "gray"}. Note that modifications to this set are not pushed to - * the backing {@code class} attribute; use the {@link #classNames(java.util.Set)} method to persist them. + * Get all of the element's class names. E.g. on element + * {@code <div class="header gray"}>}, returns a set of two elements + * {@code "header", "gray"}. Note that modifications to this set are not + * pushed to the backing {@code class} attribute; use the + * {@link #classNames(java.util.Set)} method to persist them. + * * @return set of classnames, empty if no class attribute */ public Set<String> classNames() { @@ -945,9 +1146,11 @@ public class Element extends Node { } /** - Set the element's {@code class} attribute to the supplied class names. - @param classNames set of classes - @return this element, for chaining + * Set the element's {@code class} attribute to the supplied class names. + * + * @param classNames + * set of classes + * @return this element, for chaining */ public Element classNames(Set<String> classNames) { Validate.notNull(classNames); @@ -957,22 +1160,27 @@ public class Element extends Node { /** * Tests if this element has a class. Case insensitive. - * @param className name of class to check for + * + * @param className + * name of class to check for * @return true if it does, false if not */ public boolean hasClass(String className) { Set<String> classNames = classNames(); for (String name : classNames) { - if (className.equalsIgnoreCase(name)) + if (className.equalsIgnoreCase(name)) { return true; + } } return false; } /** - Add a class name to this element's {@code class} attribute. - @param className class name to add - @return this element + * Add a class name to this element's {@code class} attribute. + * + * @param className + * class name to add + * @return this element */ public Element addClass(String className) { Validate.notNull(className); @@ -985,9 +1193,11 @@ public class Element extends Node { } /** - Remove a class name from this element's {@code class} attribute. - @param className class name to remove - @return this element + * Remove a class name from this element's {@code class} attribute. + * + * @param className + * class name to remove + * @return this element */ public Element removeClass(String className) { Validate.notNull(className); @@ -1000,90 +1210,114 @@ public class Element extends Node { } /** - Toggle a class name on this element's {@code class} attribute: if present, remove it; otherwise add it. - @param className class name to toggle - @return this element + * Toggle a class name on this element's {@code class} attribute: if + * present, remove it; otherwise add it. + * + * @param className + * class name to toggle + * @return this element */ public Element toggleClass(String className) { Validate.notNull(className); Set<String> classes = classNames(); - if (classes.contains(className)) + if (classes.contains(className)) { classes.remove(className); - else + } else { classes.add(className); + } classNames(classes); 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. */ public String val() { - if (tagName().equals("textarea")) + if (tagName().equals("textarea")) { return text(); - else + } else { return attr("value"); + } } - + /** * Set the value of a form element (input, textarea, etc). - * @param value value to set + * + * @param value + * value to set * @return this element (for chaining) */ public Element val(String value) { - if (tagName().equals("textarea")) + if (tagName().equals("textarea")) { text(value); - else + } else { attr("value", value); + } return this; } - void outerHtmlHead(StringBuilder accum, int depth, Document.OutputSettings out) { - if (accum.length() > 0 && out.prettyPrint() && (tag.formatAsBlock() || (parent() != null && parent().tag().formatAsBlock()))) + @Override + void outerHtmlHead(StringBuilder accum, int depth, + Document.OutputSettings out) { + if (accum.length() > 0 + && out.prettyPrint() + && (tag.formatAsBlock() || (parent() != null && parent().tag() + .formatAsBlock()))) { indent(accum, depth, out); - accum - .append("<") - .append(tagName()); + } + accum.append("<").append(tagName()); attributes.html(accum, out); - if (childNodes.isEmpty() && tag.isSelfClosing()) + if (childNodes.isEmpty() && tag.isSelfClosing()) { accum.append(" />"); - else + } else { accum.append(">"); + } } - void outerHtmlTail(StringBuilder accum, int depth, Document.OutputSettings out) { + @Override + void outerHtmlTail(StringBuilder accum, int depth, + Document.OutputSettings out) { if (!(childNodes.isEmpty() && tag.isSelfClosing())) { - if (out.prettyPrint() && !childNodes.isEmpty() && tag.formatAsBlock()) + if (out.prettyPrint() && !childNodes.isEmpty() + && tag.formatAsBlock()) { indent(accum, depth, out); + } accum.append("</").append(tagName()).append(">"); } } /** - * 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>}.) + * 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() */ public String html() { StringBuilder accum = new StringBuilder(); - html(accum); + html(accum); return accum.toString().trim(); } private void html(StringBuilder accum) { - for (Node node : childNodes) + 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 + * + * @param html + * HTML to parse and set into this element * @return this element * @see #append(String) */ @@ -1093,6 +1327,7 @@ public class Element extends Node { return this; } + @Override public String toString() { return outerHtml(); } @@ -1113,7 +1348,8 @@ public class Element extends Node { @Override public Element clone() { Element clone = (Element) super.clone(); - clone.classNames(); // creates linked set of class names from class attribute + clone.classNames(); // creates linked set of class names from class + // attribute return clone; } } |