Package Torello.HTML

Class TagNode

  • All Implemented Interfaces:
    java.io.Serializable, java.lang.CharSequence, java.lang.Cloneable, java.lang.Comparable<TagNode>

    public final class TagNode
    extends HTMLNode
    implements java.lang.CharSequence, java.io.Serializable, java.lang.Cloneable, java.lang.Comparable<TagNode>
    TagNode - Documentation.

    This class is intended to represent HTMLNode's that are Tags, also known as 'elements.'

    Here, below, are just simple examples of what constitute an instance of this class TagNode. Technically speaking, each of the String's below would be stored in the ancestor / parent class HTMLNode.str field. To retrieve the actual text-String of an HTML Element, just reference TagNode.str to get that String ().

    Example:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
     <A HREF="http://some.url.com/index.html">
     <IMG SRC="https://some.image.url.net/image.jpg">
     <DIV ID="MainDivClass">
     <BODY ONLOAD="callInit();">
     <HEAD>, <TITLE>
     <TABLE CLASS="DataTables" ID="Weights" STYLE="width: 80%; color: red; background: black">
     </TABLE>
     </SPAN>
     </A>
     </BODY>
     etc...
    


    The three inherited classes of abstract class HTMLNode are very light-weight, and contain some amount of public methods, but do not have heavy internal-state (either static, or non-static). Below is a list of the internal field's that are added to each of the three instantiations of the ancestor HTMLNode class:

    • class TagNode adds a field public final boolean isClosing - which tells a user if this tag has a forward-slash immediately following the '<' (less-than symbol) at character position 2. This is how one identifies a 'closing-version' of the element, for instance: '</DIV>' and '</SPAN>' would both have their public final boolean isClosing fields set to TRUE. There is also a public final String tok field added to instances of TagNode that identify what html element the TagNode represents. For example an HTML Element such as: <A HREF="http://My.URL.com" TARGET=_blank>, would have it's String 'tok' field set to 'a'

    • class TextNode this inherited class from class HTMLNode does not add any internal state at all. It has the exact same internally-maintained fields as its parent-class. The public final String str field merely states what text this text-node actually represents.

    • class CommentNode for searching-purposes, and ease-of-use, class CommentNode, which is the third and final class to inherit HTMLNode keeps one extra internal-field, which is public final String body. This field is a redundant, duplicate, of the internal string public final String str - which is inherited from the HTML Node class. The subtle difference is that, since comment nodes represent the HTML <!-- and --> symbols, the 'body' of the comment sometimes needs to be searched, quickly. The public final String body leaves off these leading and ending comment delimiter symbols: <!-- and -->
    See Also:
    TextNode, CommentNode, HTMLNode, Serialized Form



    • Nested Class Summary

      Nested Classes 
      Modifier and Type Class
      static class  TagNode.AttrRegEx
    • Constructor Summary

      Constructors 
      Constructor
      TagNode​(String s)
      TagNode​(String tok, Properties attributes, Iterable<String> keyOnlyAttributes, SD quotes, boolean addEndingForwardSlash)
      TagNode​(String tok, Properties attributes, SD quotes, boolean addEndingForwardSlash)
    • Field Detail

      • serialVersionUID

        public static final long serialVersionUID
        This fulfils the SerialVersion UID requirement for all classes that implement Java's interface java.io.Serializable. Using the Serializable Implementation offered by java is very easy, and can make saving program state when debugging a lot easier. It can also be used in place of more complicated systems like "hibernate" to store data as well.
        See Also:
        Constant Field Values
        Code:
        Exact Field Declaration Expression:
        1
        public static final long serialVersionUID = 1;
        
      • tok

        public final java.lang.String tok
        This variable, always lower-case, and always final, as well retains the "token" or "element name" (of the HTML Element) in an independent field of this class. Remember that the complete element is retained in the HTMLNode.str field which is a public, final member field of the parent-class.

        IMPORTANT: As mentioned, 'class TagNode' string-field, 'public final String tok' is always stored here with lower-case characters. Furthermore, since TagNode's are an immutable class (there are no fields or methods that may be modified inside of an instance of TagNode) once the object has been constructed. This 'tok' field also may not be changed after the object is constructed.

        Here is a table of some common HTML Elements found on web-pages. In each row of this table, the HTML Element, which is precisely equal to the 'public final String str' field of the parent class ('HTMLNode') is shown in the first column of the table. The second column of the table shows what the 'public final String tok' field would contain if the primary 'str' field were as chosen from column 1.

        Field: public final String str Field: public final String tok
        <A HREF="http://some.url.com"> "a"(all lower-case).
        <div class="MyFavoriteClass"> "div"(all lower-case).
        <IMG SRC="MyImage.jpg"> "img"(all lower-case).
        </body> "body"(all lower-case).
        </SPAN> "span"(all lower-case).

        The 'tok' variable always keeps a copy of the "separated HTML tag" (first few characters of the constructor-passed-string) from the rest of the inner-tag information. This allows the programmer to extract or obtain what type of HTML-Tag this is, without re-doing the String-comparisons and String-substring operations.
        Code:
        Exact Field Declaration Expression:
        1
        public final String tok;
        
      • isClosing

        public final boolean isClosing
        If the public final String str field of 'this' object-instance begins with the character "</" (which 'identifies' itself as an HTML-closing tag), then this boolean-variable will be TRUE. If the public final String str field of this object does not begin with begins with "</", (and leaves off the beginning forward-slash, then this HTML-Tag Element should be through of as either a singleton (self-closing) tag, or if this HTML-Element is not self-closing, then this is an opening-HTML tag. In this case this boolean-field will be FALSE.

        The table below shows a few examples of what an HTML Element with the field for 'public final String str' set to the values in Column 1 would look like. The values in Column 2 represent what the resulting boolean would evaluate for the field 'public final boolean isClosing'

        Field: public final String str Field: public final boolean isClosing
        <A HREF="http://some.url.com"> FALSE
        <IMG SRC="MyImage.jpg"> FALSE
        </body> TRUE. Note that the tag begins with a forward-slash '/' character.
        </SPAN> TRUE. Note that the tag begins with a forward-slash '/' character.
        Code:
        Exact Field Declaration Expression:
        1
        public final boolean isClosing;
        
    • Constructor Detail

      • TagNode

        public TagNode​(java.lang.String s)
        Creates a TagNode, an inherited class of 'HTMLNode' that can be used as an element of an HTML Vector.

        NOTE: Attribute values are neither parsed, nor checked when this constructor is used. This constructor could allow malformed HTML to be passed to the public final String str field!

        Example:
        1
        2
        3
        4
        5
         TagNode tn = new TagNode("<DIV CLASS='SUMMARY' onmouseover=\"alert('Hello!');\">");
         System.out.println(tn.str);
         
         // Prints to Terminal: <DIV CLASS='SUMMARY' onmouseover="alert('Hello!');">
         
        
        Parameters:
        s - Any valid HTML tag, for instance: <H1>, <A HREF="somoe url">, <DIV ID="some id"> etc...
        Throws:
        MalformedTagNodeException - If the passed String wasn't valid - meaning it did not match the regular-expression parser.
        HTMLTokException - If the String found where the usual HTML token-element is situated is not a valid HTML element then the HTMLTokException will be thrown.
        See Also:
        HTMLTags.getTag_MEM_HEAP_CHECKOUT_COPY(String)
      • TagNode

        public TagNode​(java.lang.String tok,
                       java.util.Properties attributes,
                       SD quotes,
                       boolean addEndingForwardSlash)
        This will build a new TagNode that contains the inner tag values specified here. It is some-what checked for validity - though not all possible error cases are listed. If a major issue is discovered an exception is thrown.

        ATTRIBUTES: This constructor accepts only key-value attributes, Boolean / key-only attributes are NOT accepted by this constructor.

        Example:
        1
        2
        3
        4
        5
        6
        7
        8
        9
         Properties attributes = new Properties();
         attributes.put("CLASS", "SUMMARY");
         attributes.put("onmouseover", "alert('Hello');");
         
         TagNode tn = new TagNode("DIV", attributes, SD.DoubleQuotes, false);
         
         System.out.println(tn);
         // Prints To Terminal: <DIV CLASS="SUMMARY" onmouseover="alert('Hello');">
         
        
        Parameters:
        tok - This may be any valid HTML Element name. Its value will always be reduced to lower case ASCII. If this parameter does not contain a valid HTML element, then an exception is thrown.
        attributes - This must be a table of HTML inner-tag key-value a.k.a. HTML Attributes that are acceptable for using with the HTML element that is being created. Validity checking includes *only* the following:

        • If any key of Properties parameter 'p' contains characters outside of this ASCII-subset: [A..Za..z0..9_-]
        • If any key of parameter 'p' does not start with attributes a character from this subset: [A..Za..z]
        • If any value of Properties parameter 'p' has a "quote within quote" problem - a 'value' that contains quotation marks that are the same as the quotation-parameter SD quotes

        If these requirements fail, the exceptions listed below will be thrown.

        NOTE: When specifying a java.util.Properties parameter for which the quotes have already been added to the values inside the table, parameter SD quote must be set to null. If parameter quote were not null, a second set of surrounding quotes would be appended each attribute-value in the output HTML-Element - and likely generate a QuotesException, due to a "quotes within quotes" error.

        NOTE: This parameter may be null, and if so, no key-value attributes will be included in the HTML Element.
        quotes - This is either a single-quote (') or a double-quote 'attributes' parameter is an instance of the Enumerated-Type: SD from this package.

        NOTE ABOUT 'SD' This parameter (quotes) may be null. If 'null' is passed, then it should be expected that the contents of the Properties p parameter contains values that obey these characteristics:

        • Either: The values in Properties parameter 'p' already have quotes surrounding their String contents.
        • Or: The values in 'p' do not contain white-space, in which case, quotes are not mandatory.
        addEndingForwardSlash - There are a few (very few) instances where an "ending forward-slash" ('/') is expected at the end of the HTML -Tag. If this is desired, set this value to true.
        Throws:
        InnerTagKeyException - This exception will throw if any of the keys in the Properties parameter contain non-standard HTML Attribute / Inner-Tag key Strings. HTML expects that attribute-names conform to a set of rules in order to be processed properly by a browser. There is a 'check(...)' in class InnerTagKeyException which delineates the exact requirements made by HTML for attribute-names.
        QuotesException - if there are "quotes within quotes" problems, due to the values of the key-value pairs inside the input Properties hash-table.. This will also throw if the the 'quotes' parameter is passed 'null', and any of the property-values in the table contain white-space.
        HTMLTokException - if an invalid HTML 4 or 5 token is not present CASE_INSENSITIVE
        See Also:
        tok, InnerTagKeyException.check(String, String), QuotesException.check(String, SD, String), generateElementString(String, Properties, Iterable, SD, boolean)
      • TagNode

        public TagNode​(java.lang.String tok,
                       java.util.Properties attributes,
                       java.lang.Iterable<java.lang.String> keyOnlyAttributes,
                       SD quotes,
                       boolean addEndingForwardSlash)
        This will build a new TagNode that contains the inner tag attributes specified here. It is some-what checked for validity - though not all possible error cases are listed. If a major issue is discovered an exception is thrown.

        Example:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
         Properties attributes = new Properties();
         attributes.put("CLASS", "SUMMARY");
         attributes.put("onmouseover", "alert('Hello');");
        
         Vector<String> booleanAttributes = new Vector<>();
         booleanAttributes.add("HIDDEN");
         
         TagNode tn = new TagNode("DIV", attributes, booleanAttributes, SD.DoubleQuotes, false);
         
         System.out.println(tn);
         // Prints To Terminal: <DIV CLASS="SUMMARY" onmouseover="alert('Hello');" HIDDEN>
         
        


        ATTRIBUTES: This constructor accepts both key-value attributes, and boolean / key-only attributes as input. These two lists are passed through two different input-parameters.
        Parameters:
        tok - This may be any valid HTML Element name. Its value will always be reduced to lower case ASCII. If this parameter does not contain a valid HTML element, then an exception is thrown.
        attributes - This must be a table of HTML inner-tag key-value pairs, a.k.a. HTML 'Attributes', that are acceptable with the element that is being created. Validity checking includes *only* the following:

        • If any key of 'Properties' parameter 'attributes' contains characters outside of this ASCII-subset: [A..Za..z0..9_-]
        • If any key of 'attributes' does not start with a character from this subset: [A..Za..z]
        • If any value of 'attributes' has a "quote within quote" problem - a 'value' that contains quotation marks that are the same as the quotation parameter SD quotes

        If these requirements fail, the exceptions listed below will be thrown.

        NOTE: When specifying a java.util.Properties parameter for which the quotes have already been added to the values inside the table, parameter SD quote must be set to null. If parameter quote were not null, a second set of surrounding quotes would be appended each attribute-value in the output HTML-Element - and likely generate a QuotesException, due to a "quotes within quotes" error.

        NOTE: This parameter may be null, and if so, no key-value attributes will be included in the HTML Element.
        keyOnlyAttributes - This should be a valid list of "Attribute-Only" Inner-Tags. Such attributes are often called "Boolean Attributes." They are just a stand-alone keywords, without any value assignment. The CSS keyword 'HIDDEN' is a common boolean-attribute.

        NOTE: This parameter may be null, and if so, no boolean-attributes will be included in the HTML Element.
        quotes - This is either a single-quote (') or a double-quote ("). It uses the Enumerated Type: 'SD' from this package.

        NOTE ABOUT 'SD' This parameter 'quotes' may be null. If 'null' is passed, then it should be expected that the contents of the 'attributes' parameter contains values:

        • Either: The values in Properties parameter 'p' already have quotes surrounding their String contents.
        • Or: The values in 'p' do not contain white-space, in which case, quotes are not mandatory.

        addEndingForwardSlash - There are a few (very few) instances where an "ending forward-slash" is expected at the end of the HTML -Tag. If this is desired, set this value to TRUE.
        Throws:
        InnerTagKeyException - This exception will throw if any of the keys in the Properties parameter contain non-standard HTML Attribute / Inner-Tag key Strings. HTML expects that attribute-names conform to a set of rules in order to be processed properly by a browser. There is a 'check(...)' in class InnerTagKeyException which delineates the exact requirements made by HTML for attribute-names.
        QuotesException - If there are "quotes within quotes" problems when invoking the TagNode constructor, this exception will throw. The problem occurs when one or more of the attribute key-value pairs have a quotation-choice such that the chosen quotation-mark is also found within the attribute-value.

        QuotesException will also throw in the case that an attribute key-value pair has elected to use the "No Quotes" option, but the attribute-value contains white-space.
        HTMLTokException - if an invalid HTML 4 or 5 token is not present CASE_INSENSITIVE
        See Also:
        InnerTagKeyException.check(String, String), QuotesException.check(String, SD, String), generateElementString(String, Properties, Iterable, SD, boolean)
    • Method Detail

      • generateElementString

        protected static java.lang.String generateElementString​
                    (java.lang.String tok,
                     java.util.Properties p,
                     java.lang.Iterable<java.lang.String> keyOnlyAttributes,
                     SD quotesChoice,
                     boolean addEndingForwardSlash)
        
        This builds an HTML Element as a String. This String may be passed to the standard HTML TagNode Constructor that accepts a String as input.
        Parameters:
        tok - This is the HTML Element name. Valid String's that this parameter can accept include 'div', 'img', 'span', 'a', etc...
        p - This is a java.util.Properties table of HTML Attribute Key-Value Pairs.

        NOTE: This parameter may be null, and if so, no attribute-values will be included in the TagNode.
        keyOnlyAttributes - This is a list of keyword attributes that do not have value assignments, and are to be inserted into the HTML Element. This parameter could also be named 'Boolean Attributes.'

        NOTE: This parameter may be null, and if so, no boolean-attributes will be included in the TagNode.
        quotesChoice - These quotes are used to encapsulate the value String of all key-value pairs when building the HTML Element. This parameter may be null, and if it is - then no quotes will be added to the value String's that are inserted

        IMPORTANT: If different quotes-selections are to be used for different attribute key-value pairs, then those quotes should be provided inside the Properties data-structure - already pre-wrapped. When individualized quotes are needed, this parameter should be passed null.
        addEndingForwardSlash - When this receives TRUE, an ending '/' forward slash is appended to the second-to-last character of the output String.
        Returns:
        This method returns an HTML Element, as a String.
        Throws:
        InnerTagKeyException - This exception will throw if any of the keys in the Properties parameter contain non-standard HTML Attribute / Inner-Tag key Strings. HTML expects that attribute-names conform to a set of rules in order to be processed properly by a browser. There is a 'check(...)' in class InnerTagKeyException which delineates the exact requirements made by HTML for attribute-names.
        QuotesException - If there are "quotes within quotes" problems when invoking the TagNode constructor, this exception will throw. The problem occurs when one or more of the attribute key-value pairs have a quotation-choice such that the chosen quotation-mark is also found within the attribute-value.

        QuotesException will also throw in the case that an attribute key-value pair has elected to use the "No Quotes" option, but the attribute-value contains white-space.
        HTMLTokException - if an invalid HTML 4 or 5 token is not present CASE_INSENSITIVE
        See Also:
        HTMLTokException.check(String[]), InnerTagKeyException.check(String, String), QuotesException.check(String, SD, String)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
         String computedQuote = (quotesChoice == null) ? "" : ("" + quotesChoice.quote);
         HTMLTokException.check(tok);
        
         // The HTML Element is "built" using a StringBuilder
         StringBuilder sb = new StringBuilder();
         sb.append("<" + tok);
        
         // If there are any Inner-Tag Key-Value pairs, insert them first.
         if ((p != null) && (p.size() > 0))
             for (String key : p.stringPropertyNames())
             {
                 String value = p.getProperty(key);
        
                 InnerTagKeyException.check(key, value);
        
                 QuotesException.check(
                     value, quotesChoice,
                     "parameter 'Properties' contains:\nkey:\t" + key + "\nvalue:\t" + value + "\n"
                 );
        
                 sb.append(" " + key + '=' + computedQuote + value + computedQuote);
             }
        
         // If there are any Key-Only Inner-Tags (Boolean Attributes), insert them next.
         if (keyOnlyAttributes != null)
             for (String keyOnlyAttribute : keyOnlyAttributes) 
             {
                 InnerTagKeyException.check(keyOnlyAttribute);
                 sb.append(" " + keyOnlyAttribute);
             }
        
         // Add a closing forward-slash
         sb.append(addEndingForwardSlash ? " />" : ">");
        
         // Build the String, using the StringBuilder, and return the newly-constructed HTML Element.
         return sb.toString();
        
      • isTagNode

        public boolean isTagNode()
        This method identifies that 'this' instance of 'HTMLNode' is, indeed, actually an instance of the (sub-class) TagNode.
        Overrides:
        isTagNode in class HTMLNode
        Returns:
        This method shall always return TRUE It overrides the parent-class HTMLNode method isTagNode(), which always returns FALSE.
        See Also:
        isTagNode()
        Code:
        Exact Method Body:
        1
         return true;
        
      • isTag

        public boolean isTag​(java.lang.String... possibleTags)
        Receives a list of html-elements which the this.tok field must match. This method returns TRUE if any match is found.

        example
        Parameters:
        possibleTags - This non-null list of potential HTML tags.
        Returns:
        TRUE If this.tok matches at least one of these tags.
        See Also:
        tok
        Code:
        Exact Method Body:
        1
        2
        3
        4
        5
         for (String htmlTag : possibleTags)
             if (htmlTag.equalsIgnoreCase(this.tok))
                 return true;
                
         return false;
        
      • isTagExcept

        public boolean isTagExcept​(java.lang.String... possibleTags)
        Receives a list of html-elements which this.tok field MAY NOT match. This method returns FALSE if any match is found.
        Parameters:
        possibleTags - This must be a non-null list of potential HTML tags.
        Returns:
        FALSE If this.tok matches any one of these tags, and TRUE otherwise.
        See Also:
        tok, isTag(String[])
        Code:
        Exact Method Body:
        1
        2
        3
        4
        5
         for (String htmlTag : possibleTags)
             if (htmlTag.equalsIgnoreCase(this.tok))
                 return false;
                
         return true;
        
      • isTag

        public boolean isTag​(TC tagCriteria,
                             java.lang.String... possibleTags)
        Receives two "criteria-specifier" parameters. This method shall return TRUE if:

        • Field 'isClosing' is equal-to / consistent-with TC tagCriteria
        • Field 'tok' is equal to at least one of the 'possibleTags'


        example
        Parameters:
        tagCriteria - This ought to be either 'TC.OpeningTags' or TC.ClosingTags'. This parameter specifies what 'this' instance of TagNode is expected to contain, as this.isClosing field shall be compared against it.
        possibleTags - This is presumed to be a non-zero-length, and non-null-valued list of html tokens.
        Returns:
        TRUE If 'this' matches the specified criteria, and FALSE otherwise.
        See Also:
        TC, isClosing, tok
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
         // Requested an "OpeningTag" but this is a "ClosingTag"
         if ((tagCriteria == TC.OpeningTags) && this.isClosing)
             return false;
        
         // Requested a "ClosingTag" but this is an "OpeningTag"
         if ((tagCriteria == TC.ClosingTags) && ! this.isClosing)
             return false;
        
         for (int i=0; i < possibleTags.length; i++)
             if (this.tok.equalsIgnoreCase(possibleTags[i]))
                 return true;
                         // Found a TOKEN match, return TRUE immediately
        
         return false;   // None of the elements in 'possibleTags' equalled tn.tok
        
      • isTagExcept

        public boolean isTagExcept​(TC tagCriteria,
                                   java.lang.String... possibleTags)
        Receives a TagNode and then two "criteria-specifier" parameters. This method shall return FALSE if:

        • Field 'isClosing' is not equal-to / not consistent-with TC tagCriteria
        • Field 'tok' is equal-to any of the 'possibleTags'
        Parameters:
        tagCriteria - tagCriteria This ought to be either 'TC.OpeningTags' or TC.ClosingTags' This parameter specifies what 'this' instance of TagNode is expected to contain, as this.isClosing field shall be compared against it.
        possibleTags - This is presumed to be a non-zero-length, and non-null-valued list of html tokens.
        Returns:
        TRUE If this TagNode 'n' matches the specified criteria explained above, and FALSE otherwise.
        See Also:
        TC, tok, isClosing
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
         // Requested an "OpeningTag" but this is a "ClosingTag"
         if ((tagCriteria == TC.OpeningTags) && this.isClosing)
             return false;
        
         // Requested a "ClosingTag" but this is an "OpeningTag"
         if ((tagCriteria == TC.ClosingTags) && ! this.isClosing)
             return false;
        
         for (int i=0; i < possibleTags.length; i++)
             if (this.tok.equalsIgnoreCase(possibleTags[i]))
                 return false;
                         // The Token of the input node was a match with one of the 'possibleTags'
                         // Since this is "Except" - we must return 'false'
        
         return true;    // None of the elements in 'possibleTags' equalled tn.tok
                         // since this is "Except" - return 'true'
        
      • AV

        public java.lang.String AV​(java.lang.String innerTagAttribute)
        The letters: AV simply mean "Attribute Value". In this HTML scrape & search package, the words attribute and inner-tag are used synonymously.

        This will return the value of any "Inner Tag" inside the HTML TagNode. An inner-tag is the choice of wording used in this scrape package - partially for brevity since "tag" is usually interchangeable with "inner tag." Often HTML coders refer to this particular data as an HTML Element Attribute (or more simply, just "Attribute").

        example

        The following example will hilite what this method "AV(String attributeName)" can achieve. The output that is generated has been transcribed to the next window (after the one below).

        Example:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        import Torello.HTML.*;
        import Torello.HTML.NodeSearch.InnerTagFind;
        import Torello.Java.*;
        import Torello.Java.Shell.C;
        
        import java.util.*;
        import java.io.IOException;
        import java.net.URL;
        
        public class test
        {
            // This field prints its output both to the terminal, and to an internal string-buffer.
            static final StorageWriter sw = new StorageWriter();
        
            public static void main(String[] argv) throws IOException
            {
                // Load a page from a "reputable" news-source, and parse it into a vectorized-html page.
                Vector<HTMLNode>    page        = HTMLPage.getPageTokens(new URL("https://foxnews.com"), false);
        
                // Select all HTMLNodes that are "instanceof" TagNode, where the Element is "IMG" and, also,
                // only select images that have an "ALT" text-string inner-tag (a.k.a. english-description string).
                int[]               imgPosArr   = InnerTagFind.all(page, "img", "alt");
        
                // This uses the "Attribute Update Mode" (AUM) 'Replace' to convert the (very-long, very-unreadable)
                // variable Image-URL's to just say the word "REPLACED" when printing the node.
                // NOTE: They could easily be retrieved and saved, if needed.
                Attributes.update(page, AUM.Replace, imgPosArr, "data-src", "REMOVED", null);
                Attributes.update(page, AUM.Replace, imgPosArr, "data-srcset", "REMOVED", null);
        
                for (int pos : imgPosArr)
                {
                    // Retrieves an HTML '<IMG SRC=...>' TagNode
                    TagNode tn = (TagNode) page.elementAt(pos);
        
                    // Retrieves the 'IMG' ALT=... text
                    String altText = tn.AV("alt");
        
                    // Clean up some of the HTML 'escape' characters (for readability-printing purposes)
                    altText = Escape.replaceAll(altText).replace("&apos;","'");
        
                    // Print out the TagNode.  Use UNIX Color-Code "bright-yellow" (for readability), and the "ALT" attribute
                    sw.println(C.BYELLOW + tn + C.RESET + "\nALT: [" + C.BRED + altText + C.RESET + "]\n");
                }
        
                FileRW.writeFile(C.toHTML(sw.getString(), true, true), "alt-text.txt");
            }
        }
        

        The following text was generated by the above short program, scraping Yahoo! News. It prints out relevant CSS ID Information. Notice, perhaps, that almost 90% of the HTML Elements which have ID's are HTML 'divider' (div) Elements.

        UNIX or DOS Shell Command:
        <img src="//static.foxnews.com/static/orion/styles/img/fox-news/bg/fn-logo-watch-now.png" alt="Fox News"> ALT: [Fox News] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/b6a0e412-d473-4dab-b498-335e45d4c2e2/f65a1645-8c2d-4358-ae27-475863ff0098/1280x720/match/480/270/image.jpg" alt="Easter Sunday at Washington National Cathedral"> ALT: [Easter Sunday at Washington National Cathedral] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/957bd65c-090d-4a21-a3ce-c2a96ad810f3/fcb61255-7f3c-4011-b969-4baa7eb1a501/1280x720/match/480/270/image.jpg" alt="Easter Sunday at Saddleback Church with Rick Warren"> ALT: [Easter Sunday at Saddleback Church with Rick Warren] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/21388d3a-2474-45b0-a426-c6216579e628/6f4f9f8f-686e-41e2-859a-2b0173ec396c/1280x720/match/480/270/image.jpg" alt="Easter Sunday at First Baptist Dallas with Dr. Robert Jeffress"> ALT: [Easter Sunday at First Baptist Dallas with Dr. Robert Jeffress] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/d1edbbd6-4d73-4c15-914e-79c93a9f4292/73e97a51-0eff-4a5c-a5c6-3bdcf46db949/1280x720/match/480/270/image.jpg" alt="Easter Sunday Mass at St. Patrick's Cathedral"> ALT: [Easter Sunday Mass at St. Patrick's Cathedral] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/a34386a0-1571-47f2-af4c-3b939ae1933a/666dcd16-b5fd-49a3-89b6-98e8c738989e/1280x720/match/480/270/image.jpg" alt="Easter service at Lakewood Church with Joel Osteen"> ALT: [Easter service at Lakewood Church with Joel Osteen] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/a65bd207-e193-4dac-a22b-9befbb2277a1/dc0a4684-b98d-439f-9137-771fc7651420/1280x720/match/480/270/image.jpg" alt="Pope Francis gives Urbi et Orbi blessing"> ALT: [Pope Francis gives Urbi et Orbi blessing] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/11b72aee-0f98-4ab6-9a5f-ffbdab2eaab3/6338cc80-9038-47e8-b417-41252465ea38/1280x720/match/480/270/image.jpg" alt="Pope Francis presides over Easter Mass"> ALT: [Pope Francis presides over Easter Mass] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/53ee3ae5-c56d-4004-8fc4-deaed9a81f1d/ee9ac8f8-7c88-4d36-911f-4bb709846561/1280x720/match/480/270/image.jpg" alt="Pope Francis presides over Easter vigil ceremony"> ALT: [Pope Francis presides over Easter vigil ceremony] <img src="//a57.foxnews.com/hp.foxnews.com/images/2020/04/480/270/b0ac3b9b6689b080244fb89788ee7f0e.jpg" alt="President Trump tells Judge Jeanine that America will be back bigger, better and stronger than ever before"> ALT: [President Trump tells Judge Jeanine that America will be back bigger, better and stronger than ever before] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/97fc9be6-aaf0-4fe0-91d3-3a46f2b11443/f1955d76-1bc4-4b23-8161-74a2ab6f6bf5/1280x720/match/480/270/image.jpg" alt="Rudy Giuliani hits back at Democrats' pandemic politics"> ALT: [Rudy Giuliani hits back at Democrats' pandemic politics] <img src="//a57.foxnews.com/hp.foxnews.com/images/2020/04/1280/533/52d91505f5ca105624f439a86e062ced.jpg?tl=1&ve=1" alt="Country of coronavirus' origin blocks research on outbreak, deleted posts show"> ALT: [Country of coronavirus' origin blocks research on outbreak, deleted posts show] <img src="//a57.foxnews.com/static.foxnews.com/foxnews.com/content/uploads/2020/04/1024/576/AP20103813232544.jpg?tl=1&ve=1" alt="Tornadoes kill at least 6 in Mississippi, damage homes, buildings in Louisiana"/> ALT: [Tornadoes kill at least 6 in Mississippi, damage homes, buildings in Louisiana] <img src="//a57.foxnews.com/hp.foxnews.com/images/2020/04/1024/576/362782ebab386b42da12b464073f9395.jpg?tl=1&ve=1" alt="Dad, 20-year-old son laid to rest after losing coronavirus battles 3 days apart"/> ALT: [Dad, 20-year-old son laid to rest after losing coronavirus battles 3 days apart] <img src="//a57.foxnews.com/static.foxnews.com/foxnews.com/content/uploads/2020/04/1024/576/marylou-amer.jpg?tl=1&ve=1" alt="Detective died from coronavirus after twice being denied test, sister says"/> ALT: [Detective died from coronavirus after twice being denied test, sister says] <img src="//a57.foxnews.com/static.foxnews.com/foxnews.com/content/uploads/2020/04/1024/576/WisconsinFirefigther_1.jpg?tl=1&ve=1" alt="Check out how these firefighters paid tribute to health-care workers"/> ALT: [Check out how these firefighters paid tribute to health-care workers] <img src="//static.foxnews.com/static/orion/styles/img/fox-news/bg/fn-logo-watch-now.png" alt="Fox News"> ALT: [Fox News] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/b6a0e412-d473-4dab-b498-335e45d4c2e2/f65a1645-8c2d-4358-ae27-475863ff0098/1280x720/match/480/270/image.jpg" alt="Easter Sunday at Washington National Cathedral"> ALT: [Easter Sunday at Washington National Cathedral] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/957bd65c-090d-4a21-a3ce-c2a96ad810f3/fcb61255-7f3c-4011-b969-4baa7eb1a501/1280x720/match/480/270/image.jpg" alt="Easter Sunday at Saddleback Church with Rick Warren"> ALT: [Easter Sunday at Saddleback Church with Rick Warren] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/21388d3a-2474-45b0-a426-c6216579e628/6f4f9f8f-686e-41e2-859a-2b0173ec396c/1280x720/match/480/270/image.jpg" alt="Easter Sunday at First Baptist Dallas with Dr. Robert Jeffress"> ALT: [Easter Sunday at First Baptist Dallas with Dr. Robert Jeffress] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/d1edbbd6-4d73-4c15-914e-79c93a9f4292/73e97a51-0eff-4a5c-a5c6-3bdcf46db949/1280x720/match/480/270/image.jpg" alt="Easter Sunday Mass at St. Patrick's Cathedral"> ALT: [Easter Sunday Mass at St. Patrick's Cathedral] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/a34386a0-1571-47f2-af4c-3b939ae1933a/666dcd16-b5fd-49a3-89b6-98e8c738989e/1280x720/match/480/270/image.jpg" alt="Easter service at Lakewood Church with Joel Osteen"> ALT: [Easter service at Lakewood Church with Joel Osteen] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/a65bd207-e193-4dac-a22b-9befbb2277a1/dc0a4684-b98d-439f-9137-771fc7651420/1280x720/match/480/270/image.jpg" alt="Pope Francis gives Urbi et Orbi blessing"> ALT: [Pope Francis gives Urbi et Orbi blessing] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/11b72aee-0f98-4ab6-9a5f-ffbdab2eaab3/6338cc80-9038-47e8-b417-41252465ea38/1280x720/match/480/270/image.jpg" alt="Pope Francis presides over Easter Mass"> ALT: [Pope Francis presides over Easter Mass] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/53ee3ae5-c56d-4004-8fc4-deaed9a81f1d/ee9ac8f8-7c88-4d36-911f-4bb709846561/1280x720/match/480/270/image.jpg" alt="Pope Francis presides over Easter vigil ceremony"> ALT: [Pope Francis presides over Easter vigil ceremony] <img src="//a57.foxnews.com/hp.foxnews.com/images/2020/04/480/270/b0ac3b9b6689b080244fb89788ee7f0e.jpg" alt="President Trump tells Judge Jeanine that America will be back bigger, better and stronger than ever before"> ALT: [President Trump tells Judge Jeanine that America will be back bigger, better and stronger than ever before] <img src="//a57.foxnews.com/cf-images.us-east-1.prod.boltdns.net/v1/static/694940094001/97fc9be6-aaf0-4fe0-91d3-3a46f2b11443/f1955d76-1bc4-4b23-8161-74a2ab6f6bf5/1280x720/match/480/270/image.jpg" alt="Rudy Giuliani hits back at Democrats' pandemic politics"> ALT: [Rudy Giuliani hits back at Democrats' pandemic politics] <img data-src="REMOVED" alt="Pope Francis presides over Easter Mass" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Pope Francis presides over Easter Mass] <img data-src="REMOVED" alt="Oil surges after Saudi Arabia, Russia end price war with historic deal" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Oil surges after Saudi Arabia, Russia end price war with historic deal] <img data-src="REMOVED" alt="Broadway star in 'very critical condition' amid possible coronavirus fight" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Broadway star in 'very critical condition' amid possible coronavirus fight] <img data-src="REMOVED" alt="Rev. Franklin Graham's Easter Sunday message from NYC's Central Park" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Rev. Franklin Graham's Easter Sunday message from NYC's Central Park] <img data-src="REMOVED" alt="6 shot at California party that violated stay-at-home order, drew hundreds" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [6 shot at California party that violated stay-at-home order, drew hundreds] <img data-src="REMOVED" alt="JUSTIN HASKINS: AOC and friends peddling false cure for what ails US" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [JUSTIN HASKINS: AOC and friends peddling false cure for what ails US] <img data-src="REMOVED" alt="Cuban predicts new post-virus business models, talks of possible WH run" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Cuban predicts new post-virus business models, talks of possible WH run] <img data-src="REMOVED" alt="Police attacked with stones, iron bars while enforcing social distance measures" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Police attacked with stones, iron bars while enforcing social distance measures] <img data-src="REMOVED" alt="KFC mocks fans' homemade attempts to recreate fried chicken during coronavirus shutdown" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [KFC mocks fans' homemade attempts to recreate fried chicken during coronavirus shutdown] <img data-src="REMOVED" alt="Nurse confronts 'stupid' men defying coronavirus social distancing" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Nurse confronts 'stupid' men defying coronavirus social distancing] <img data-src="REMOVED" alt="What's coming to and leaving Hulu in April amid the coronavirus pandemic" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [What's coming to and leaving Hulu in April amid the coronavirus pandemic] <img data-src="REMOVED" alt="Nurses treating coronavirus patients find tires slashed" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Nurses treating coronavirus patients find tires slashed] <img data-src="REMOVED" alt="SEE IT: Woman, 93, makes coronavirus plea for more beer amid lockdown in viral photo" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [SEE IT: Woman, 93, makes coronavirus plea for more beer amid lockdown in viral photo] <img data-src="REMOVED" alt="'Tiger King': Carole, Howard Baskin say they feel 'betrayal' from filmmakers, are getting death threats" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: ['Tiger King': Carole, Howard Baskin say they feel 'betrayal' from filmmakers, are getting death threats] <img data-src="REMOVED" alt="Woman arrested for entering New Orleans airport naked amidst coronavirus lockdown" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Woman arrested for entering New Orleans airport naked amidst coronavirus lockdown] <img data-src="REMOVED" alt="Students in this country are protesting schools reopening" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Students in this country are protesting schools reopening] <img data-src="REMOVED" alt="‘Huge influx’ of COVID-19 patients putting strain on Moscow hospitals, Kremlin says" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [‘Huge influx’ of COVID-19 patients putting strain on Moscow hospitals, Kremlin says] <img data-src="REMOVED" alt="Olivia Munn on getting married someday: 'I never have ever been that girl'" src="//static.foxnews.com/static/orion/img/clear-16x9.gif" /> ALT: [Olivia Munn on getting married someday: 'I never have ever been that girl'] <img src="//a57.foxnews.com/hp.foxnews.com/images/2020/04/480/270/dd3f0280320f00471aa6e99ac2d14b8e.jpg" alt="Join Pete Hegseth as he focuses on one of the most important moments of the year for Christianity: Easter Sunday."> ALT: [Join Pete Hegseth as he focuses on one of the most important moments of the year for Christianity: Easter Sunday.] <img src="//a57.foxnews.com/hp.foxnews.com/images/2020/04/320/180/1c0802431b0b4b32b2cc72b49995772d.png" alt="America Together Category Page"> ALT: [America Together Category Page] <img src="//a57.foxnews.com/static.foxbusiness.com/foxbusiness.com/content/uploads/2020/04/480/270/Credible-no-cost-loan-thumbnail-1069166364.jpg" alt="How to refinance your mortgage without paying upfront closing costs"> ALT: [How to refinance your mortgage without paying upfront closing costs] <img data-src="REMOVED" alt="Paul Batura" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Paul Batura] <img data-src="REMOVED" alt="Scott Gunn" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Scott Gunn] <img data-src="REMOVED" alt="Jim Daly" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Jim Daly] <img src="//a57.foxnews.com/hp.foxnews.com/images/2017/10/128/128/63ba2426c69bdae0a03232c5b547f162.jpg" alt="Podcast Logo"> ALT: [Podcast Logo] <img data-src="REMOVED" alt="7 cool desserts to make with PEEPS" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [7 cool desserts to make with PEEPS] <img data-src="REMOVED" alt="Prankster costs pizzerias THOUSANDS" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Prankster costs pizzerias THOUSANDS] <img data-src="REMOVED" alt="Dolly's SPECIAL Easter message" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Dolly's SPECIAL Easter message] <img data-src="REMOVED" alt="'Tiger King' star getting DEATH threats" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: ['Tiger King' star getting DEATH threats] <img data-src="REMOVED" alt="Singer SLAMS Trump" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Singer SLAMS Trump] <img data-src="REMOVED" alt="Actor in 'VERY critical condition'" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Actor in 'VERY critical condition'] <img data-src="REMOVED" alt="Home tanning attempt goes WRONG" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Home tanning attempt goes WRONG] <img data-src="REMOVED" alt="Coronavirus ‘achilles heel’ FOUND?" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Coronavirus ‘achilles heel’ FOUND?] <img data-src="REMOVED" alt="Are 5G towers spreading COVID-19?" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Are 5G towers spreading COVID-19?] <img data-src="REMOVED" alt="10 tech spring cleaning tips for the quarantine era" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [10 tech spring cleaning tips for the quarantine era] <img data-src="REMOVED" alt="Can THIS slow coronavirus’ spread?" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Can THIS slow coronavirus’ spread?] <img data-src="REMOVED" alt="Iconic singer: 'Music doesn't mean ANYTHING'" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Iconic singer: 'Music doesn't mean ANYTHING'] <img data-src="REMOVED" alt="'Tiger King' star tells ALL" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: ['Tiger King' star tells ALL] <img data-src="REMOVED" alt="Hanks hosts SPECIAL show" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Hanks hosts SPECIAL show] <img data-src="REMOVED" alt="Best dramas to STREAM" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Best dramas to STREAM] <img data-src="REMOVED" alt="BEST idea for a quarantine birthday?" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [BEST idea for a quarantine birthday?] <img data-src="REMOVED" alt="WWI grenade found WHERE?" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [WWI grenade found WHERE?] <img data-src="REMOVED" alt="What to do for EASTER during coronavirus crisis" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [What to do for EASTER during coronavirus crisis] <img data-src="REMOVED" alt="Adopting RABBITS the new quarantine thing?" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [Adopting RABBITS the new quarantine thing?] <img data-src="REMOVED" alt="New CANNONBALL RUN record set?" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [New CANNONBALL RUN record set?] <img data-src="REMOVED" alt="America Together: YOUR inspiring pics" src="//global.fncstatic.com/static/orion/img/clear.gif" /> ALT: [America Together: YOUR inspiring pics] <img data-srcset="REMOVED" data-src="REMOVED" alt="Charlie Kirk fires back at Trump critics over economic shutdown: Time to stop 'crisis in America'" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Charlie Kirk fires back at Trump critics over economic shutdown: Time to stop 'crisis in America'] <img data-srcset="REMOVED" data-src="REMOVED" alt="Charlie Kirk fires back at Trump critics over economic shutdown: Time to stop 'crisis in America'" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Charlie Kirk fires back at Trump critics over economic shutdown: Time to stop 'crisis in America'] <img data-srcset="REMOVED" data-src="REMOVED" alt="Reporter's Notebook: How Congress may proceed in the 'After Coronavirus' era" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Reporter's Notebook: How Congress may proceed in the 'After Coronavirus' era] <img data-srcset="REMOVED" data-src="REMOVED" alt="Tornadoes raging across South kill at least 6 in Mississippi, damage hundreds of buildings in Louisiana" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Tornadoes raging across South kill at least 6 in Mississippi, damage hundreds of buildings in Louisiana] <img data-srcset="REMOVED" data-src="REMOVED" alt="Justin Haskins: Coronavirus and socialism – AOC and friends peddling false cure for what ails US" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Justin Haskins: Coronavirus and socialism – AOC and friends peddling false cure for what ails US] <img data-srcset="REMOVED" data-src="REMOVED" alt="Kathie Lee Gifford says her kids 'insisted' she self-isolate in Florida: ‘It’s quite beautiful’" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Kathie Lee Gifford says her kids 'insisted' she self-isolate in Florida: ‘It’s quite beautiful’] <img data-srcset="REMOVED" data-src="REMOVED" alt="Matthew McConaughey, wife Camila donate 80K face masks to coronavirus first-responders in Texas, Louisiana" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Matthew McConaughey, wife Camila donate 80K face masks to coronavirus first-responders in Texas, Louisiana] <img data-srcset="REMOVED" data-src="REMOVED" alt="Gov. DeSantis on protecting senior citizens, expanding coronavirus testing and supporting health care workers" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Gov. DeSantis on protecting senior citizens, expanding coronavirus testing and supporting health care workers] <img data-srcset="REMOVED" data-src="REMOVED" alt="First coronavirus relief checks deposited to Americans: IRS" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [First coronavirus relief checks deposited to Americans: IRS] <img data-srcset="REMOVED" data-src="REMOVED" alt="FBI exposes coronavirus scam after 39M masks promised from overseas fail to reach California hospitals" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [FBI exposes coronavirus scam after 39M masks promised from overseas fail to reach California hospitals] <img data-srcset="REMOVED" data-src="REMOVED" alt="Mustang crashes off California cliff during coronavirus lockdown, gets pulled out by 'Hulk' wrecker" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Mustang crashes off California cliff during coronavirus lockdown, gets pulled out by 'Hulk' wrecker] <img data-srcset="REMOVED" data-src="REMOVED" alt="Trump administration plans to open 2.3 million acres of wildlife refuges to hunting and fishing" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Trump administration plans to open 2.3 million acres of wildlife refuges to hunting and fishing] <img data-srcset="REMOVED" data-src="REMOVED" alt="Many restaurants weather coronavirus storm by converting to grocery stores" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Many restaurants weather coronavirus storm by converting to grocery stores] <img data-srcset="REMOVED" data-src="REMOVED" alt="Coronavirus: How to celebrate a birthday during the pandemic" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Coronavirus: How to celebrate a birthday during the pandemic] <img data-srcset="REMOVED" data-src="REMOVED" alt="Woman accidentally tans company logo onto her leg during coronavirus lockdown" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Woman accidentally tans company logo onto her leg during coronavirus lockdown] <img data-srcset="REMOVED" data-src="REMOVED" alt="Petition calling for WHO boss Tedros to resign nears 1M signatures" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Petition calling for WHO boss Tedros to resign nears 1M signatures] <img data-srcset="REMOVED" data-src="REMOVED" alt="REPAYE could lower your student loan payments — here's how" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [REPAYE could lower your student loan payments — here's how] <img data-srcset="REMOVED" data-src="REMOVED" alt="Matthew McConaughey, wife Camila donate 80K face masks to coronavirus first-responders in Texas, Louisiana" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Matthew McConaughey, wife Camila donate 80K face masks to coronavirus first-responders in Texas, Louisiana] <img data-srcset="REMOVED" data-src="REMOVED" alt="Fox News Sunday - Sunday, April 12" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Fox News Sunday - Sunday, April 12] <img data-srcset="REMOVED" data-src="REMOVED" alt="Dolly Parton shares special Easter message: It's 'a little different this year' with coronavirus" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: [Dolly Parton shares special Easter message: It's 'a little different this year' with coronavirus] <img data-srcset="REMOVED" data-src="REMOVED" alt="'Tiger King': Carole, Howard Baskin say they feel 'betrayal' from filmmakers, are getting death threats" src="//global.fncstatic.com/static/orion/img/clear.gif"> ALT: ['Tiger King': Carole, Howard Baskin say they feel 'betrayal' from filmmakers, are getting death threats]
        Parameters:
        innerTagAttribute - This may be any Java-String, but very common examples of HTML attributes (and their values) include:

        Attribute / Inner-Tag Commonly Found Attribute-Values
        HREF="..." where the attribute value ("...") - is a URL
        SRC='...' and the attribute value specified ('...') - is usually an Image-URL (like a "pic.jpg")
        ID=... where the attribute value (...) - would be a "CSS Identifier Tag"
        CLASS='...' and the attribute value ('...') - is the "CSS Class" to which the particular HTML element belongs
        OnClick="..." and the attribute value ("...") - is often a function call to a Java-Script module, or actual Java-Script
        href="..." SAME AS ABOVE! - Remember an "inner-tag" or "attribute" name is CASE-INSENSITIVE
        src='...' SAME AS ABOVE! - Remember an "inner-tag" or "attribute" name is CASE-INSENSITIVE
        Returns:
        The "Attribute Value", which for the inner-tag named by the input String-parameter.

        NOTE: If 'this' TagNode is a closing-tag (specifically, if the 'isClosing' boolean-field is true), this method will exit immediately, and return null. Unlike the other Attribute-Modification Methods in this class, no ClosingTagNodeException shall throw, but rather the method will exit gracefully. This is because this method is a 'getter' only. No invalid data will be instantiated or saved - even if this method executes to completion. Note, though, valid HTML pages do not allow attributes inside of closing HTML Elements.

        ALSO: If the 'str' String-field of 'this' TagNode has a length that isn't greater than the following: 3 + tok.length() + innerTagAttribute.trim().length()), then in this case this AV method will also return null. The rational for returning null here is that the final String str field simply does not have enough characters to contain this inner-tag.
        See Also:
        isClosing, HTMLNode.str, tok, StringParse.ifQuotesStripQuotes(String), TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
         // All HTML element tags that start like: </DIV> with a front-slash.
         // They may not legally contain inner-tag attributes.
        
         if (this.isClosing) return null;    
        
         // All HTML element tags that contain only <TOK> (TOK <==> Element-Name) in their .str field...
         // Specifically: '<', TOKEN, '>',  (Where TOKEN is 'div', 'span', 'table', 'ul', etc...)
         // are TOO SHORT to have the attribute, so don't check... return null.
        
         if (this.str.length() < 
             (3 + this.tok.length() + (innerTagAttribute = innerTagAttribute.trim()).length()))
             return null;
        
         // Matches "Attribute / Inner-Tag Key-Value" Pairs.
         Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // This loop iterates the KEY_VALUE PAIRS THAT HAVE BEEN FOUND.
         /// NOTE: The REGEX Matches on Key-Value Pairs.
         while (m.find())
        
             // m.group(2) is the "KEY" of the Attribute KEY-VALUE Pair
             // m.group(3) is the "VALUE" of the Attribute.
             if (m.group(2).equalsIgnoreCase(innerTagAttribute))
                 return StringParse.ifQuotesStripQuotes(m.group(3));
        
         // This means the attribute name provided to parameter 'innerTagAttribute' was not found.
         return null;
        
      • AVOPT

        public java.lang.String AVOPT​(java.lang.String innerTagAttribute)
        OPT: Optimized

        This is an "optimized" version of method AV(String). This method does the exact same thing as AV(...), but leaves out parameter-checking and error-checking. This is used internally (repeatedly) by the NodeSearch Package Search Loops.
        Parameters:
        innerTagAttribute - This is the inner-tag / attribute name whose value is hereby being requested.
        Returns:
        String-value of this inner-tag / attribute.
        See Also:
        StringParse.ifQuotesStripQuotes(String), HTMLNode.str, TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
         // COPIED DIRECTLY FROM class TagNode, leaves off initial tests.
        
         // Matches "Attribute / Inner-Tag Key-Value" Pairs.
         Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // This loop iterates the KEY_VALUE PAIRS THAT HAVE BEEN FOUND.
         /// NOTE: The REGEX Matches on Key-Value Pairs.
         while (m.find())
        
             // m.group(2) is the "KEY" of the Attribute KEY-VALUE Pair
             // m.group(3) is the "VALUE" of the Attribute.
             if (m.group(2).equalsIgnoreCase(innerTagAttribute))
                 return StringParse.ifQuotesStripQuotes(m.group(3));
        
         // This means the attribute name provided to parameter 'innerTagAttribute' was not found.
         return null;
        
      • setAV

        public TagNode setAV​(java.lang.String attribute,
                             java.lang.String value,
                             SD quote)
        This function will instantiate a new TagNode which contains this newly added attribute-value pair. It uses the constructor listed above, and furthermore does some error-handling checks. It will throw an exception if the inner-tag / value pairs do not pass inspection on quotes-error cases, or contain invalid characters.

        example
        Parameters:
        attribute - Any valid HTML attribute-name. This parameter may not be null, or a NullPointerException will throw.

        NOTE: If the attribute that is specified is already contained within this tag (where a CASE-INSENSITIVE comparison to the inner-tag's returned by public Properties allAV() gets a match), then the original attribute is simply over-written. A Duplicate HTML-Element attribute will not be added.
        value - Any valid attribute-value. This parameter may not be null, or a NullPointerException will throw.
        quote - This is either a single-quote, double-quote, or null.

        • When parameter 'quote' is SD.SingleQuotes, a single-quote is prepended and appended to the beginning and ending (respectively) of the value parameter before inserting or replacing the inner-tag of this HTML (TagNode) Element.
        • When parameter 'quote' is SD.DoubleQuotes, a double-quote is added to the beginning and ending of the value-parameter before inserting (or re-inserting, if this attribute as already present).
        • When 'quote' is null, there are two alternative results, depending on the TagNode:

          1. If the TagNode already has an inner-tag name that is equal (CASE_INSENSITIVE) to the 'key' parameter, then the original quote found in 'this' Element is used.
          2. If a new attribute, not already found in 'this' TagNode is being inserted, and parameter 'quote' is null, then no quotes will be used at all - which is a scenario sometimes found in HTML documents. In this case, the key-value inner-tag will simply contain the String <HTML-ELEMENT ... key=value ...> without any quotes present, at all.
        Returns:
        An HTML TagNode instance with updated attribute information.

        IMPORTANT: Because TagNode's are immutable (since they are just wrapped-java-Strings, which are also immutable), it is important to remember that this method does not change the contents of a TagNode, but rather returns an entirely new TagNode instance as a result instead.
        Throws:
        InnerTagKeyException - This exception will throw if a non-standard String-value is passed to parameter String 'attribute'. HTML expects that an attribute-name conform to a set of rules in order to be processed by a browser.
        QuotesException - If there are "quotes within quotes" problems when invoking the TagNode constructor, this exception will throw. The problem occurs when one or more of the attribute key-value pairs have a quotation-choice such that the chosen quotation-mark is also found within the attribute-value.

        QuotesException will also throw in the case that an attribute key-value pair has elected to use the "No Quotes" option, but the attribute-value contains white-space.
        HTMLTokException - If an invalid HTML 4 or 5 token is not present CASE_INSENSITIVE
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        See Also:
        ClosingTagNodeException.check(TagNode), generateElementString(String, Properties, Iterable, SD, boolean), setAV(Properties, SD), tok, HTMLNode.str, isClosing
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
         ClosingTagNodeException.check(this);
        
         if (attribute == null) throw new NullPointerException(
             "You have passed 'null' to the 'attribute' (attribute-name) String-parameter, " +
             "but this is not allowed here."
         );
        
         if (value == null) throw new NullPointerException(
             "You have passed 'null' to the 'attribute' (attribute-value) String-parameter, " +
             "but this is not allowed here."
         );
        
         // Retrieve all "Key-Only" (Boolean) Attributes from 'this' (the original) TagNode
         // Use Java Streams to filter out any that match the newly-added attribute key-value pair.
         // SAVE: Save the updated / shortened list to a List<String>
        
         List<String> prunedOriginalKeyOnlyAttributes = allKeyOnlyAttributes(true)
             .filter((String originalKeyOnlyAttribute) -> 
                 ! originalKeyOnlyAttribute.equalsIgnoreCase(attribute))
             .collect(Collectors.toList());
        
         // Retrieve all Inner-Tag Key-Value Pairs.  Preserve the Case of the Attributes.  Preserve
         // the Quotation-Marks.
        
         Properties  p                       = allAV(true, true);
         String      originalValueWithQuotes = null;
         String      computedQuote           = null;
        
         // NOTE, there should only be ONE instance of an attribute in an HTML element, but malformed
         // HTML happens all the time, so to keep this method safe, it checks (and removes) the entire
         // attribute-list for matches - not just the first found instance.
        
         for (String key : p.stringPropertyNames())
             if (key.equalsIgnoreCase(attribute))
             {
                 Object temp = p.remove(key);
                 if (temp instanceof String) originalValueWithQuotes = (String) temp;
             }
        
         // If the user does not wish to "change" the original quote choice, then find out what
         // the original-quote choice was...
        
         if (    (quote == null) 
             &&  (originalValueWithQuotes != null)
             &&  (originalValueWithQuotes.length() >= 2)
         )
         {
             char s = originalValueWithQuotes.charAt(0);
             char e = originalValueWithQuotes.charAt(originalValueWithQuotes.length() - 1);
             if ((s == e) && (s == '\''))        computedQuote = "" + SD.SingleQuotes.quote;
             else if ((s == e) && (s == '"'))    computedQuote = "" + SD.DoubleQuotes.quote;
             else                                computedQuote = "";
         }
         else if (quote == null)                 computedQuote = "";
         else                                    computedQuote = "" + quote.quote;
        
         p.put(attribute, computedQuote + value + computedQuote);
        
         return new TagNode(generateElementString(
             // Rather than using '.tok' here, preserve the case of the original HTML Element
             this.str.substring(1, 1 + tok.length()), p,
             prunedOriginalKeyOnlyAttributes, null /* SD */, this.str.endsWith("/>")
         ));
        
      • setAV

        public TagNode setAV​(java.util.Properties attributes,
                             SD defaultQuote)
        This allows for inserting or updating multiple TagNode inner-tag key-value pairs with a single method invocation.
        Parameters:
        attributes - These are the new attribute key-value pairs to be inserted.
        defaultQuote - This is the default quotation mark to use, if the 'attribute' themselves do not already have quotations.

        IMPORTANT: If this value is used, then none of the provided Property-values of the input java.lang.Properties instance should have quotes already. Each of these new-values will be wrapped in the quote that is provided as the value to this parameter.

        HOWEVER: If this parameter is passed a value of 'null', then no quotes will be added to the new keys - unless the attribute being inserted is replacing a previous attribute that was already present in the element. In this case, the original quotation shall be used. If this parameter receives 'null' and any of the new Properties were not already present in the original ('this') element, then no quotation marks will be used, which may throw a QuotesException if the attribute value contains any white-space.
        Returns:
        An HTML TagNode instance with updated TagNode information.

        IMPORTANT: Because TagNode's are immutable (since they are just wrapped-java-String's, which are also immutable), it is important to remember that this method does not change the contents of a TagNode, but rather returns an entirely new TagNode as a result instead.
        Throws:
        InnerTagKeyException - This exception will throw if any of the keys in the Properties parameter contain non-standard HTML Attribute / Inner-Tag key Strings. HTML expects that attribute-names conform to a set of rules in order to be processed properly by a browser. There is a 'check(...)' in class InnerTagKeyException which delineates the exact requirements made by HTML for attribute-names.
        QuotesException - if there are "quotes within quotes" problems, due to the values of the key-value pairs.
        HTMLTokException - if an invalid HTML 4 or 5 token is not present (CASE_INSENSITIVE)
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        See Also:
        ClosingTagNodeException.check(TagNode), setAV(String, String, SD), allKeyOnlyAttributes(boolean), tok, HTMLNode.str, isClosing
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79
        80
        81
        82
        83
        84
        85
         ClosingTagNodeException.check(this);
        
         // Check that this attributes has elements.
         if (attributes.size() == 0) throw new IllegalArgumentException(
             "You have passed an empty java.util.Properties instance to the " +
             "setAV(Properties, SD) method"
         );
        
         // Retrieve all Inner-Tag Key-Value Pairs.
         //      Preserve: the Case of the Attributes.
         //      Preserve: the Quotation-Marks.
         Properties originalAttributes = allAV(true, true);
        
         // Retrieve all "Key-Only" (Boolean) attributes from the new / update attribute-list
         Set<String> newAttributeKeys = attributes.stringPropertyNames();
        
         // Retrieve all "Key-Only" (Boolean) Attributes from 'this' (the original) TagNode
         // Use Java Streams to filter out all the ones that need to be clobbered by-virtue-of
         // the fact that they are present in the new / parameter-updated attribute key-value list.
         // SAVE: Save the updated / shortened list to a List<String>
        
         List<String> prunedOriginalKeyOnlyAttributes = allKeyOnlyAttributes(true)
             .filter((String originalKeyOnlyAttribute) ->
             {
                 // Returns false when the original key-only attribute matches one of the
                 // new attributes being inserted.  Notice that a case-insensitive comparison
                 // must be performed - to preserve case.
                 for (String newKey : newAttributeKeys) 
                     if (newKey.equalsIgnoreCase(originalKeyOnlyAttribute)) 
                         return false;
        
                 return true;
             })
             .collect(Collectors.toList());
        
         // NOTE: There is no need to check the validity of the new attributes.  The TagNode
         //       constructor that is invoked on the last line of this method will do a 
         //       validity-check on the attribute key-names provided to the 'attributes' 
         //       java.util.Properties instance passed to to this method.
        
         for (String newKey : newAttributeKeys)
         {
             String      originalValueWithQuotes = null;
             String      computedQuote           = null;
        
             // NOTE, there should only be ONE instance of an attribute in an HTML element, but
             // malformed HTML happens all the time, so to keep this method safe, it checks (and
             // removes) the entire attribute-list for matches - not just the first found instance.
        
             for (String originalKey : originalAttributes.stringPropertyNames())
                 if (originalKey.equalsIgnoreCase(newKey))
                 {
                     // Remove the original key-value inner-tag pair.
                     Object temp = originalAttributes.remove(originalKey);
                     if (temp instanceof String) originalValueWithQuotes = (String) temp;
                 }
        
             // If the user does not wish to "change" the original quote choice, then find out what
             // the original-quote choice was...
        
             if (    (defaultQuote == null) 
                 &&  (originalValueWithQuotes != null)
                 &&  (originalValueWithQuotes.length() >= 2)
             )
             {
                 char s = originalValueWithQuotes.charAt(0);
                 char e = originalValueWithQuotes.charAt(originalValueWithQuotes.length() - 1);
                 if ((s == e) && (s == '\''))        computedQuote = "" + SD.SingleQuotes.quote;
                 else if ((s == e) && (s == '"'))    computedQuote = "" + SD.DoubleQuotes.quote;
                 else                                computedQuote = "";
             }
             else if (defaultQuote == null)          computedQuote = "";
             else                                    computedQuote = "" + defaultQuote.quote;
        
             // Insert the newly, updated key-value inner-tag pair.  This 'Properties' will be
             // used to construct a new TagNode.
             originalAttributes.put(newKey, computedQuote + attributes.get(newKey) + computedQuote);
         }
        
         return new TagNode(generateElementString(
             // Rather than using '.tok' here, preserve the case of the original HTML Element
             this.str.substring(1, 1 + tok.length()),
             originalAttributes, prunedOriginalKeyOnlyAttributes, null /* SD */,
             this.str.endsWith("/>")
         ));
        
      • appendToAV

        public TagNode appendToAV​(java.lang.String attribute,
                                  java.lang.String appendStr,
                                  boolean startOrEnd,
                                  SD quote)
        This will append a substring to the attribute value of an HTML TagNode. This method can be very useful, for instance when dealing with CSS tags that are inserted inside the HTML node itself. For instance, in order to add a 'color: red; background: white;' portion to the CSS 'style' tag of an HTML <TABLE STYLE="..."> element, without clobbering the style-information that is already inside the element, then this method will achieve such a result.
        Parameters:
        attribute - The name of the attribute to which the value must be appended. This parameter may not be null, or a NullPointerException will throw.
        appendStr - The String to be appended to the attribute-value.
        startOrEnd - If this parameter is TRUE then the append-String will be inserted at the beginning (before) whatever the current attribute- value is. If this parameter is FALSE then the append-String will be inserted at the end (after) the current attribute-value String.

        NOTE: If tag element currently does not posses this attribute, then the attribute/value pair will be created and inserted with its value set to the value of 'appendStr'.
        quote - This is the quote that will be used when defining the attribute's key-value pair. This parameter allows for SD.Single, SD.Double or 'null'. This parameter is provided to help ensure that improper quotations do not occur when modifying HTML pages. If 'null' is passed to this parameter, then the choice for quote will follow the rules found in method setAV(String, String, SD).
        Returns:
        Since all instances of TagNode are immutable, this method will not actually alter the TagNode element, but rather create a new object reference that contains the updated attribute.
        Throws:
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        QuotesException - The rules of the setAV(String, String, SD) method for quotation marks apply here as well. See that method's requirements and definitions to understand how this exception could possibly be thrown.
        See Also:
        AV(String), setAV(String, String, SD), ClosingTagNodeException.check(TagNode)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
         ClosingTagNodeException.check(this);
        
         if (attribute == null) throw new NullPointerException(
             "You have passed 'null' to the 'attribute' (attribute-name) String-parameter, " +
             "but this is not allowed here."
         );
        
         if (appendStr == null) throw new NullPointerException(
             "You have passed 'null' to the 'appendStr' (attribute-value-append-string) " +
             "String-parameter, but this is not allowed here."
         );
        
         String curVal = AV(attribute);
         if (curVal == null) curVal = "";
        
         // This decides whether to insert the "appendStr" before the current value-string,
         // or afterwards.  This is based on the passed boolean-parameter 'startOrEnd'
         curVal = startOrEnd ? (appendStr + curVal) : (curVal + appendStr);
        
         // Reuse the 'setAV(String, String, SD)' method already defined in this class.
         return setAV(attribute, curVal, quote);
        
      • removeAttributes

        public TagNode removeAttributes​(java.lang.String... attributes)
        This will remove all inner-tag's whose names match (using CASE-INSENSITIVE comparisons) the specified attribute-names in this input parameter list 'attributes'.

        NOTE: This will remove all inner-tags that match the listed attributes provided. This means removing BOTH boolean 'key-only' attributes, AND any key-value inner-tags that have names which match the requested remove-list of names.
        Parameters:
        attributes - This is a list of attribute-names (inner-tags) to be removed from 'this' instance of TagNode. Each String in this var-args String... parameter will have String.toLowerCase() invoked before performing these attribute-name comparisons.

        NOTE: If 'this' instance of TagNode node does not contain any of these attributes, then nothing shall happen; however, a new TagNode instance shall still be constructed and returned.
        Returns:
        An HTML TagNode instance with updated TagNode information.

        IMPORTANT: Because TagNode's are immutable (since they are just wrapped-java-String's, which are also immutable), it is important to remember that this method does not change the contents of a TagNode, but rather returns an entirely new TagNode as a result instead.
        Throws:
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        See Also:
        ClosingTagNodeException.check(TagNode), tok, isClosing, HTMLNode.str, TagNode(String), generateElementString(String, Properties, Iterable, SD, boolean)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
         ClosingTagNodeException.check(this);
        
         // Retrieve all Inner-Tag Key-Value Pairs.  Preserve the Case of the Attributes.  Preserve
         // the Quotation-Marks.
         Properties originalAttributes = allAV(true, true);
        
         // Remove any attributes from the "Attributes Key-Value Properties Instance" which MATCH the
         // attribute names that have been EXPLICITLY REQUESTED FOR REMOVAL
         for (String key : originalAttributes.stringPropertyNames())
             for (String attribute : attributes)
                 if (key.equalsIgnoreCase(attribute))
                     originalAttributes.remove(key);
        
         // Retrieve all "Boolean Attributes" (key-no-value).  Preserve the Case of these Attributes.
         // Retain only the attributes in the 'filteredKeyOnlyAttributes' String-Array which have
         // PASSED THE FILTER OPERATION.  The filter operation only returns TRUE if the 
         // requested-attribute-list does not contain a copy of the Key-Only-Attribute
         // NOTE: 'true' is passed as input to the 'allKeyOnlyAttributes(boolean)' method to request
         //       that CASE be PRESERVED.
         Iterable<String> prunedKeyOnlyAttributes = allKeyOnlyAttributes(true)
             .filter((String attribute) ->
             {
                 // Returns false when the original key-only attribute matches one of the attributes
                 // that was requested to to be removed.  Notice that a case-insensitive comparison 
                 // must be performed.
                 for (String removeAttributes : attributes)
                     if (removeAttributes.equalsIgnoreCase(attribute))
                         return false;
        
                 return true;
             })
             .collect(Collectors.toList());
        
         return new TagNode(generateElementString(
             // Rather than using '.tok' here, preserve the case of the original HTML Element
             this.str.substring(1, 1 + tok.length()),
             originalAttributes, prunedKeyOnlyAttributes, /* SD */ null, 
             this.str.endsWith("/>")
         ));
        
      • removeAllAV

        public TagNode removeAllAV()
        TagNode's are immutable. And because of this, calling removeAllAV() is actually the same as retrieving the standard, zero-attribute, pre-instantiated instance of an HTML Element. Pre-instantiated factory-instances of class TagNode for every HTML-Element are stored by class HTMLTags inside a Hashtable. They can be retrieved in multiple ways, two of which are found in methods in this class.

        Point of Interest: Calling these three different methods will all return identical Object references:

        • TagNode v1 = myTagNode.removeAllAV();
        • TagNode v2 = TagNode.getInstance(myTagToken, openOrClosed);
        • TagNode v3 = HTMLTag.hasTag(myTagToken, openOrClosed);
        • assert((v1 == v2) && (v2 == v3));


        example
        Returns:
        An HTML TagNode instance with all inner attributes removed.

        NOTE: If this tag contains an "ending forward slash" that ending slash will not be included in the output TagNode.

        IMPORTANT: Because TagNode's are immutable (since they are just wrapped-java-String's, which are also immutable), it is important to remember that this method does not change the contents of a TagNode, but rather returns an entirely new TagNode as a result instead.
        Throws:
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        See Also:
        ClosingTagNodeException.check(TagNode), getInstance(String, TC), HTMLNode.str, tok, TC.OpeningTags
        Code:
        Exact Method Body:
        1
        2
        3
        4
        5
        6
        7
        8
        9
         ClosingTagNodeException.check(this);
        
         // NOTE: We *CANNOT* use the 'tok' field to instantiate the TagNode here, because the 'tok'
         // String-field is *ALWAYS* guaranteed to be in a lower-case format.  The 'str'
         // String-field, however uses the original case that was found on the HTML Document by the
         // parser (or in the Constructor-Parameters that were passed to construct 'this' instance
         // of TagNode.
        
         return getInstance(this.str.substring(1, 1 + tok.length()), TC.OpeningTags);
        
      • allAV

        public java.util.Properties allAV()
        Convenience Method. Invokes allAV(boolean, boolean), attribute-names will be in lower-case.
        Code:
        Exact Method Body:
        1
         return allAV(false, false);
        
      • allAV

        public java.util.Properties allAV​(boolean keepQuotes,
                                          boolean preserveKeysCase)
        This will copy every attribute key-value pair inside 'this' HTML TagNode element into a java.util.Properties Hash-Table.

        RETURN-VALUE NOTE: This method shall not return any "Key-Only Attributes" (a.k.a. "Boolean Attributes"). The most commonly used "Boolean Attribute" example is the 'HIDDEN' key-word that is used to prevent the browser from displaying an HTML Element. Inner-tags that represent attribute key-value pairs are the only attributes that may be included in the returned 'Properties' instance.

        example
        Parameters:
        keepQuotes - If this parameter is passed TRUE, then any surrounding quotation marks will be included for each the values of each attribute key-value pair.
        preserveKeysCase - If this parameter is passed TRUE, then the method String.toLowerCase() will not be invoked on any of the keys (attribute-names) of each inner-tag key-value pair.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        Returns:
        This returns a list of each and every attribute-name - and the associate value of the attribute - found in 'this' TagNode. An instance of class java.util.Properties is used to store the attribute key-value pairs.

        NOTE: This method will NOT return any boolean, key-only attributes present in 'this' TagNode.

        ALSO: This method shall not return 'null'. If there do not exist any Attribute-Value Pairs, or if 'this' node is a closing-element, then an empty 'Properties' instance shall be returned.
        See Also:
        StringParse.ifQuotesStripQuotes(String), TagNode.AttrRegEx.KEY_VALUE_REGEX, tok, HTMLNode.str
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
         Properties ret = new Properties();
        
         // NOTE:    OPTIMIZED, "closing-versions" of the TagNode, and TagNode's whose 'str' field is
         //          only longer than the token, itself, by 3 or less characters cannot have attributes.
         // CHARS:   '<', TOKEN, SPACE, '>'
         // RET:     In that case, just return an empty 'Properties' instance.
         if (isClosing || (str.length() <= (tok.length() + 3))) return ret;
        
         // This RegEx Matcher 'matches' against Attribute/InnerTag Key-Value Pairs.
         // m.group(1): UN-USED!  (Includes Key, Equals-Sign, and Value).  Leaves-off leading white-space.
         // m.group(2): returns the 'key' portion of the key-value pair, before an '=' (equals-sign).
         // m.group(3): returns the 'value' portion of the key-value pair, after an '='
         Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // MORE-CODE, but MORE-EFFICIENT (slightly)  It looks neat to boot.
         if      (keepQuotes     && preserveKeysCase)    while (m.find()) ret.put(m.group(2), m.group(3));
         else if (!keepQuotes    && preserveKeysCase)    while (m.find()) ret.put(m.group(2), StringParse.ifQuotesStripQuotes(m.group(3)));
         else if (keepQuotes     && !preserveKeysCase)   while (m.find()) ret.put(m.group(2).toLowerCase(), m.group(3));
         else if (!keepQuotes    && !preserveKeysCase)   while (m.find()) ret.put(m.group(2).toLowerCase(), StringParse.ifQuotesStripQuotes(m.group(3)));
        
         return ret;
        
      • allAN

        public java.util.stream.Stream<java.lang.String> allAN()
        Convenience Method. Invokes allAN(boolean, boolean).

        Attribute-names will be in lower-case.
        Code:
        Exact Method Body:
        1
         return allAN(false, false);
        
      • allAN

        public java.util.stream.Stream<java.lang.String> allAN​
                    (boolean preserveKeysCase,
                     boolean includeKeyOnlyAttributes)
        
        This method will only return a list of attribute-names. The attribute-values shall NOT be included in the result. The String's returned can have their "case-preserved" by passing TRUE to the input boolean parameter 'preserveCase'.
        Parameters:
        preserveKeysCase - If this is parameter receives TRUE then the case of the attribute-names shall be preserved.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        includeKeyOnlyAttributes - When this parameter receives TRUE, then any "Boolean Attributes" or "Key-Only, No-Value-Assignment" Inner-Tags will ALSO be included in the Stream<String> returned by this method.
        Returns:
        an instance of Stream<String> containing all attribute-names identified in 'this' instance of TagNode. A java.util.stream.Stream is used because it's contents can easily be converted to just about any data-type.

        Conversion-Target Stream-Method Invocation
        String[] Stream.toArray(String[]::new);
        List<String> Stream.collect(Collectors.toList());
        Vector<String> Stream.collect(Collectors.toCollection(Vector::new));
        TreeSet<String> Stream.collect(Collectors.toCollection(TreeSet::new));
        Iterator<String> Stream.iterator();

        NOTE: This method shall never return 'null' - even if there are no attribute key-value pairs contained by 'this' TagNode. If there are strictly zero attributes, an empty Stream shall be returned, instead.
        See Also:
        allKeyOnlyAttributes(boolean), allAN()
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
         // If there is NO ROOM in the "str" field for attributes, then there is now way attributes
         // could exist in this element.  Return "empty" immediately.
         // 
         // NOTE:    OPTIMIZED, "closing-versions" of the TagNode, and TagNode's whose 'str' field
         //          is only longer than the token, itself, by 3 or less characters cannot have
         //          attributes.
         // CHARS:   '<', TOKEN, SPACE, '>'
         // RET:     In that case, just return an empty Stream.
         if (isClosing || (str.length() <= (tok.length() + 3))) return Stream.empty();
        
         // Use Java Streams.  A String-Stream is easily converted to just about any data-type
         Stream.Builder<String> b = Stream.builder();
        
         // This RegEx Matcher 'matches' against Attribute/InnerTag Key-Value Pairs.
         // m.group(2): returns the 'key' portion of the key-value pair, before an '=' (equals-sign).
         Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // Retrieve all of the keys of the attribute key-value pairs.
         while (m.find()) b.add(m.group(2));
        
         // This Stream contains only keys that were once key-value pairs, if there are "key-only" 
         // attributes, they have not been added yet.
         Stream<String> ret = b.build();
        
         // Convert these to lower-case, (if requested)
         if (! preserveKeysCase) ret = ret.map((String attribute) -> attribute.toLowerCase());
        
         // Now, add in all the "Key-Only" attributes (if there are any).  Note, "preserve-case"
         // and "to lower case" are handled, already, in method "allKeyOnlyAttributes(boolean)"
         if (includeKeyOnlyAttributes)
             return Stream.concat(ret, allKeyOnlyAttributes(preserveKeysCase));
        
         return ret;
        
      • allKeyOnlyAttributes

        public java.util.stream.Stream<java.lang.String> allKeyOnlyAttributes​
                    (boolean preserveKeysCase)
        
        This method returns a Stream<String> of all token-String's that are found between attribute key-value pairs in 'this' TagNode instance. These attribute-names may not have any values assignments, or they will be considered INELLIGIBLE for being included in the return result set from this method.

        example
        Parameters:
        preserveKeysCase - If this parameter is passed TRUE, then the method String.toLowerCase() will not be invoked on any of the keys (attribute-names) returned.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        Returns:
        a java Stream<String> that contains any and all character text that resides between attribute key-value pairs that have matched. Generally, in well formed HTML, this should correspond directly to what are normally called "Boolean Attributes." Boolean attributes are just words inside of an HTML Element that describe the contents of the HTML. The primary issue about "Boolean Attributes" is that they do not need values - they are strictly a key, alone.

        Conversion-Target Stream-Method Invocation
        String[] Stream.toArray(String[]::new);
        List<String> Stream.collect(Collectors.toList());
        Vector<String> Stream.collect(Collectors.toCollection(Vector::new));
        TreeSet<String> Stream.collect(Collectors.toCollection(TreeSet::new));
        Iterator<String> Stream.iterator();

        NOTE: This method shall never return 'null' - even if there are no attribute key-value pairs contained by the TagNode. If there are strictly zero attributes, Stream.empty() shall be returned, instead.
        See Also:
        tok, HTMLNode.str
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
         // NOTE: OPTIMIZED, "closing-versions" of the TagNode, and TagNode's whose 'str'
         //       field is only longer than the token, itself, by 3 or less characters cannot have
         //       attributes.  In that case, just return an empty 'Stream' instance.
         int len = str.length();
         if (isClosing || (len <= (tok.length() + 3))) return Stream.empty();
        
         // Leaves off the opening 'token' and less-than '<' symbol  (leaves off, <DIV - for example)
         // Also leave off the "ending-forward-slash" (if there is one).
         String  s = str.substring(tok.length() + 1, len - ((str.charAt(len - 2) == '/') ? 2 : 1));
        
         // if all lower-case is requested, do that here.
         if (! preserveKeysCase) s = s.toLowerCase();
        
         // java.util.regex.Pattern.split(CharSequence) is sort of an "inverse reg-ex" in that it 
         // returns all of the text that was present BETWEEN the matches 
         // NOTE: This is the "opposite of the matches, themselves)" - a.k.a. all the stuff that was
         //       left-out.
        
         Stream.Builder<String> b = Stream.builder();
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(s))          // 'split' => inverse-matches
             for (String keyWord : unMatchedStr.split("\\s+"))                   // white-space split (connected chars)
                 if ((keyWord = keyWord.trim()).length() > 0)                    // Call String.trim() and String.length()
                     if (AttrRegEx.ATTRIBUTE_KEY_REGEX.matcher(keyWord).find())  // Check for valid Attribute-Name
                         b.add(keyWord);                                         // ... put it in the return stream.
                                                                                 // NOTE: This has the potential to slightly
                                                                                 //       change the original HTML... It will
                                                                                 //       "leave out any guck" that was in the Element
        
         // Build the Stream<String>, and return;
         return b.build();
        
      • hasKeyOnlyAttribute

        public boolean hasKeyOnlyAttribute​(java.lang.String keyOnlyAttribute)
        Will identify if a "boolean attribute" - a.k.a. a token-name that exists BETWEEN inner-tag key-value pairs is present in the TagNode. One of the most common "Key-Word-Only Attributes" is the inner-tag 'HIDDEN'. Hidden HTML Elements have their CSS feature style.display set to 'NONE';
        Parameters:
        keyOnlyAttribute - This may be the name of any inner-tag.

        NOTE: This parameter, is not checked for validity against the attribute-name regular-expression.
        Returns:
        Will return TRUE if the named 'keyOnlyAttribute' is present in the HTML Element as a stand-alone attribute - i.e., lacking a value assignment The comparison performed is case-insensitive.

        If 'this' instance of TagNode is a closing-version of the element, this method shall return FALSE immediately, and exit.
        Throws:
        java.lang.IllegalArgumentException - If the input-parameter receives a String that contains any white-space itself, an exception will throw. The search-logic splits the String's based on white-space, so if a user passes a white-space containing String, a match would simply never occur.

        IMPORTANT: This method does not check the validity of the 'keyOnlyAttribute' parameter against the Attribute-name regular-expression, because this method uses the 'split(String)' method of the Regular-Expression Matcher. All this means, is that this method may actually be used to check for any-text inside of an HTML Element - so long as that text does not contain white-space. This is not an encouraged use of this method, but it will work.
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
         // Closing TagNode's do not have attributes, return false immediately.
         if (this.isClosing) return false;
        
         // ONLY CHECKS FOR WHITE-SPACE, *NOT* VALIDITY...
         if (StringParse.hasWhiteSpace(keyOnlyAttribute)) throw new IllegalArgumentException(
             "The attribute you have passed [" + keyOnlyAttribute + "] has white-space, " +
             "This is not allowed here, because the search routine splits on whitespace, and " +
             "therefore a match would never be found."
         );
        
         // NOTE: TagNode's whose 'str' field is only longer than the token, itself, by 3 or less
         //       characters cannot have attributes.  In that case, just return false.
         int len = str.length();
         if (len <= (tok.length() + 3)) return false;
        
         // Leaves off the opening 'token' and less-than '<' symbol  (leaves off, <DIV - for example)
         // Also leave off the "ending-forward-slash" (if there is one).
         String s = str.substring(tok.length() + 1, len - ((str.charAt(len - 2) == '/') ? 2 : 1));
        
         // java.util.regex.Pattern.split(CharSequence) is sort of an "inverse reg-ex" in that it 
         // returns all of the text that was present BETWEEN the matches 
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(s))  // 'split' => inverse-matches
             for (String keyWord : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyWord = keyWord.trim()).length() > 0)            // trim, check-length...
                     if (keyOnlyAttribute.equalsIgnoreCase(keyWord))
                         return true;
        
         // Was not found, return false;
         return false;
        
      • testAV

        public boolean testAV​(java.lang.String attributeName,
                              TextComparitor tc,
                              java.lang.String... compareStr)
        Test the value of the inner-tag named 'attributeName' (if that attribute exists, and has a non-empty value) using a provided TextComparitor
        Parameters:
        attributeName - Any String will suffice - but only valid attribute names will match the internal regular-expression.

        NOTE: The validity of this parameter is not checked with the HTML attribute-name Regular-Expression exception checker.
        tc - This may be any pre-instantiated or user created TextComparitor
        compareStr - This should be a String[] array of comparison-strings to work with parameter 'tc'.
        Returns:
        Method will return TRUE if:

        • 'this' instance of TagNode has an inner-tag named 'attributeName'.

        • The results of the provided TextComparitor (BiPredicate<String, String[]>) parameter 'tc' using method tc.test(attribute-value, compareStr) when applied to the value of the requested attribute return TRUE.
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX, HTMLNode.str, isClosing, StringParse.ifQuotesStripQuotes(String)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
         // Closing TagNode's (</DIV>, </A>) cannot attributes, or attribute-values
         if (isClosing) return false;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have the attribute named by the input parameter
         if (this.str.length() < (this.tok.length() + attributeName.length() + 4)) return false;
        
         // This Reg-Ex will allow us to iterate through each attribute key-value pair
         // contained / 'inside' this instance of TagNode.
         Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // Test each attribute key-value pair, and return the test results if an attribute
         // whose name matches 'attributeName' is found.
         while (m.find())
             if (m.group(2).equalsIgnoreCase(attributeName))
                 return tc.test(StringParse.ifQuotesStripQuotes(m.group(3)), compareStr);
        
         // No attribute key-value pair was found whose 'key' matched input-parameter
         // 'attributeName'
         return false;
        
      • testAV

        public boolean testAV​(java.lang.String attributeName,
                              java.util.regex.Pattern regEx)
        Test the value of the inner-tag named 'attributeName' (if that attribute exists, and has a non-empty value) using a provided java.util.regex.Pattern

        example
        Parameters:
        attributeName - Any String will suffice - but only valid attribute names will match the internal regular-expression.

        NOTE: The validity of this parameter is not checked with the HTML attribute-name Regular-Expression exception checker.
        regEx - This may be an regular-expression
        Returns:
        Method will return TRUE if:

        • 'this' instance of TagNode has an inner-tag named 'attributeName'.

        • The results of invoking regEx.matcher(...).find() on the value of the attribute return TRUE.
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX, HTMLNode.str, isClosing, StringParse.ifQuotesStripQuotes(String)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
         // Closing TagNode's (</DIV>, </A>) cannot attributes, or attribute-values
         if (isClosing) return false;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have the attribute named by the input parameter
         if (this.str.length() < (this.tok.length() + attributeName.length() + 4)) return false;
        
         // This Reg-Ex will allow us to iterate through each attribute key-value pair
         // contained / 'inside' this instance of TagNode.
         Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // Test each attribute key-value pair, and return the test results if an attribute
         // whose name matches 'attributeName' is found.
         while (m.find())
             if (m.group(2).equalsIgnoreCase(attributeName))
                 return regEx.matcher(StringParse.ifQuotesStripQuotes(m.group(3))).find();
        
         // No attribute key-value pair was found whose 'key' matched input-parameter
         // 'attributeName'
         return false;
        
      • testAV

        public boolean testAV​(java.lang.String attributeName,
                              java.util.function.Predicate<java.lang.String> pred)
        Test the value of the inner-tag named 'attributeName' (if that attribute exists, and has a non-empty value) using a provided Predicate<String>.

        example
        Parameters:
        attributeName - Any String will suffice - but only valid attribute names will match the internal regular-expression.

        NOTE: The validity of this parameter is not checked with the HTML attribute-name Regular-Expression exception checker.
        pred - Any java.util.function.Predicate<String>
        Returns:
        Method will return TRUE if:

        • 'this' instance of TagNode has an inner-tag named 'attributeName'.

        • The results of the provided String-Predicate when applied against the value of the requested attribute return TRUE.
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX, HTMLNode.str, isClosing, StringParse.ifQuotesStripQuotes(String)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
         // Closing TagNode's (</DIV>, </A>) cannot attributes, or attribute-values
         if (isClosing) return false;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have the attribute named by the input parameter
         if (this.str.length() < (this.tok.length() + attributeName.length() + 4)) return false;
        
         // This Reg-Ex will allow us to iterate through each attribute key-value pair
         // contained / 'inside' this instance of TagNode.
         Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // Test each attribute key-value pair, and return the test results if an attribute
         // whose name matches 'attributeName' is found.
         while (m.find())
             if (m.group(2).equalsIgnoreCase(attributeName))
                 return pred.test(StringParse.ifQuotesStripQuotes(m.group(3)));
        
         // No attribute key-value pair was found whose 'key' matched input-parameter
         // 'attributeName'
         return false;
        
      • hasNAND

        public boolean hasNAND​(boolean checkAttributeStringsForErrors,
                               java.lang.String... attributes)
        This method provides a quick way of testing if 'this' HTML TagNode contains certain inner-tags with specified names.

        This uses logical NAND meaning that FALSE shall be returned if any of the inner tags provided in the 'attributes' parameter list are found in 'this' instance of TagNode.
        Parameters:
        attributes - This is a list of HTML Element Attribute-Names or "Inner Tags" as they are called in this Search and Scrape Package.
        checkAttributeStringsForErrors - Some may argue this is confusing. The purpose of this boolean is actually somewhat important. People who have written software libraries start to see that "Error Checking" (Proper Exceptions with consistent error messages) is one of the more beneficial features of the library. The catch is that it can add a layer of redundancy and inefficiency - where the same set of data are tested and retested (inside a loop, for instance) over and over.

        If the programmer has already checked the attributes / inner-tags for validity, and intends to use this boolean-method inside of a loop-construct, it should be obvious that FALSE needs to be passed to this parameter. If this test is not within a loop, it is much less of an issue.

        NOTE: When this variable is *FALSE*, the passed attribute parameters will not be checked for validity. When *TRUE* the attributes Strings will be tested on each method invocation. This is included to encourage programmers to check their HTML and avoid invalid attribute strings (has spaces, are null, etc.) earlier.
        Returns:
        TRUE if none of these attributes-names are present, in 'this' instance and FALSE otherwise.

        NOTE: If this method is passed a zero-length String-array to the 'attributes' parameter, this method shall exit immediately and return TRUE.
        Throws:
        InnerTagKeyException - If any of the 'attributes' are not valid HTML attributes, and the user has passed TRUE to parameter checkAttributeStringsForErrors.
        java.lang.NullPointerException - If any of the 'attributes' are null.
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        java.lang.IllegalArgumentException - If the 'attributes' parameter has length zero.
        See Also:
        InnerTagKeyException.check(String[]), AV(String), TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
         ClosingTagNodeException.check(this);
        
         // If no attributes are passed to 'attributes' parameter, throw exception.
         if (attributes.length == 0) throw new IllegalArgumentException
             ("Input variable-length String[] array parameter, 'attributes', has length zero.");
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have any attribute-value pairs.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         //
         // This TagNode doesn't have any attributes in it.
         // There is no need to check anything, so return FALSE immediately ("NAND" succeeds)
         if (this.str.length() < (this.tok.length() + 4)) return true;
        
         if (checkAttributeStringsForErrors) InnerTagKeyException.check(attributes);
        
         // Get all inner-tag key-value pairs.  If even one of these is inside the 'attributes'
         // input-parameter string-array.  Then we must return false, since this is a NAND - or
         // 'NOT ANY' - operation.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         while (keyValueInnerTags.find())
         {
             String innerTagKey = keyValueInnerTags.group(2);                    // Retrieve the key of the key-value pair
             for (String attributeToNAND : attributes)                           // Iterate every element of the String[] 'attributes' parameter
                 if (innerTagKey.equalsIgnoreCase(attributeToNAND))              // Does the input parameter String-array match with an inner-tag key?
                     return false;                                               // NAND: If there is a match, return FALSE immediately
         }
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attributes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the real matches)
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     for (String attributeToNAND : attributes)                   // Iterate all the input-parameter String-array attributes
                         if (keyOnlyInnerTag.equalsIgnoreCase(attributeToNAND))  // If even one of them
                             return false;                                       // NAND: If there is a match, return FALSE immediately
        
         // If NIETHER the Key-Value attributes, NOR the Key-Only attributes had any matches with
         // the input String-Array parameter "attributes", then (and only then) should TRUE be returned.
         return true;
        
      • hasXOR

        public boolean hasXOR​(boolean checkAttributeStringsForErrors,
                              java.lang.String... attributes)
        This method provides a quick way of testing if 'this' HTML TagNode contains certain inner-tags with specified names.

        This uses logical XOR meaning that TRUE shall be returned if *precisely one* of the inner tags provided in the 'attributes' parameter list are found in 'this' instance of TagNode.
        Parameters:
        attributes - This is a list of HTML Element Attribute-Names or "Inner Tags" as they are called in this Search and Scrape Package.
        checkAttributeStringsForErrors - Some may argue this is confusing. The purpose of this boolean is actually somewhat important. People who have written software libraries start to see that "Error Checking" (Proper Exceptions with consistent error messages) is one of the more beneficial features of the library. The catch is that it can add a layer of redundancy and inefficiency - where the same set of data are tested and retested (inside a loop, for instance) over and over.

        If the programmer has already checked the attributes / inner-tags for validity, and intends to use this boolean-method inside of a loop-construct, it should be obvious that FALSE needs to be passed to this parameter. If this test is not within a loop, it is much less of an issue.

        NOTE: When this variable is *FALSE*, the passed attribute parameters will not be checked for validity. When *TRUE* the attributes Strings will be tested on each method invocation. This is included to encourage programmers to check their HTML and avoid invalid attribute strings (has spaces, are null, etc.) earlier.
        Returns:
        TRUE if exactly one of these attributes-names are present in 'this' instance, and FALSE otherwise.

        NOTE: If this method is passed a zero-length String-array to the 'attributes' parameter, this method shall exit immediately and return FALSE.
        Throws:
        InnerTagKeyException - If any of the 'attributes' are not valid HTML attributes and the user has passed TRUE to parameter checkAttributeStringsForErrors.
        java.lang.NullPointerException - If any of the 'attributes' are null.
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        java.lang.IllegalArgumentException - If the 'attributes' parameter has length zero.
        See Also:
        InnerTagKeyException.check(String[]), AV(String), TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
         ClosingTagNodeException.check(this);
        
         // If no attributes are passed to 'attributes' parameter, throw exception.
         if (attributes.length == 0) throw new IllegalArgumentException
             ("Input variable-length String[] array parameter, 'attributes', has length zero.");
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have any attribute-value pairs.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         //
         // This TagNode doesn't have any attributes in it.
         // There is no need to check anything, so return FALSE immediately ("XOR" fails)
         if (this.str.length() < (this.tok.length() + 4)) return false;
        
         if (checkAttributeStringsForErrors) InnerTagKeyException.check(attributes);
        
         // Temporary variable.  This is required because this is an "XOR" comparison.
         boolean ret = false;
        
         // Get all inner-tag key-value pairs.  When one of the input-parameter string-array
         // inner-tags is found, we must take a note saying a match has been found.  If there is a
         // second-match, because this is XOR, we must return FALSE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         while (keyValueInnerTags.find())
         {
             String innerTagKey = keyValueInnerTags.group(2);                    // Retrieve the key of the key-value pair
             for (String attributeToXOR : attributes)                            // Iterate every element of the String[] 'attributes' parameter
                 if (innerTagKey.equalsIgnoreCase(attributeToXOR))               // Does the input parameter String-array match with an inner-tag key?
                 { if (ret) return false; else ret = true; }                     // XOR: If there is a second match must, return FALSE immediately
         }
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attributes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the
         // real matches)
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     for (String attributeToXOR : attributes)                    // Iterate all the input-parameter String-array attributes
                         if (keyOnlyInnerTag.equalsIgnoreCase(attributeToXOR))   // Is there a match?
                         { if (ret) return false; else ret = true; }             // XOR: If there is a second match must, return FALSE immediately
        
         // This will be TRUE if there was PRECISELY one match.
         return ret;
        
      • hasAND

        public boolean hasAND​(boolean checkAttributeStringsForErrors,
                              java.lang.String... attributes)
        This method provides a quick way of testing if 'this' HTML TagNode contains certain inner-tags with specified names.

        This uses logical AND meaning that TRUE shall be returned *if and only if* all of the inner tags provided in the 'attributes' parameter list are found in 'this' instance of TagNode.

        Example
        Parameters:
        attributes - This is a list of HTML Element Attribute-Names or "Inner Tags" as they are called in this Search and Scrape Package.
        checkAttributeStringsForErrors - Some may argue this is confusing. The purpose of this boolean is actually somewhat important. People who have written software libraries start to see that "Error Checking" (Proper Exceptions with consistent error messages) is one of the more beneficial features of the library. The catch is that it can add a layer of redundancy and inefficiency - where the same set of data are tested and retested (inside a loop, for instance) over and over.

        If the programmer has already checked the attributes / inner-tags for validity, and intends to use this boolean-method inside of a loop-construct, it should be obvious that FALSE needs to be passed to this parameter. If this test is not within a loop, it is much less of an issue.

        NOTE: When this variable is *FALSE*, the passed attribute parameters will not be checked for validity. When *TRUE* the attributes Strings will be tested on each method invocation. This is included to encourage programmers to check their HTML and avoid invalid attribute strings (has spaces, are null, etc.) earlier.
        Returns:
        TRUE if each and every one of these attributes-names are present in 'this' instance, and FALSE otherwise.

        NOTE: If this method is passed a zero-length String-array to the 'attributes' parameter, this method shall exit immediately and return FALSE
        Throws:
        InnerTagKeyException - If any of the 'attributes' are not valid HTML attributes, and the user has passed TRUE to parameter checkAttributeStringsForErrors.
        java.lang.NullPointerException - If any of the 'attributes' are null.
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        java.lang.IllegalArgumentException - If the 'attributes' parameter has length zero.
        See Also:
        InnerTagKeyException.check(String[]), AV(String), TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
         ClosingTagNodeException.check(this);
        
         // If no attributes are passed to 'attributes' parameter, throw exception.
         if (attributes.length == 0) throw new IllegalArgumentException
             ("Input variable-length String[] array parameter, 'attributes', has length zero.");
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have any attribute-value pairs.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         //
         // This TagNode doesn't have any attributes in it.
         // There is no need to check anything, so return FALSE immediately ("AND" fails)
         if (this.str.length() < (this.tok.length() + 4)) return false;
        
         if (checkAttributeStringsForErrors) InnerTagKeyException.check(attributes);
        
         // This is a temporary data-structure.  It will keep a list of the inner-tags from the
         // input-parameter string-array 'attributes' stored in lowe-case format.  These elements
         // will be slowly removed as they are found in the HTML Element.  Once the size() of this
         // TreeSet is zero, it would imply that ALL INNER-TAGS WERE FOUND, and then TRUE would be
         // returned.
         TreeSet<String> tlcAttributes = new TreeSet<>();
        
         for (String attribute : attributes) tlcAttributes.add(attribute.toLowerCase());
        
         // Get all inner-tag key-value pairs.  Every time the "attribute-name" matches one of the
         // elements in the temporary TreeSet data-structure, it means another one of the requested
         // attributes has been found.  When they are all found, we can return TRUE.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         String innerTagKey;
         while (keyValueInnerTags.find())
             if (tlcAttributes.contains(innerTagKey = keyValueInnerTags.group(2).toLowerCase()))
             {
                 tlcAttributes.remove(innerTagKey);                              // Found another match with the input String[] parameter
                 if (tlcAttributes.size() == 0) return true;                     // If all attributes have been found, return TRUE immediately
             }
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attrbutes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the
         // real matches)
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     if (tlcAttributes.contains(keyOnlyInnerTag = keyOnlyInnerTag.toLowerCase()))
                     {
                         tlcAttributes.remove(keyOnlyInnerTag);                  // Found another match with the input String[] parameter
                         if (tlcAttributes.size() == 0) return true;             // If all attributes have been found, return TRUE immediately
                     }
        
         // Some of the input-parameter string-array 'attributes' were NOT FOUND (or else TRUE would
         // have already been returned).
         // Since this is an 'AND' return false.
         return false;
        
      • hasOR

        public boolean hasOR​(boolean checkAttributeStringsForErrors,
                             java.lang.String... attributes)
        This method provides a quick way of testing if 'this' HTML TagNode contains certain inner-tags with specified names.

        This uses logical OR meaning that TRUE shall be returned if any of the inner tags provided in the 'attributes' parameter list are found in 'this' instance of TagNode.

        Example
        Parameters:
        attributes - This is a list of HTML Element Attribute-Names or "Inner Tags" as they are called in this Search and Scrape Package.
        checkAttributeStringsForErrors - Some may argue this is confusing. The purpose of this boolean is actually somewhat important. People who have written software libraries start to see that "Error Checking" (Proper Exceptions with consistent error messages) is one of the more beneficial features of the library. The catch is that it can add a layer of redundancy and inefficiency - where the same set of data are tested and retested (inside a loop, for instance) over and over.

        If the programmer has already checked the attributes / inner-tags for validity, and intends to use this boolean-method inside of a loop-construct, it should be obvious that FALSE needs to be passed to this parameter. If this test is not within a loop, it is much less of an issue.

        NOTE: When this variable is *FALSE*, the passed attribute parameters will not be checked for validity. When *TRUE* the attributes Strings will be tested on each method invocation. This is included to encourage programmers to check their HTML and avoid invalid attribute strings (has spaces, are null, etc.) earlier.
        Returns:
        TRUE if at least one of these attribute-names are present in 'this' instance, and FALSE otherwise.

        NOTE: If this method is passed a zero-length String-array to the 'attributes' parameter, this method shall exit immediately and return FALSE.
        Throws:
        InnerTagKeyException - If any of the 'attributes' are not valid HTML attributes, and the user has passed TRUE to parameter checkAttributeStringsForErrors.
        java.lang.NullPointerException - If any of the 'attributes' are null.
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        java.lang.IllegalArgumentException - If the 'attributes' parameter has length zero.
        See Also:
        InnerTagKeyException.check(String[]), AV(String), TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
         ClosingTagNodeException.check(this);
        
         // If no attributes are passed to 'attributes' parameter, throw exception.
         if (attributes.length == 0) throw new IllegalArgumentException
             ("Input variable-length String[] array parameter, 'attributes', has length zero.");
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have any attribute-value pairs.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         //
         // This TagNode doesn't have any attributes in it.
         // There is no need to check anything, so return FALSE immediately ("OR" fails)
         if (this.str.length() < (this.tok.length() + 4)) return false;
        
         if (checkAttributeStringsForErrors) InnerTagKeyException.check(attributes);
        
         // Get all inner-tag key-value pairs.  If even one of these is inside the 'attributes'
         // input-parameter string-array,  Then we must return true, since this is OR
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         while (keyValueInnerTags.find())
         {
             String innerTagKey = keyValueInnerTags.group(2);                    // Retrieve the key of the key-value pair
             for (String attributeToOR : attributes)                             // Iterate every element of the String[] 'attributes' parameter
                 if (innerTagKey.equalsIgnoreCase(attributeToOR))                // Does the input parameter String-array match with an inner-tag key?
                     return true;                                                // OR: If there is any match, return TRUE immediately
         }
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attributes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the
         // real matches)
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     for (String attributeToOR : attributes)                     // Iterate all the input-parameter String-array attributes
                         if (keyOnlyInnerTag.equalsIgnoreCase(attributeToOR))    // If even one of them
                             return true;                                        // NAND: If there is any match, return TRUE immediately
        
         // If NIETHER the Key-Value attributes, NOR the Key-Only attributes had any matches with
         // the input String-Array parameter "attributes", the OR-comparison has failed, and we
         // must return false.
         return false;
        
      • has

        public boolean has​(java.lang.String attributeName)
        Will search this TagNode to determine if any inner-tag key-value pairs have a key (attribute-name) that are identical (using case-insensitive comparisons) to input-parameter String attributeName.

        This method will also check the boolean-attributes, which are attributes that include a key, but do not have a value assigned to them (such as the commonly used boolean-attribute 'hidden' - which indicates that a particular element should not be rendered-visible).

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning FALSE. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML does not actually allow for attributes placed inside of closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).

        example
        Parameters:
        attributeName - A String parameter that may be the name of any attribute / inner-tag to be found inside 'this' HTML Element.

        NOTE: No validity checks are performed about this parameter. If the 'attributeName' does not represent a valid attribute, it likely will just fail to find a match - and the method will exit gracefully, returning FALSE.
        Returns:
        Will return TRUE if there are any inner-tag's whose name equals parameter 'attributeName' using a case-insensitive String-equality comparison (and FALSE otherwise).
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return false;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         if (this.str.length() < (this.tok.length() + 4)) return false;
        
         // Get all inner-tag key-value pairs.  If any are 'equal' to parameter attributeName,
         // return TRUE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // the matcher.group(2) has the key (not the value)
         while (keyValueInnerTags.find())
             if (attributeName.equalsIgnoreCase(keyValueInnerTags.group(2)))
                 return true;
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attributes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the
         // real matches)
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     if (attributeName.equalsIgnoreCase(keyOnlyInnerTag)) return true;
        
         // A match was not found in either the "key-value pairs", or the boolean "key-only list."
         return false;
        
      • has

        public boolean has​
                    (java.util.function.Predicate<java.lang.String> attributeNameTest)
        
        Will search this TagNode to determine if any inner-tag key-value pairs have a key (attribute-name) that is accepted by the Predicate<String> parameter 'attributeNameTest'.

        This method will also check the boolean-attributes, which are attributes that include a key, but do not have a value assigned to them (such as the commonly used boolean-attribute 'hidden' - which indicates that a particular element should not be rendered-visible).

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning FALSE. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML does not actually allow for attributes placed inside of closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).
        Parameters:
        attributeNameTest - A String Predicate parameter that is used to accept or reject a match with the list of attribute keys or boolean-attribute keys in 'this' HTML Element.

        NOTE: The StrFilter in package Torello.Java is capable of generating a rather wide range of test-Predicate's using regular-expressions. This is one option to think about when using this method.
        Returns:
        Will return TRUE if there are any inner-tag's whose name is accepted by the input-parameter Predicate 'attributeNameTest' (and FALSE otherwise).
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX, StrFilter
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return false;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         if (this.str.length() < (this.tok.length() + 4)) return false;
        
         // Get all inner-tag key-value pairs.  If any of them match with the 'attributeNameTest'
         // Predicate, return TRUE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // the matcher.group(2) has the key (not the value)
         while (keyValueInnerTags.find())
             if (attributeNameTest.test(keyValueInnerTags.group(2)))
                 return true;
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attributes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the
         // real matches)
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     if (attributeNameTest.test(keyOnlyInnerTag))
                         return true;
        
         // A match was not found in either the "key-value pairs", or the boolean "key-only list."
         return false;
        
      • has

        public boolean has​(java.util.regex.Pattern attributeNameTest)
        Will search this TagNode to determine if any inner-tag key-value pairs have a key (attribute-name) that are accepted by the Java Regular-Expression parameter 'attributeNameTest'.

        This method will also check the boolean-attributes, which are attributes that include a key, but do not have a value assigned to them (such as the commonly used boolean-attribute 'hidden' - which indicates that a particular element should not be rendered-visible).

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning FALSE. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML does not actually allow for attributes placed inside of closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).
        Parameters:
        attributeNameTest - A java.util.regex.Pattern regular-expression that is used to accept or reject a match with the list of attribute keys or boolean-attribute keys in 'this' HTML Element
        Returns:
        Will return TRUE if there are any inner-tag's whose name is accepted by the input-parameter regular-expression 'attributeNameTest' (and FALSE otherwise).
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return false;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         if (this.str.length() < (this.tok.length() + 4)) return false;
        
         // Get all inner-tag key-value pairs.  If any of them match with the 'attributeNameTest'
         // Regular-Expression, return TRUE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // the matcher.group(2) has the key (not the value)
         while (keyValueInnerTags.find())
             if (attributeNameTest.matcher(keyValueInnerTags.group(2)).find())
                 return true;
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attributes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the
         // real matches)
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     if (attributeNameTest.matcher(keyOnlyInnerTag).find())
                         return true;
        
         // A match was not found in either the "key-value pairs", or the boolean "key-only list."
         return false;
        
      • has

        public boolean has​(TextComparitor tc,
                           java.lang.String... compareStrs)
        Will search this TagNode to determine if any inner-tag key-value pairs have a key (attribute-name) that are accepted by the TextComparitor & Compare-String's parameters.

        This method will also check the boolean-attributes, which are attributes that include a key, but do not have a value assigned to them (such as the commonly used boolean-attribute 'hidden' - which indicates that a particular element should not be rendered-visible).

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning FALSE. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML does not actually allow for attributes placed inside of closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).
        Parameters:
        tc - An instance of TextComparitor that is used (in conjunction with the 'compareStrs') to accept or reject a match with the list of attribute keys or boolean-attribute keys in 'this' HTML Element
        compareStrs - This is the list of comparison-String's with which the named TextComparitor performs the tests against an input 'test-String.' For more information, please review the list of TextComparitor static-instances defined in that class.
        Returns:
        Will return TRUE if there are any inner-tag's whose name is accepted by the input-parameter TextComparitor (and FALSE otherwise).
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX, TextComparitor
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return false;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 4 (characters) are: '<', '>', ' ' and 'X'
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X>
         if (this.str.length() < (this.tok.length() + 4)) return false;
        
         // Get all inner-tag key-value pairs.  If any of them match with the TextComparitor & compareStrs,
         // return TRUE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         // the matcher.group(2) has the key (not the value)
         while (keyValueInnerTags.find())
             if (tc.test(keyValueInnerTags.group(2), compareStrs))
                 return true;
        
         // Also check the "Boolean Attributes" also known as the "Key-Word Only Attributes"
         // Use the "Inverse Reg-Ex Matcher" (which matches all the strings that are "between" the
         // real matches)
        
         for (String unMatchedStr : AttrRegEx.KEY_VALUE_REGEX.split(this.str))   // INVERSE-matches of the key-value Reg-Ex
             for (String keyOnlyInnerTag : unMatchedStr.split("\\s+"))           // white-space split (connected chars)
                 if ((keyOnlyInnerTag = keyOnlyInnerTag.trim()).length() > 0)    // Just-In-Case, usually not necessary
                     if (tc.test(keyOnlyInnerTag, compareStrs))
                         return true;
        
         // A match was not found in either the "key-value pairs", or the boolean "key-only list."
         return false;
        
      • hasValue

        public java.util.Map.Entry<java.lang.String,​java.lang.String> hasValue​
                    (java.lang.String attributeValue,
                     boolean retainQuotes,
                     boolean preserveKeysCase)
        
        Will search this TagNode to determine if any inner-tag key-value pairs have a an attribute-value that is identical (using case-insensitive comparisons) to input-parameter String attributeName.

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning 'null'. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML will not permit attributes placed inside closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).
        Parameters:
        attributeValue - A String parameter that may be the value of any attribute / inner-tag to be found inside 'this' HTML Element.

        NOTE: This parameter is not checked for validity. If this String is not a valid HTML Attribute-Name, likely it will not match the regular-expression matcher. In such cases, the method returns null.
        retainQuotes - The parameter is required to inform the program whether or not the quotation marks should be included along with the returned value inside the Map.Entry. This can sometimes be useful, for example, when complicated script-containing TagNode's are involved.
        preserveKeysCase - When this parameter is TRUE, the program will not invoke String.toLowerCase() on the Map.Entry's key. If FALSE, then the returned Map.Entry will have a key (attribute-name) that is strictly in lower-case format.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        Returns:
        Returns a Map.Entry<String, String> Key-Value Pair - if and only if there are any inner-tag's whose value is equal to input-parameter 'attributeValue' using a case-insensitive String-equality comparison.

        If no such match is found, this method will return 'null'
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return null;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 5 (characters) are: '<', '>', ' ', 'X' and '=' 
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X=>
         if (this.str.length() < (this.tok.length() + 5)) return null;
        
         // Get all inner-tag key-value pairs.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         while (keyValueInnerTags.find())
         {
             // Matcher.group(3) has the key's value, of the inner-tag key-value pair
             // (matcher.group(2) has the key's name)
             String foundAttributeValue = keyValueInnerTags.group(3);
        
             // The comparison must be performed on the version of the value that DOES NOT HAVE the
             // surrounding quotation-marks
             String foundAttributeValueNoQuotes = StringParse.ifQuotesStripQuotes(foundAttributeValue);
        
             // Perform the comparison, making sure to ignore case when comparing.
             if (attributeValue.equalsIgnoreCase(foundAttributeValueNoQuotes))
        
                 // matcher.group(2) has the key's name, not the value.  This is returned via the
                 // Map.Entry key
                 return retainQuotes
                     ? new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValue)
                     : new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValueNoQuotes);
         }
        
         // No match was identified, return null.
         return null;
        
      • hasValue

        public java.util.Map.Entry<java.lang.String,​java.lang.String> hasValue​
                    (java.util.function.Predicate<java.lang.String> attributeValueTest,
                     boolean retainQuotes,
                     boolean preserveKeysCase)
        
        Will search this TagNode to determine if any inner-tag key-value pairs have a an attribute-value that is accepted by the String-Predicate parameter 'attributeValueTest'.

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning 'null'. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML will not permit attributes placed inside closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).
        Parameters:
        attributeValueTest - A String-Predicate parameter that is used to accept or reject a match with the list of all attribute values in 'this' HTML Element

        NOTE: The StrFilter in package Torello.Java is capable of generating a rather wide range of test-Predicate's using regular-expressions. This is one option to think about when using this method.
        retainQuotes - The parameter is required to inform the program whether or not the quotation marks should be included along with the returned value inside the Map.Entry. This can sometimes be useful, for example, when complicated script-containing TagNode's are involved.
        preserveKeysCase - When this parameter is TRUE, the program will not invoke String.toLowerCase() on the Map.Entry's key. If FALSE, then the returned Map.Entry will have a key (attribute-name) that is strictly in lower-case format.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        Returns:
        Returns a Map.Entry<String, String> Key-Value Pair - if and only if there are any inner-tag's whose value is accepted by the input-parameter Predicate-test 'attributeNameTest'.

        If no such match is found, this method will return 'null'
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX, StrFilter
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return null;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 5 (characters) are: '<', '>', ' ', 'X' and '=' 
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X=>
         if (this.str.length() < (this.tok.length() + 5)) return null;
        
         // Get all inner-tag key-value pairs.  If any are 'equal' to parameter attributeName,
         // return TRUE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         while (keyValueInnerTags.find())
         {
             // Matcher.group(3) has the key's value, of the inner-tag key-value pair
             // (matcher.group(2) has the key's name)
             String foundAttributeValue = keyValueInnerTags.group(3);
        
             // The comparison must be performed on the version of the value that DOES NOT HAVE the
             // surrounding quotation-marks
             String foundAttributeValueNoQuotes = StringParse.ifQuotesStripQuotes(foundAttributeValue);
        
             // Matcher.group(3) has the key-value, make sure to remove quotation marks (if present) before comparing.
             if (attributeValueTest.test(foundAttributeValueNoQuotes))
        
                 // matcher.group(2) has the key's name, not the value.  This is returned via the
                 // Map.Entry key
                 return retainQuotes
                     ? new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValue)
                     : new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValueNoQuotes);
         }
        
         // No match was identified, return null.
         return null;
        
      • hasValue

        public java.util.Map.Entry<java.lang.String,​java.lang.String> hasValue​
                    (java.util.regex.Pattern attributeValueTest,
                     boolean retainQuotes,
                     boolean preserveKeysCase)
        
        Will search this TagNode to determine if any inner-tag key-value pairs have an attribute-value that is accepted by the Java Regular-Expression parameter 'attributeValueTest'.

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning 'null'. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML will not permit attributes placed inside closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).
        Parameters:
        attributeValueTest - A java.util.regex.Pattern parameter that is used to accept or reject a match with the list of all attribute values in 'this' HTML Element
        retainQuotes - The parameter is required to inform the program whether or not the quotation marks should be included along with the returned value inside the Map.Entry. This can sometimes be useful, for example, when complicated script-containing TagNode's are involved.
        preserveKeysCase - When this parameter is TRUE, the program will not invoke String.toLowerCase() on the Map.Entry's key. If FALSE, then the returned Map.Entry will have a key (attribute-name) that is strictly in lower-case format.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return null;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 5 (characters) are: '<', '>', ' ', 'X' and '=' 
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X=>
         if (this.str.length() < (this.tok.length() + 5)) return null;
        
         // Get all inner-tag key-value pairs.  If any are 'equal' to parameter attributeName, return
         // TRUE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         while (keyValueInnerTags.find())
         {
             // Matcher.group(3) has the key's value, of the inner-tag key-value pair
             // (matcher.group(2) has the key's name)
             String foundAttributeValue = keyValueInnerTags.group(3);
        
             // The comparison must be performed on the version of the value that DOES NOT HAVE the
             // surrounding quotation-marks
             String foundAttributeValueNoQuotes = StringParse.ifQuotesStripQuotes(foundAttributeValue);
        
             // Matcher.group(3) has the key-value, make sure to remove quotation marks (if present)
             // before comparing.
             if (attributeValueTest.matcher(foundAttributeValueNoQuotes).find())
        
                 // matcher.group(2) has the key's name, not the value.  This is returned via the 
                 // Map.Entry key
                 return retainQuotes
                     ? new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValue)
                     : new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValueNoQuotes);
         }
        
         // No match was identified, return null.
         return null;
        
      • hasValue

        public java.util.Map.Entry<java.lang.String,​java.lang.String> hasValue​
                    (boolean retainQuotes,
                     boolean preserveKeysCase,
                     TextComparitor tc,
                     java.lang.String... compareStrs)
        
        Will search this TagNode to determine if any inner-tag key-value pairs have a an attribute-value that is accepted by the TextComparitor & Compare-String's parameters.

        NOTE: If 'this' instance of TagNode is a closing-version of the HTML Tag (SPECIFICALLY: this.isClosing == TRUE), then this method shall exit gracefully - and immediately - returning 'null'. The (sometimes) requisite exception for such cases - OpeningTagNodeExpectedException - shall NOT throw. Remember that HTML will not permit attributes placed inside closing HTML elements (</DIV>, </TABLE>, or </SPAN> for instance).
        Parameters:
        tc - An instance of TextComparitor that is used (in conjunction with the 'compareStrs') to accept or reject a match with the list of attribute-values in 'this' HTML Element
        compareStrs - These are the standard compare-String's to be used as reference with the TextComparitor provided.
        retainQuotes - The parameter is required to inform the program whether or not the quotation marks should be included along with the returned value inside the Map.Entry. This can sometimes be useful, for example, when complicated script-containing TagNode's are involved.
        preserveKeysCase - When this parameter is TRUE, the program will not invoke String.toLowerCase() on the Map.Entry's key. If FALSE, then the returned Map.Entry will have a key (attribute-name) that is strictly in lower-case format.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        Returns:
        Returns a Map.Entry<String, String> Key-Value Pair - if and only if there are any inner-tag's whose value is accepted by the input-parameter TextComparitor.

        If no such match is found, this method will return 'null'
        See Also:
        TagNode.AttrRegEx.KEY_VALUE_REGEX, TextComparitor
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
         // Closing HTML Elements may not have attribute-names or values.
         // Exit gracefully, and immediately.
         if (this.isClosing) return null;
        
         // OPTIMIZATION: TagNode's whose String-length is less than this computed length 
         // are simply too short to have such an attribute-value pair.
         // 5 (characters) are: '<', '>', ' ', 'X' and '=' 
         // SHORTEST POSSIBLE SUCH-ELEMENT: <DIV X=>
         if (this.str.length() < (this.tok.length() + 5)) return null;
        
         // Get all inner-tag key-value pairs.  If any are 'equal' to parameter attributeName, 
         // return TRUE immediately.
         Matcher keyValueInnerTags = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
        
         while (keyValueInnerTags.find())
         {
             // Matcher.group(3) has the key's value, of the inner-tag key-value pair
             // (matcher.group(2) has the key's name)
             String foundAttributeValue = keyValueInnerTags.group(3);
        
             // The comparison must be performed on the version of the value that DOES NOT HAVE the
             // surrounding quotation-marks
             String foundAttributeValueNoQuotes = StringParse.ifQuotesStripQuotes(foundAttributeValue);
        
             // Matcher.group(3) has the key-value, make sure to remove quotation marks (if present)
             // before comparing.
             if (tc.test(foundAttributeValueNoQuotes, compareStrs))
        
                 // matcher.group(2) has the key's name, not the value.  This is returned via the
                 // Map.Entry key
                 return retainQuotes
                     ? new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValue)
                     : new AbstractMap.SimpleImmutableEntry<String, String>(preserveKeysCase
                             ? keyValueInnerTags.group(2)
                             : keyValueInnerTags.group(2).toLowerCase(),
                         foundAttributeValueNoQuotes);
         }
        
         // No match was identified, return null.
         return null;
        
      • getInstance

        public static TagNode getInstance​(java.lang.String tok,
                                          TC openOrClosed)
        This is an "identical" method to the one found in class HTMLTags, specifically: public static TagNode hasTag(String tag, TC openOrClosed) { ... } , except that this method will do some parameter error-checking, and throw exceptions if possible, rather than simply returning null.

        NOTE: All three classes which inherit class HTMLNode - including classes TagNode, TextNode and CommentNode are immutable. The instances cannot be changed. In order to update, tweak, or modify an HTML page, you must instantiate or retrieve another instance of HTMLNode, and replace the position in the containing-Vector with a new node. This method, 'getInstance(...)' interacts with the class HTMLTags to retrieve one of the pre-instantiated TC.OpeningTags or TC.ClosingTags TagNode's. It is possible to 're-use' the same instance of a TagNode in different pages and different Vector's in the same way that Java String's can be re-used, exactly because they are immutable.

        If a TagNode instance is used in different pages, the in-ability to change a TagNode's contents is what allows multiple, different pages to use them in multiple Vector's without worrying about their contents becoming affected by concurrency issues. class HTMLTags maintains 4 complete lists of already-instantiated TagNode's (upper-case, lower-case, opening-tag, closing-tag) in a java.util.TreeMap<String, TagNode> since usually a very high percentage of HTML Element's are elements with no attribute information at all.

        ALSO: It should be obvious to the reader, by now, that keeping 'pre-instantiated' TagNode elements that do contain attributes would be orders of magnitude more costly (and difficult}. This practice is not performed by the Java-HTML library.

        SIMILARLY: All pre-instantiated HTML TagNode's have ZERO attributes or "inner-tags" inside. To generate a TagNode with attributes, use the class TagNode constructor. Make sure not to leave out the rest of the inner-tags from the element-body String.
        Parameters:
        tok - Any valid HTML tag.
        openOrClosed - If TC.OpeningTags is passed, then an "open" version of the HTML tag will be returned,
        If TC.ClosingTags is passed, then a closing version will be returned.
        If TC.Both is accidentally passed - an IllegalArgumentException is thrown.
        Throws:
        java.lang.IllegalArgumentException - If parameter TC openOrClose is null or TC.Both
        HTMLTokException - If the parameter String tok is not a valid HTML-tag
        SingletonException - If the token requested is a singleton (self-closing) tag, but the Tag-Criteria 'TC' parameter is requesting a closing-version of the tag.
        See Also:
        HTMLTags.hasTag(String, TC), HTMLTags.isSingleton(String)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
         if (openOrClosed == null)
             throw new NullPointerException("The value of openOrClosed cannot be null.");
        
         if (openOrClosed == TC.Both)
             throw new IllegalArgumentException("The value of openOrClosed cannot be TC.Both.");
        
         if (HTMLTags.isSingleton(tok) && (openOrClosed == TC.ClosingTags))
             throw new SingletonException(
                     "The value of openOrClosed is TC.ClosingTags, but unfortunately you have asked " +
                     "for a [" + tok + "] HTML element, which is a singleton element, and therefore " +
                     "cannot have a closing-tag instance."
                 );
        
         TagNode ret = HTMLTags.hasTag(tok, openOrClosed);
         if (ret == null)
             throw new HTMLTokException("The HTML-Tag provided isn't valid!\ntok: " + tok + "\nTC: " + openOrClosed);
         return ret;
        
      • cssClassesNOCSE

        public java.util.stream.Stream<java.lang.String> cssClassesNOCSE()
        Convenience Method. Invokes cssClasses(). Catches-Exception.
        Code:
        Exact Method Body:
        1
         try { return cssClasses(); } catch (CSSStrException e) { return Stream.empty(); }
        
      • cssClasses

        public java.util.stream.Stream<java.lang.String> cssClasses()
        Method cssClasses is to be used as a tool for splitting the results of invoking method TagNode.AV("class") into String's - separating it by white-space. The code here is a rather 'naïve' attempt to retrieve CSS classes, because it follows the 95% percent rule: 95% of the time there should be no problems! More advanced HTML Generator Tools have taken advantage of HTML's 'flexibility' and have created CSS classes that my contain anything from parenthesis '(' and ')', to semi-colons ';', to many other formats that proprietary web-developers so choose. And rather than attempting to 'predict' all future changes to CSS Class-Names, this method is intended to help developers retrieve basic (READ: 'non-preprocessor') CSS class-information. This is the nature of changing web-internet tools.

        Primarily, recognizing that dynamically, script and pre-processor generated class information, cannot be known, without having script-processing performed. This package is designed for parsing, searching, and updating HTML Pages, but it does not execute script (at least not now). Therefore, rather than returning possibly fallacious results - an exception throws when non-standard HTML CSS Classes are used, instead. This does not mean that the CSS String 'attribute-value' cannot be retrieved, but rather just that the method TagNode.getAV("class") should always work and be used instead.

        If a return value is provided it is guaranteed to be correct (unless of course script on the page would further change or modify the class). This HTML Parse-and-Search Package is highly flexible when it comes to attribute-values, in that an attribute is never checked for 'validity.'

        COUNTER-EXAMPLE: The following output was generated by a small, 8-line program that scrapes a web-page from 'Yahoo! News', and then prints out the CSS Class Information by invoking this, 'cssClasses(...)' method. Notice, that after the very fourth iteration of the loop (which iterates over instances of 'TagNode' - skipping TextNode and HTMLNode instances), the exception 'CSSStrException' is thrown. This is because Yahoo! uses something known as "CSS Pre-Processor" and "SCSS", and though the scrape-functions, themselves, cannot interpret the script-generted CSS Class-Names in this example, React-JS is able to update the class-strings (after-download, and in-the-browser) easily. The first text-box, below, contains the hilited source-code that produced the output. The next text-box contains the ouptut of this short program to the terminal-shell window.

        Counter Example:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        // Load the top-level news page for Yahoo! news
         Vector<HTMLNode> v = HTMLPage.getPageTokens(new URL("https://news.yahoo.com"), false);
        
         // Iterate through each and every "TagNode" element that contains a CSS "Class" Attribute
         for (TagNode tn : InnerTagGet.all(v, "class"))
         {
             // Print out the actual 'text' of the TagNode.  Use the "Shell.C" Unix-Colors class to print them in "Bright Red"
             System.out.println(C.BRED + tn.str + C.RESET);
        
             // Retrieve each / all / any of the CSS 'class' attributes, and print them.
             tn.cssClasses().forEach(c -> System.out.print("[" + c + "],\t"));
        
             // Before starting next TagNode class-attribute information, send a CRLF to the terminal
             System.out.println();
         }
        

        The following text was generated by the above short program, scraping Yahoo! News. It prints out relevant CSS Class Information. Notice, perhaps, the EXCEPTION which is eventually thrown is due to complicated Dynamically-Loaded CSS classes. Do keep in mind that their are simple alternatives, though.

        UNIX or DOS Shell Command:
        Downloading, Scraping HTTPS://news.yahoo.com/, Printing all TagNode Elements, and their CSS-Classes by invoking (and printing) the results of: TagNode.cssClasses() <html id="atomic" class="NoJs chrome featurephone" lang="en-US"> [nojs], [chrome], [featurephone], <div class="render-target-active render-target-default" id="render-target-default" data-reactid="2"> [render-target-active], [render-target-default], <div class="news US en-US" data-reactid="3"> [news], [us], [en-us], <div id="YDC-MainCanvas" class="YDC-MainCanvas Bgc($bg-body) Bxz(bb) Mih(100%) W(100%) Pos(a) lightweight_Miw(1247px)" data-reactid="4"> Exception in thread "main" Torello.HTML.NodeSearch.CSSStrException: One of the compare-strings passed to a search-method's var-args String parameter 'compareStr': [bgc($bg-body)] Did not pass the CSS Token-Naming Testing Regular-Expression: [^-?[_a-zA-Z]+[_\-a-zA-Z0-9]*$] And this means it has been identified as an invalid CSS Token. This is not allowed here. If you are using TagNode.cssClasses(), switch to TagNode.AV('class'). If you are using TextComparitor.CONTAINS_CSS_CLASS*, switch to TextComparitor.EQ_CI_TRM
        Returns:
        The String-Value of the Attribute-Name 'class' where that String has been 'split' - using {String.split(...)} and a Match-White-Space Regular-Expression. This is a method that will work just fine, unless proprietary non-standard HTML 5 CSS Class-Names have been used.

        Conversion-Target Stream-Method Invocation
        String[] Stream.toArray(String[]::new);
        List<String> Stream.collect(Collectors.toList());
        Vector<String> Stream.collect(Collectors.toCollection(Vector::new));
        TreeSet<String> Stream.collect(Collectors.toCollection(TreeSet::new));
        Iterator<String> Stream.iterator();

        NOTE: This method shall never return 'null' - even if there are no attribute key-value pairs contained by the TagNode. If there are strictly zero classes, Stream.empty() shall be returned, instead.
        Throws:
        CSSStrException - IMPORTANT: This exception will throw if *any* of the identified sub-String's of the CSS 'class' attributes contain non-standard characters, or do not meet the requirements of a standard CSS class name. The Regular-Expression describing a properly-formed CSS Name can be viewed on the internet, or in class CSSStrException field 'VALID_CSS_CLASS_OR_NAME_TOKEN'. This is an unchecked-RuntimeException, so beware before using this method on pages generated by different, proprietary Web-Design Tools.

        NOTE: To avoid an exception throw and retrieve the CSS 'class' attribute, regardless of whether it is standard CSS, use one of the following alternatives:

        1. TagNode.AV("class") This will return the raw-contents of the 'class' inner-tag inside 'this' HTML Element
        2. TagNode.cssClassesNOCSE(true) This invocation will retrieve the contents of a CSS 'class' inner-tag, but if there are non-standard CSS class names, or invalid CSS class names, this method will exit gracefully, and just return 'null'.
        See Also:
        cssClasses(), AV(String), StringParse.WHITE_SPACE_REGEX, CSSStrException.check(Stream)
        Code:
        Exact Method Body:
        1
        2
        3
        4
        5
        6
        7
        8
        9
         // The CSS Class is just an attribute/inner-tag within an HTML Element.
         String classes = AV("class"); 
        
         // IF the "class" attribute was not present, OR (after trimming) was empty, return "empty stream"
         if ((classes == null) || ((classes = classes.trim()).length() == 0)) return Stream.empty();
        
         // STEP 1: Split the string on white-space
         // STEP 2: Check each element of the output Stream using the "CSSStrException Checker"
         return CSSStrException.check(StringParse.WHITE_SPACE_REGEX.splitAsStream(classes));
        
      • setCSSClasses

        public TagNode setCSSClasses​(SD quote,
                                     boolean appendOrClobber,
                                     java.lang.String... cssClasses)
        This method sets the CSS Class attribute-value, applying either one, or many, CSS Class-Names.

        example
        Parameters:
        quote - It should become a habit to chose which quote to use when setting HTML TagNode attributes. Though CSS Class-Names are not allowed to contain either single or double-quotes - so it should not matter which quote is used - force of habit and consistency requires this parameter be used. This parameter may be passed 'null' - and if it is, the original quotation provided in the element will be used if there were quotations, or no-quotes will be used if the 'class' inner-tag was not defined in the element.
        appendOrClobber - When this parameter is TRUE, new class-names will be appended to any, already-existing, class-names in the TagNode 'class' attribute-value. When this parameter is FALSE, the new class-names will replace the current TagNode's 'class' attribute's value/definition. If FALSE is used, and this TagNode does not have a 'class' attribute-value assigned to it yet, a new one will be created.
        cssClasses - This var-args (String[] array, or 'list-of-String's) will accept any combination of valid CSS Class-Names. It is important to know that the elements of the input-parameter 'cssClasses' will each be checked for validity based on the standard CSS Class-Naming Rules. There is a regular-expression in the exception class CSSStrException that depicts exactly what constitutes a valid CSS Class-Name.

        NOTE: If the programmer is using a web-development tool that relies on bypassing the browser's CSS Styling Rules, and intends to dynamically update class-names (using some form of CSS pre-processor), then perhaps he/she may wish to use non-standard CSS class-names in his TagNode elements. If so, inserting non-standard names into the 'class' attribute-value of a TagNode should be done by calling the general-purpose method TagNode.setAV("class", "any-CSS-Class-String").

        The user may also use the general-purpose TagNode.appendAV("class", "any-CSS-Class-String", boolean, SD), if appending a non-standard CSS class-name String, rather than clobbering or setting it is the desired result. In this way, the programmer can avoid the "Invalid CSS Class" Exception, which would certainly be thrown for most pre-processor CSS directives.
        Returns:
        This will return an updated TagNode. TagNode's are immutable, and cannot be changed. This is identical behavior to Java's class String Libraries and Classes. The updated TagNode that is returned will have it's original 'class' attribute eliminated (if it had one), and a new 'class' attribute-value will be assigned to contain the CSS classes named by the var-args String-parameter, each separated by a space character.
        Throws:
        CSSStrException - This exception shall throw if any of the 'cssClasses' in the var-args String... parameter do not meet the HTML 5 CSS Class naming rules.
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        QuotesException - Quotation marks are not intended to be used in a class inner-tag definition. However, if there happened to be one in a non-standard class-attribute definition and the same quote is selected to wrap the updated class definition, this exception will throw. If this parameter is passed 'null', and the updated HTML Element class-attribute definition would require quotation marks, then this exception will also throw - because an unquoted attribute-value would have white-space in its definition, which is not allowed either.
        See Also:
        CSSStrException.check(String[]), CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN, appendToAV(String, String, boolean, SD), setAV(String, String, SD)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
         // Throw CSSStrException if any of the input strings are invalid CSS Class-Names.
         CSSStrException.check(cssClasses);
        
         // Build the CSS 'class' Attribute String.  This will be inserted into the TagNode Element.
         StringBuilder   sb      = new StringBuilder();
         boolean         first   = true;
        
         for (String c : cssClasses) 
         { sb.append((first ? "" : " ") + c.toLowerCase().trim()); first=false; }
        
         return appendOrClobber
             ? appendToAV("class", " " + sb.toString(), false, quote)
             : setAV("class", sb.toString(), quote);
        
      • appendCSSClass

        public TagNode appendCSSClass​(java.lang.String cssClass,
                                      SD quote)
        Adds another CSS Class Name into 'this' TagNode

        example
        Parameters:
        cssClass - This is the CSS-Class name that is being inserted into 'this' instance of TagNode
        quote - This is the quote to use for this attribute. This parameter may be null, and if it is the current-quote already in place will be used. If this parameter is null, and the "class" attribute is not defined in 'this' instance of TagNode, then no quotation mark will be used.
        Returns:
        A newly instantiated TagNode with updated CSS Class Name(s).
        Throws:
        CSSStrException - This exception shall throw if any of the 'cssClasses' in the var-args String... parameter do not meet the HTML 5 CSS Class naming rules.
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        QuotesException - Quotation marks within the provided CSS Class are not compapitable with the quotation marks used by the attribute, itself. Generally, quotations are not used with CSS Classes; however, if a proprietary-implementation requires them, it becomes necessary to heed the requirements set forth by a browser's parser.
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
         // Do a validity check on the class.  If this is "problematic" due to use of specialized / 
         // pre-processed CSS Class directives, use the general purpose "setAV(...)" method
        
         CSSStrException.check(cssClass);
        
         String curCSSClassSetting = AV("class");
        
         // If there wasn't a CSS Class already defined, use "setAV(...)", 
         // otherwise use "appendToAV(...)"
        
         if ((curCSSClassSetting == null) || (curCSSClassSetting.length() == 0))
             return setAV("class", cssClass, quote);
         else
             return appendToAV("class", cssClass + ' ', true, quote);
        
      • cssStyle

        public java.util.Properties cssStyle()
        This is a regular-expression matching for picking out all CSS Style Elements. There are myriad 'ways and means' when it comes to CSS style elements. The official line is that CSS declarations and definitions, when inside an HTML TagNode Element, 'style' attribute-names must obey a few simple rules. The declaration-name - which includes things like 'width', 'height', 'font-weight', 'border', etc... should be first. That declaration must be followed by a colon ':' character. Afterwards, the Style-Definition must either end, or a semi-color ';' must be present, and another Style-Definition may follow.

        SANITY-CHECK: At any point, an invocation of the TagNode.AV(...) method, using the parameter 'style' - as in TagNode.AV("style") - should always return the complete inline CSS-Style Attribute Value. Because of the sheer-number of variation of style-properties, this method will refrain from any type of validity-checking. So long as the rules declared by the Regular-Expression TagNode.AttrRegEx.CSS_INLINE_STYLE_REGEX are obeyed, this method will return a usable java.util.Properties instance.

        The following example will hilite what this method "cssStyle()" can achieve. The output that is generated has been transcribed to the next window (after the one below).

        Example:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        import Torello.HTML.*;
        import Torello.HTML.NodeSearch.*;
        import Torello.Java.Shell.C;
        
        import java.util.*;
        import java.io.IOException;
        import java.net.URL;
        
        public class test
        {
            public static void main(String[] argv) throws IOException
            {
                // Load a page, and parse it into a vectorized-html page.
                Vector<HTMLNode>    page        = HTMLPage.getPageTokens(new URL("https://news.yahoo.com"), false);
        
                // Select all HTMLNodes that are "instanceof" TagNode, and have a STYLE="..." attribute / inner-tag
                Vector<TagNode>     withStyle   = InnerTagGet.all(page, "style");
        
                // The 'class' Attribute on Yahoo! News is exceptionally long.  For 'readability' (here), remove them.
                // This removes all 'class' attributes in the 'withStyle' TagNode-Vector
                Attributes.remove(withStyle, "class");
        
                for (TagNode tn : withStyle)
                {
                    // retrieve all CSS Style-Properties from the TagNode
                    Properties styleProps = tn.cssStyle();
        
                    // Print out the TagNode.  Use UNIX Color-Code "bright-yellow" (for readability)
                    System.out.println(C.BYELLOW + tn + C.RESET);
        
                    String s;
        
                    // Iterate through every property key-value definition found in the "Properties" variable.
                    for (String property : styleProps.stringPropertyNames())
        
                        System.out.println(
                            "\t" +  // make output readable
                            String.format("%-" + 30 + "s", (C.BCYAN + property + C.RESET + " : ")) +
                                // Print the Property 'Name' with some 'right-padding' and use UNIX color "Bright-Cyan"
                            (s=styleProps.get(property).toString()).substring(0, Math.min(s.length(), 75))
                                // For the Property "value" - only print the first 75 characters...
                        );
                }
            }
        }
        

        TThe following text was generated by the above short program, scraping Yahoo! News. It prints out relevant CSS Style Information. Each CSS Attribute that has been defined within-the-confines of the Element itself (via the 'STYLE' inner-tag) is printed out to the UNIX terminal.

        UNIX or DOS Shell Command:
        <div style="height:86px;" data-reactid="5" id="YDC-UH"> height : 86px <div style="background-color:transparent;" data-reactid="8"> background-color : transparent <input autocomplete="off" style="-webkit-appearance:none;" name="p" data-reactid="19" aria-label="Search" autocorrect="off" type="text" placeholder="Search" value="" autocapitalize="off" /> -webkit-appearance : none <button style="filter:chroma(color=#000);" type="submit" aria-label="Search" data-reactid="22" id="search-button"> filter : chroma(color=#000) <svg height="34" viewbox="0 0 48 48" width="34" style="fill:#400090;stroke:#400090;stroke-width:0;vertical-align:bottom;" data-icon="profile" data-reactid="27"> stroke-width : 0 vertical-align : bottom fill : #400090 stroke : #400090 <svg height="35" viewbox="0 0 512 512" width="30" style="fill:#400090;stroke:#400090;stroke-width:0;vertical-align:bottom;" data-icon="NavMail" data-reactid="31"> stroke-width : 0 vertical-align : bottom fill : #400090 stroke : #400090 <nav style="width:auto;" data-reactid="14" role="navigation"> width : auto <svg height="20" viewbox="0 0 96 96" width="20" style="vertical-align:middle;cursor:pointer;margin-top:-2px;color:#26282a;fill:#26282a;stroke:#26282a;stroke-width:0;" data-icon="StreamShare" data-reactid="51"> color : #26282a vertical-align : middle stroke-width : 0 margin-top : -2px fill : #26282a stroke : #26282a cursor : pointer <div id="defaultdestMAST" style=""> <div id="defaultdestLDRB" style=""> <div style="position:relative;top:0px;" data-reactid="22"> position : relative top : 0px <div style="max-width:900px;" data-reactid="2" id="YDC-Stream"> max-width : 900px <div tabindex="-1" style="padding-bottom:56.3%;" data-reactid="9"> padding-bottom : 56.3% <div style="-webkit-tap-highlight-color:transparent;" data-reactid="21"> -webkit-tap-highlight-color : transparent <div style="margin:-10px;" data-reactid="22"> margin : -10px <button style="padding:10px;" aria-label="Share Menu" data-reactid="23"> padding : 10px <svg height="20" viewbox="0 0 48 48" width="20" style="fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="share" data-reactid="24"> stroke-width : 0 vertical-align : bottom fill : #fff stroke : #fff <svg height="20" viewbox="0 0 16 16" width="20" style="position:relative;top:1px;margin:auto;vertical-align:middle;fill:#fff;stroke:#fff;stroke-width:0;" data-icon="comments" data-reactid="27"> position : relative vertical-align : middle stroke-width : 0 margin : auto top : 1px fill : #fff stroke : #fff <ul style="top:0;transition:top .4s;" data-reactid="30"> top : 0 transition : top .4s <div data-test-locator="streamhero-controls" style="height:26px;" data-reactid="49"> height : 26px <svg height="23" viewbox="0 0 48 48" width="23" style="margin-left:12px;opacity:0.6;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="caret-up" data-reactid="52"> margin-left : 12px opacity : 0.6 stroke-width : 0 vertical-align : bottom fill : #fff stroke : #fff <svg height="23" viewbox="0 0 48 48" width="23" style="margin-left:12px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="caret-down" data-reactid="55"> margin-left : 12px stroke-width : 0 vertical-align : bottom fill : #fff stroke : #fff <div style="padding-bottom:56%;" data-reactid="61"> padding-bottom : 56% <div style="padding-bottom:88%;" data-reactid="75"> padding-bottom : 88% <div style="padding-bottom:56%;" data-reactid="113"> padding-bottom : 56% <div style="padding-bottom:88%;" data-reactid="127"> padding-bottom : 88% <div style="padding-bottom:88%;" data-reactid="154"> padding-bottom : 88% <div style="padding-bottom:56%;" data-reactid="181"> padding-bottom : 56% <div style="padding-bottom:88%;" data-reactid="195"> padding-bottom : 88% <div style="padding-bottom:88%;" data-reactid="222"> padding-bottom : 88% <div style="padding-bottom:88%;" data-reactid="248"> padding-bottom : 88% <div style="padding-bottom:56%;" data-reactid="275"> padding-bottom : 56% <div style="padding-bottom:56%;" data-reactid="289"> padding-bottom : 56% <div style="padding-bottom:56%;" data-reactid="303"> padding-bottom : 56% <div style="padding-bottom:88%;" data-reactid="317"> padding-bottom : 88% <div style="padding-bottom:88%;" data-reactid="344"> padding-bottom : 88% <div style="padding-bottom:88%;" data-reactid="371"> padding-bottom : 88% <div style="padding-bottom:56%;" data-reactid="398"> padding-bottom : 56% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="400"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="401"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:56%;" data-reactid="415"> padding-bottom : 56% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="417"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 48 48" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="gallery" data-reactid="418"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:88%;" data-reactid="432"> padding-bottom : 88% <div style="padding-bottom:88%;" data-reactid="459"> padding-bottom : 88% <div style="padding-bottom:56%;" data-reactid="486"> padding-bottom : 56% <div style="padding-bottom:88%;" data-reactid="500"> padding-bottom : 88% <div style="padding-bottom:56%;" data-reactid="527"> padding-bottom : 56% <div style="padding-bottom:56%;" data-reactid="541"> padding-bottom : 56% <div style="padding-bottom:56%;" data-reactid="555"> padding-bottom : 56% <div style="height:0;width:0;" data-reactid="3" id="defaultFOOT-sizer"> height : 0 width : 0 <div id="defaultdestFOOT" style=""> <div style="height:0;width:0;" data-reactid="3" id="defaultFSRVY-sizer"> height : 0 width : 0 <div id="defaultdestFSRVY" style=""> <div style="position:relative;top:0px;" data-reactid="39"> position : relative top : 0px <div id="defaultdestLREC" style=""> <div style="max-width:900px;" data-reactid="2" id="Col2-1-Stream"> max-width : 900px <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/zHVKAUu2r5TTwV6qakrkGA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/2UIXIIqm4LTnxZg0X5i.yA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2020-04/6f703ff0-7d04-11ea-beef-801a4e71e676);" data-reactid="10"> background-image : url(https://s.yimg.com/ny/api/res/1.2/zHVKAUu2r5TTwV6qakrkGA--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/Ys5hJWbOLmM_KAo5BwK0BQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/gqSAvUFANziiTJbCyeJmqw--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/os/creatr-images/2019-11/38e0d390-fcd6-11e9-9dbf-19fe4fa04817);" data-reactid="22"> background-image : url(https://s.yimg.com/ny/api/res/1.2/Ys5hJWbOLmM_KAo5BwK0BQ--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/G5wdl.JqIzh2ahAdWE.1tA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/4TK6IvbcM6B2WT8aN60UKg--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-US/reuters-finance.com/6203123d7024cdfb221604ba4a7b8aa0);" data-reactid="34"> background-image : url(https://s.yimg.com/ny/api/res/1.2/G5wdl.JqIzh2ahAdWE.1tA--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/5fhqXTevZUHU.nbsEW7dpQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/UI0jsUElQl1vdfCIvRVrJg--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-us/insidermonkey.com/f32ccf5bbd02501b27ef965a1a7aad26);" data-reactid="46"> background-image : url(https://s.yimg.com/ny/api/res/1.2/5fhqXTevZUHU.nbsEW7dpQ--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/a42nb3icSWNkNKgdFIe4Rg--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/bddUizWuPmPkXWok0myBOg--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/os/creatr-uploaded-images/2020-04/f1a6a890-7b76-11ea-b7ab-69a6e125abb4);" data-reactid="58"> background-image : url(https://s.yimg.com/ny/api/res/1.2/a42nb3icSWNkNKgdFIe4Rg--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/y9Bfyy_r5d0fClE.ibKusQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/ua9rm3_4SLLffTmAoSvmvA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/uu/api/res/1.2/VDzkhWpF77whaWcOA1yXPA--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e937918b43fec6780d02a0f/5e937918b43fec6780d02a10_o_U_v2.jpg);" data-reactid="70"> background-image : url(https://s.yimg.com/ny/api/res/1.2/y9Bfyy_r5d0fClE.ibKusQ--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="71"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="72"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/QVFAZDpObZQbly2hVJFTtQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/oyvxAW4tgdtJIKnCZL29wQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2020-04/991c2290-79b9-11ea-be7f-c79a30d5ee49);" data-reactid="85"> background-image : url(https://s.yimg.com/ny/api/res/1.2/QVFAZDpObZQbly2hVJFTtQ--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/EX.pCQqbU3p6B6WUj.NaaA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/hKCLVgBst8AbLJPMxG5jgQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2020-04/f337b140-7aa4-11ea-a7bd-b72f2d3182a5);" data-reactid="97"> background-image : url(https://s.yimg.com/ny/api/res/1.2/EX.pCQqbU3p6B6WUj.NaaA--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/CwPc0kK56oBN0mhFxrRieA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/M5DLYlWDOnTYEM0vNum_ow--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-US/reuters.com/637ba4f5a27bbf8ff710aff35be65228);" data-reactid="109"> background-image : url(https://s.yimg.com/ny/api/res/1.2/CwPc0kK56oBN0mhFxrRieA--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/Q.id3lA_2RVHUipYYwwsIA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/7I4Kuij80NUX0cESxgrbyA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/os/creatr-images/2020-04/89c52ea0-7c15-11ea-a77d-36d3c609ef8a);" data-reactid="121"> background-image : url(https://s.yimg.com/ny/api/res/1.2/Q.id3lA_2RVHUipYYwwsIA--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/ePSJEsrMO0RGuQ4zmFlz0w--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/xaPl5Hb_YfC7WgER0wse9Q--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/uu/api/res/1.2/hQHVFPzBzVVtEbOx2hMNqg--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e93679884a08c660eef5be7/5e93679884a08c660eef5be8_o_U_v2.jpg);" data-reactid="133"> background-image : url(https://s.yimg.com/ny/api/res/1.2/ePSJEsrMO0RGuQ4zmFlz0w--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="134"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="135"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/mRRPKIyX0R7oo73K8vfstw--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/8ge4QvIM3jGA1QvKW0fZTw--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-US/reuters-finance.com/601f4a3964811a269aca0168f2f7c0f0);" data-reactid="148"> background-image : url(https://s.yimg.com/ny/api/res/1.2/mRRPKIyX0R7oo73K8vfstw--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/N9h9d6z6dhQzREy.jTeqKQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/0SF8UZWrj81i6jkuKcScJQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2020-04/b97691d0-784a-11ea-b7fe-3877f2dd45e0);" data-reactid="160"> background-image : url(https://s.yimg.com/ny/api/res/1.2/N9h9d6z6dhQzREy.jTeqKQ--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/19PqKm5gqlAMpJyAvSVRxg--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/gdmNOvRuw_vkH8Z.3ldbQw--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en/Benzinga/af3b980f0214633c97c8d1f5f6b8d8e2);" data-reactid="172"> background-image : url(https://s.yimg.com/ny/api/res/1.2/19PqKm5gqlAMpJyAvSVRxg--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/i047mKFU5JlnMz5secHo5w--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/igypgBdqYfdqBDzF63ucUQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/os/creatr-images/2020-04/b8c9afa0-7ce2-11ea-be7d-2d4971ed799b);" data-reactid="184"> background-image : url(https://s.yimg.com/ny/api/res/1.2/i047mKFU5JlnMz5secHo5w--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/bznBrHaGLHNUpRfgWb6fiA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/RMNQiT657O98l17qpLRdHA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/uu/api/res/1.2/7zVY3vVQ9.6pXGCkzSopig--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e93679484a08c660eef5be5/5e93679484a08c660eef5be6_o_U_v2.jpg);" data-reactid="196"> background-image : url(https://s.yimg.com/ny/api/res/1.2/bznBrHaGLHNUpRfgWb6fiA--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="197"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="198"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/M44IU2wp144myS9tImV5Bw--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/P7Pp2DyLpAD70AwYWF0H1A--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-us/kiplinger.com/d4bcd9f2a6ca8fde407b4053ec26eb2f);" data-reactid="211"> background-image : url(https://s.yimg.com/ny/api/res/1.2/M44IU2wp144myS9tImV5Bw--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/yMDGmcU5O6POWraLEe9QCg--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/iXVvN_KBUDNiisYS9nwhyA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/09/5e8e0ed96ca6be6d5fb31241/5e8e799e549920624eefd3b9_o_U_v3.jpg);" data-reactid="223"> background-image : url(https://s.yimg.com/ny/api/res/1.2/yMDGmcU5O6POWraLEe9QCg--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="224"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="225"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/uSq36UD_AsyJ6WberW8Ggw--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/Wa_XWc8qgTfYTG.smVQl4g--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/hd/cp-video-transcode/prod/2020-03/26/5e7cfc135f6f6542b5f1b0b0/5e7d0b6a6ca6be4217587c49_o_U_v4.jpg);" data-reactid="238"> background-image : url(https://s.yimg.com/ny/api/res/1.2/uSq36UD_AsyJ6WberW8Ggw--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="239"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="240"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/apIwx3T.jLSqpz3t6UNKgQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/wTkDXBHN2KmyTuC5OUdylA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/os/creatr-uploaded-images/2020-04/8e406100-7cda-11ea-a7fd-1c1af5d5aef1);" data-reactid="253"> background-image : url(https://s.yimg.com/ny/api/res/1.2/apIwx3T.jLSqpz3t6UNKgQ--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/yZpN1gdQt6vdqr3kPuAQ3g--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/QFEsHBuvrVZCGcF6iUAypg--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/uu/api/res/1.2/Q0NsiY9KtIUJhPYY_P6opw--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e935f4292fc28752b7df245/5e935f4525ffdb000102745c_1280x720_FES_v1.jpg);" data-reactid="265"> background-image : url(https://s.yimg.com/ny/api/res/1.2/yZpN1gdQt6vdqr3kPuAQ3g--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="266"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="267"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/5kA1NwyfUsX2ayNbkc4.bw--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/EnKSPWKjn4VFfYlO7bZ3KA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-us/usa_today_money_325/6e7e2347716edec37842707988d62d02);" data-reactid="280"> background-image : url(https://s.yimg.com/ny/api/res/1.2/5kA1NwyfUsX2ayNbkc4.bw--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/j7U2HxAm1fBqgijDZQ_G6A--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/IIiNwcYu3W1X_c5r2NryvQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-us/kiplinger.com/852b73caf9727bab7320b50cd15c4adf);" data-reactid="292"> background-image : url(https://s.yimg.com/ny/api/res/1.2/j7U2HxAm1fBqgijDZQ_G6A--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/uHKZoXXRPlg3I0SJ.Sd7SA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/4iDa4lNkS3R_BZBueOsd7g--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2020-03/910edf60-705a-11ea-8b77-8ac3eef493cf);" data-reactid="304"> background-image : url(https://s.yimg.com/ny/api/res/1.2/uHKZoXXRPlg3I0SJ.Sd7SA--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/5YLdKjYKMGL0UTdt.TvcwA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/pt.rGJPR5grq4xye.U9uwQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-us/usa_today_money_325/fe2532294f100b45f097dd6e7bb17be7);" data-reactid="316"> background-image : url(https://s.yimg.com/ny/api/res/1.2/5YLdKjYKMGL0UTdt.TvcwA--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/vE19PDNIRyV3iCxoLhMX5A--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/l.DYogf.Ixzc8XTQ.4.9Kw--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/03/5e879dce5f6f65601e7e592a/5e879dce5f6f65601e7e592b_o_U_v2.jpg);" data-reactid="328"> background-image : url(https://s.yimg.com/ny/api/res/1.2/vE19PDNIRyV3iCxoLhMX5A--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="329"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="330"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/eYIUwigcXI9U4rBnTiyNsA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/TLJkSQf78GxPiW.aGhKwrw--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/uu/api/res/1.2/kdrZPJoCx_t5tcxtWPWpEQ--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e93555a90a80d79a635231b/5e93555a90a80d79a635231c_o_U_v2.jpg);" data-reactid="343"> background-image : url(https://s.yimg.com/ny/api/res/1.2/eYIUwigcXI9U4rBnTiyNsA--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="344"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="345"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/qb6TUw7SrnCAr9HIPjnmjQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/hKr9PAu0Pe5YralkmbEAHQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en/fx_empire_176/7487ed84f0b1640fcfe8e7b1872a1881);" data-reactid="358"> background-image : url(https://s.yimg.com/ny/api/res/1.2/qb6TUw7SrnCAr9HIPjnmjQ--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/Z9lqib01GToEG0diuwJQVg--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/yURaMJJjbFn.vHqiJjyWdg--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-images/2020-01/eccde4d0-43a6-11ea-bb75-e378299e78d9);" data-reactid="370"> background-image : url(https://s.yimg.com/ny/api/res/1.2/Z9lqib01GToEG0diuwJQVg--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/F7Kzs5KAyEiqSQEw1lMA0w--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/BQhLWVPcIJZu0E_8.S0svA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en/fx_empire_176/f9c062884e57ad93391eb3b680a419b9);" data-reactid="382"> background-image : url(https://s.yimg.com/ny/api/res/1.2/F7Kzs5KAyEiqSQEw1lMA0w--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/36NzXnShtJF9otBOdCqX2w--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/3nrr93yHkmoalfbGb9rsSA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s1.yimg.com/uu/api/res/1.2/cXtj3GsNYMhGTr0c5QBq9Q--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e93465e549920624eefde60/5e9346623260720001fd89bc_1280x720_FES_v1.jpg);" data-reactid="394"> background-image : url(https://s.yimg.com/ny/api/res/1.2/36NzXnShtJF9otBOdCqX2w--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="395"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="396"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/7jUts.to8VGXohMjcw2c0A--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/L4Trp2E6Gld.gpEE8sbrGQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/hd/cp-video-transcode/prod/2020-03/24/5e7a3e11da8e831cd730e3da/5e7a3e11da8e831cd730e3db_o_U_v2.jpg);" data-reactid="409"> background-image : url(https://s.yimg.com/ny/api/res/1.2/7jUts.to8VGXohMjcw2c0A--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="410"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="411"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/uPL33Yks5vrekTM0apCTOg--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/WGPamrfVQ3HzXf2AQ6DipQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-us/usa_today_money_325/9f41ba2e34b0dfb012e3e38b461bdff5);" data-reactid="424"> background-image : url(https://s.yimg.com/ny/api/res/1.2/uPL33Yks5vrekTM0apCTOg--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/haQWWFistKffxTQb.LI8fA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l2.yimg.com/uu/api/res/1.2/C3aCT3_5CjlznKD38Nx_jA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media-mbst-pub-ue1.s3.amazonaws.com/creatr-uploaded-images/2019-09/1af93af0-db18-11e9-bff7-e34690b9be52);" data-reactid="436"> background-image : url(https://s.yimg.com/ny/api/res/1.2/haQWWFistKffxTQb.LI8fA--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="437"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="438"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/B__47T2HNVGCd8y4oJ1Sww--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/LXPYkIG9XVOhLwFXsRKYTA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/uu/api/res/1.2/b7F3OOxxajxDuM_JODhS3w--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e93397b9bb2c06b40c89e33/5e93397b9bb2c06b40c89e34_o_U_v2.jpg);" data-reactid="451"> background-image : url(https://s.yimg.com/ny/api/res/1.2/B__47T2HNVGCd8y4oJ1Sww--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="452"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="453"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/Uo5MF7aeN.kqQZBectYv6Q--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/L0huYimDIMz4wbWlMdb_1Q--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en-US/the_block_83/dfc4cd7cac9a2413be149f9d69d34c6d);" data-reactid="466"> background-image : url(https://s.yimg.com/ny/api/res/1.2/Uo5MF7aeN.kqQZBectYv6Q--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/ackt9j5fZ48Wwcv8ZFExWg--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l.yimg.com/uu/api/res/1.2/at5cquxDMds8sMRsc_NRsA--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/hd/cp-video-transcode/prod/2020-03/25/5e7b5d75da8e831cd730e6bd/5e7b5d75da8e831cd730e6be_o_U_v2.jpg);" data-reactid="478"> background-image : url(https://s.yimg.com/ny/api/res/1.2/ackt9j5fZ48Wwcv8ZFExWg--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="479"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="480"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/x3lcnEBxqUd6XfpTruJHbw--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/fq3eSPTj3sVy5009DEM3ug--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://media.zenfs.com/en/bloomberg_opinion_268/3305b3219b8ca4274de1ce2d105a4665);" data-reactid="493"> background-image : url(https://s.yimg.com/ny/api/res/1.2/x3lcnEBxqUd6XfpTruJHbw--~A/YXBwaWQ9aG padding-bottom : 52% <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/amaw5PNz2O5GGD2ylfgAqA--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/nTGrYvbX9LdQZop5GNlb2A--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/uu/api/res/1.2/aOJYFJ3Aouv7clfF0BcVSQ--~B/dz0xMjgwO2g9NzIwO2FwcGlkPXl0YWNoeW9u/https://s.yimg.com/hd/cp-video-transcode/prod/2020-04/12/5e932c7284a08c660eef5b98/5e932c7b25ffdb000102713c_1280x720_FES_v1.jpg);" data-reactid="505"> background-image : url(https://s.yimg.com/ny/api/res/1.2/amaw5PNz2O5GGD2ylfgAqA--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="506"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="507"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div style="padding-bottom:52%;background-image:url(https://s.yimg.com/ny/api/res/1.2/wtFH3BocnUekbSQZLOEcAQ--~A/YXBwaWQ9aGlnaGxhbmRlcjtoPTMxMjt3PTYwMDtxPTc1O2ZpPXN0cmlt/http://l1.yimg.com/uu/api/res/1.2/7geayxyFnaPr5ozhWsIWqQ--/YXBwaWQ9eXRhY2h5b247cT03NTs-/https://s.yimg.com/hd/cp-video-transcode/prod/2020-03/23/5e78faffcbde734796205b1e/5e7916f969e6c577d70ae714_o_U_v4.jpg);" data-reactid="520"> background-image : url(https://s.yimg.com/ny/api/res/1.2/wtFH3BocnUekbSQZLOEcAQ--~A/YXBwaWQ9aG padding-bottom : 52% <span dir="ltr" style="height:50px;width:50px;background-size:50px;background-image:url(https://s.yimg.com/dh/ap/default/150604/orb.png);" data-reactid="521"> height : 50px background-image : url(https://s.yimg.com/dh/ap/default/150604/orb.png) background-size : 50px width : 50px <svg height="20" viewbox="0 0 512 512" width="20" style="left:15px;top:15px;fill:#fff;stroke:#fff;stroke-width:0;vertical-align:bottom;" data-icon="CorePlay" data-reactid="522"> stroke-width : 0 vertical-align : bottom left : 15px top : 15px fill : #fff stroke : #fff <div id="defaultdestLREC2" style="">
        Returns:
        This shall return an instance of Java's 'Properties' class, and it will contain all the style definitions and their values.

        NOTE: All style-declaration names shall be saved using lower-case. The style attribute-values, however, will not be converted to lower-case.

        NULL NOTE: This method will not return null. Rather, if 'this' instance of TagNode Element does not have a style attribute, an empty (non-null) instance of java.util.Properties which contains zero CSS Style-Declaration Key-Value Pairs will be returned instead.
        See Also:
        TagNode.AttrRegEx.CSS_INLINE_STYLE_REGEX, AV(String)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
         Properties  p           = new Properties();
         String      styleStr    = AV("style");
             // Returns the complete attribute-value of "style" in the TagNode
        
         if (styleStr == null)           return p;
             // There was no STYLE='...' attribute found, return empty Properties.
        
         styleStr = styleStr.trim();
             // Standard String-trim routine
        
         if (styleStr.length() == 0)     return p;
        
         Matcher m = AttrRegEx.CSS_INLINE_STYLE_REGEX.matcher(styleStr);
        
             // This reg-ex "iterates" over matches of strings that follow the (very basic) form:
             // declaration-name: declaration-string;
             //
             // Where the ":" (color), and ";" (semi-colon) are the only watched/monitored tokens.
             // See the reg-ex definition in "See Also" tag.
        
         while (m.find()) p.put(m.group(1).toLowerCase(), m.group(2));
        
             // m.group(1): declaration-name     (stuff before the ":")
             // m.group(2): declaration-string   (stuff before the next ";", or end-of-string)
             // 
             // For Example, if the style attribute-value was specified as:
             // STYLE="font-weight: bold;   margin: 1em 1em 1em 2em;   color: #0000FF"
             //
             // The returned Properties object would contain the string key-value pair elements:
             // "font-weight"    -> "bold"
             // "margin"         -> "1em 1em 1em 2em"
             // "color"          -> "#0000FF"
        
         return p;
        
      • setCSSStyle

        public TagNode setCSSStyle​(java.util.Properties p,
                                   SD quote,
                                   boolean appendOrClobber)
        This will set 'this' instance of TagNode's CSS-Style Attribute field such that it contains the CSS Definition Property-Value Pairs that are contained in the java.util.Properties input-parameter.
        Parameters:
        p - This should be a map between CSS Definition Property-Names to Property-Values. Some examples would include (below):
        Property-NameProperty-Value
        font-weightbold
        backgroundlightyellow
        width200px
        height20em
        border5px 10px 5px 15px
        box-shadowinset 0 0 1em brown
        quote - It is expected that the user will decide which quote to use - SD.SingleQuotes or SD.DoubleQuotes - when building the new TagNode Element.
        appendOrClobber - When this parameter is TRUE new style-definitions will be appended to any, already-existing, style-definitions in the TagNode 'style' attribute-value. When this parameter is FALSE, the new style-definitions will replace the HTML Element 'style' attributes current-definition (or build a new one, if 'this' TagNode does not have a 'style' attribute).
        Returns:
        This will return a new, updated HTML TagNode element with the new values inserted (or replaced). The class TagNode is immutable - as it is mostly a wrapper on a Java-String.
        Throws:
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        CSSStrException - If there is an invalid CSS Style Property Name.
        QuotesException - If the style-element's quotation marks are incompatible with any and all quotation marks employed by the style-element definitions.
        See Also:
        CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN, appendToAV(String, String, boolean, SD), setAV(String, String, SD)
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
         // this is used in the "exception constructor" below (which means, it might not be used).
         int counter = 0;
        
         // Follows the "FAIL-FAST" philosophy, and does not allow invalid CSS declaration-name tokens.
         // Use TagNode.setAV("class", ...), or TagNode.appendToAV("class", ...), to bypass
         // exception-check.
         for (String key : p.stringPropertyNames())
             if (! CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN.matcher(key).find())
             {
                 String[] keyArr = new String[p.size()];
                 throw new CSSStrException(
                     "CSS Style Definition Property: [" + key + "] does not conform to the valid, " +
                     "HTML 5, regular-expression for CSS Style Definitions Properties:\n[" + 
                     CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN.pattern() + "].",
                     p.stringPropertyNames().toArray(keyArr), ++counter
                     // One minor "PRESUMPTION" that is the Iterator will return the elements of 
                     // Properties in the EXACT SAME ORDER on both creations / instantiations of the
                     // iterator.  Specifically: two invocations of method .iterator(), will return the
                     // same-list of property-keys, in the same order, BOTH TIMES.  Once for the
                     // for-loop, and once for the exception message.  This only matters if there is
                     // an exception.
                 );
             }
             else ++counter; 
        
         // Follows the "FAIL-FAST" philosophy, and does not allow "quotes-within-quote" problems to occur.
         // An attribute-value surrounded by single-quotes, cannot contain a single-quote inside, and
         // double-within-double.
         counter = 0;
        
         for (String key : p.stringPropertyNames())
             if (StrCmpr.containsOR(p.get(key).toString(), ("" + quote.quote), ";"))
             {
                 String[] keyArr = new String[p.size()];
                 throw new CSSStrException(
                     "CSS Style Definition Property: [" + key + "], which maps to style-definition " +
                     "property-value:\n[" + p.get(key) + "], contains either a semi-colon ';' " +
                     "character, or the same quotation-mark specified: [" + quote.quote + "], and is " +
                     "therefore not a valid assignment for a CSS Definition Property.",
                     p   .stringPropertyNames()
                         .stream()
                         .map((String propertyName) -> p.get(propertyName))
                         .toArray((int i) -> new String[i]),
                     ++counter
                 );
             }
             else ++counter;
        
         // ERROR-CHECKING: FINISHED, NOW JUST BUILD THE ATTRIBUTE-VALUE STRING (using StringBuilder),
         // AND INSERT IT.
         StringBuilder sb = new StringBuilder(); 
         for (String key : p.stringPropertyNames()) sb.append(key + ": " + p.get(key) + ";");
        
         return appendOrClobber
             ? appendToAV("style", " " + sb.toString(), false, quote)
             : setAV("style", sb.toString(), quote);
        
      • getID

        public java.lang.String getID()
        Convenience Method. Invokes AV(String).

        Uses String "id", the CSS-ID attribute-name.

        example
        Code:
        Exact Method Body:
        1
         return AV("id").trim();
        
      • setID

        public TagNode setID​(java.lang.String id,
                             SD quote)
        This merely sets the current CSS 'ID' Attribute Value.
        Parameters:
        id - This is the new CSS 'ID' attribute-value that the user would like applied to 'this' instance of TagNode.
        quote - All operations which involve modifying an HTML attribute key-value pair are expected to choose between the SD.SingleQuotes or SD.DoubleQuotes option. This can help avoid quotes-within-quotes problems, as HTML does not provide a way to escape quotes in attribute-value definitions.
        Returns:
        Returns a new instance of TagNode that has an updated 'ID' attribute-value.
        Throws:
        java.lang.IllegalArgumentException - This exception shall throw if an invalid String-token has been passed to parameter 'id'.

        BYPASS NOTE: If the user would like to bypass this exception-check, for instance because he / she is using a CSS Pre-Processor, then applying the general-purpose method TagNode.setAV("id", "some-new-id") ought to suffice. This other method will not apply validity checking, beyond scanning for the usual "quotes-within-quotes" problems, which is always disallowed.
        ClosingTagNodeException - This exception will throw if an attempt is made to invoke this method on a TagNode whose 'isClosing' boolean-field is set to TRUE. The exception throws because HTML browsers do not permit the closing-versions of any TagNode to contain inner-tags (attributes).
        See Also:
        setAV(String, String, SD), CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN, setAV(String, String, SD)
        Code:
        Exact Method Body:
        1
        2
        3
        4
        5
        6
        7
        8
         if (! CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN.matcher(id).find())
             throw new IllegalArgumentException(
                 "The id parameter provide: [" + id + "], does not conform to the standard CSS " +
                 "Names.\nEither try using the generic TagNode.setAV(\"id\", yourNewId, quote); " +
                 "method to bypass this check, or change the value passed to the 'id' parameter here."
             );
        
         return setAV("id", id.trim(), quote);
        
      • dataAV

        public java.lang.String dataAV​(java.lang.String dataName)
        Convenience Method. Invokes AV(String) with "data-" prepended to 'dataName'
        Code:
        Exact Method Body:
        1
         return AV("data-" + dataName);
        
      • removeDataAttributes

        public TagNode removeDataAttributes()
        This method will remove any HTML 'data' Attributes - if there are any present. "Data Inner-Tags" are simply the attributes inside of HTML Elements whose names begin with "data-".

        Since class TagNode is immutable, a new TagNode must be instantiated, if any data-inner-tags are removed. If no data-attributes are removed, 'this' instance TagNode shall be returned instead.
        Returns:
        This will return a newly constructed 'TagNode' instance, if there were any "Data Attributes" that were removed by request. If the original TagNode has remained unchanged, a reference to 'this' shall be returned.
        Throws:
        ClosingTagNodeException - This exception throws if 'this' instance of TagNode is a closing-version of the HTML Element. Closing HTML Elements may not have data attributes, because they simply are not intended to contain any attributes.
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
         // Because this method expects to modify the TagNode, this exception-check is necessary.
         ClosingTagNodeException.check(this);
        
         // Make sure to keep the quotes that were already used, we are removing attributes, and the
         // original attributes that aren't removed need to preserve their quotation marks.
         Properties          p       = this.allAV(true, true);
         Enumeration<Object> keys    = p.keys();
         int                 oldSize = p.size();
        
         // Visit each Property Element, and remove any properties that have key-names which
         // begin with the word "data-"
         while (keys.hasMoreElements())
         {
             String key = keys.nextElement().toString();
             if (key.startsWith("data-")) p.remove(key);
         }
        
         // If any of properties were removed, we have to rebuild the TagNode, and replace it.
         // REMEMBER: 'null' is passed to the 'SD' parameter, because we preserved the original
         //           quotes above.
         return (p.size() == oldSize) ? this : new TagNode(this.tok, p, null, this.str.endsWith("/>"));
        
      • getDataAV

        public java.util.Properties getDataAV()
        Convenience Method. Invokes getDataAV(boolean).

        Attribute-names will be in lower-case.
        Code:
        Exact Method Body:
        1
         return getDataAV(false);
        
      • getDataAV

        public java.util.Properties getDataAV​(boolean preserveKeysCase)
        This will retrieve and return any/all 'data' HTML Attributes. "Data Inner-Tags" are simply the attributes inside of HTML Elements whose names begin with "data-".
        Parameters:
        preserveKeysCase - When this parameter is passed TRUE, the case of the attribute names in the returned Properties table will have been preserved. When FALSE is passed, all Properties keys shall have been converted to lower-case first.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        Returns:
        This will return a java.util.Properties of all data-attributes which are found in 'this' HTML Element. If no such attributes were found, 'null' shall not be returned by this method, but rather an empty Properties instance will be provided, instead.
        See Also:
        isClosing, HTMLNode.str, tok, TagNode.AttrRegEx.DATA_ATTRIBUTE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
         Properties ret = new Properties();
        
         // NOTE: OPTIMIZED, "closing-versions" of the TagNode, and TagNode's whose 'str'
         //       field is only longer than the token, itself, by 3 or less characters cannot have
         //       attributes.v In that case, just return an empty 'Properties' instance.
         if (isClosing || (str.length() <= (tok.length() + 3))) return ret;
        
         // This RegEx Matcher 'matches' against Attribute/InnerTag Key-Value Pairs
         //      ONLY PAIRS WHOSE KEY BEGINS WITH "data-" WILL MATCH
         // m.group(2): returns the 'key' portion of the key-value pair, before an '=' (equals-sign).
         // m.group(3): returns the 'value' portion of the key-value pair, after an '='
         Matcher m = AttrRegEx.DATA_ATTRIBUTE_REGEX.matcher(this.str);
        
         // NOTE: HTML mandates attributes must be 'case-insensitive' to the attribute 'key-part'
         //      (but not necessarily the 'value-part')
         // HOWEVER: Java does not require anything for the 'Properties' class.
         // ALSO: Case is PRESERVED for the 'value-part' of the key-value pair.
         if (preserveKeysCase)
             while (m.find())
                 ret.put(m.group(2), StringParse.ifQuotesStripQuotes(m.group(3)));
         else
             while (m.find())
                 ret.put(m.group(2).toLowerCase(), StringParse.ifQuotesStripQuotes(m.group(3)));
         
         return ret;
        
      • getDataAN

        public java.util.stream.Stream<java.lang.String> getDataAN()
        Convenience Method. Invokes getDataAN(boolean).

        Attribute-names will be in lower-case.
        Code:
        Exact Method Body:
        1
         return getDataAN(false);
        
      • getDataAN

        public java.util.stream.Stream<java.lang.String> getDataAN​
                    (boolean preserveKeysCase)
        
        This method will only return a list of all data-attribute names. The data-attribute values shall not be included in the result. An HTML Element "data-attribute" is any attribute inside of an HTML TagNode whose key-value pair uses a key that begins with "data-"... It is that simple!
        Parameters:
        preserveKeysCase - When this parameter is passed TRUE, the case of the attribute names that are returned by this method will have been preserved. When FALSE is passed, they shall be converted to lower-case first.

        HTML expects that all attribute names function in a CASE INSENSITIVE fashion. Java, however, makes no such expectations on it's class java.util.Properties tables, or most other String-containers. Preserving the case of an attribute-name will produce more consistent looking HTML. But for more easily-searchable java-Strings, converting to lower-case can make coding easier.
        Returns:
        Returns an instance of Stream<String>. The attribute names that are returned, are all converted to lower-case.

        A return type of Stream<String> is used. Please see the list below for how to convert a Stream to another data-type.

        Conversion-Target Stream-Method Invocation
        String[] Stream.toArray(String[]::new);
        List<String> Stream.collect(Collectors.toList());
        Vector<String> Stream.collect(Collectors.toCollection(Vector::new));
        TreeSet<String> Stream.collect(Collectors.toCollection(TreeSet::new));
        Iterator<String> Stream.iterator();

        NOTE: This method shall never return 'null' - even if there are no data-attribute key-value pairs contained by the TagNode. If there are strictly zero such attributes, (Stream.empty()) shall be returned, instead.
        See Also:
        HTMLNode.str, tok, TagNode.AttrRegEx.DATA_ATTRIBUTE_REGEX
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
         // Java Stream's can be quickly and easily converted to any data-structure the user needs.
         Stream.Builder<String> b = Stream.builder();
        
         // NOTE: OPTIMIZED, "closing-versions" of the TagNode, and TagNode's whose 'str'
         //       field is only longer than the token, itself, by 3 or less characters cannot have
         //       attributes.  In that case, just return an empty 'Properties' instance.
         if (isClosing || (str.length() <= (tok.length() + 3))) return Stream.empty();
        
         // This RegEx Matcher 'matches' against Attribute/InnerTag Key-Value Pairs
         //      ONLY PAIRS WHOSE KEY BEGINS WITH "data-" WILL MATCH
         // m.group(2): returns the 'key' portion of the key-value pair, before an '=' (equals-sign).
         // m.group(3): returns the 'value' portion of the key-value pair, after an '='
         Matcher m = AttrRegEx.DATA_ATTRIBUTE_REGEX.matcher(this.str);
        
         // NOTE: HTML mandates attributes must be 'case-insensitive' to the attribute 'key-part'
         //      (but not necessarily the 'value-part')
         // HOWEVER: Java does not require anything for the 'Properties' class.
         // ALSO: Case is PRESERVED for the 'value-part' of the key-value pair.
         if (preserveKeysCase)   while (m.find()) b.accept(m.group(2));
         else                    while (m.find()) b.accept(m.group(2).toLowerCase());
         
         return b.build();
        
      • toStringAV

        public java.lang.String toStringAV()
        This does a "longer version" of the parent toString() method. This is because it also parses and prints inner-tag key-value pairs. The ordinary public String toString() method that is inherited from parent class HTMLNode will just return the value of class HTMLNode field: public final String str.
        Returns:
        A String with the inner-tag key-value pairs specified.

        Example:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
         // The following code, would output the text below
         TagNode tn = new TagNode("<BUTTON CLASS='MyButtons' ONCLICK='MyListener();'>");
         System.out.println(tn.toStringAV());
        
         // Outputs the following Text:
         
         // TagNode.str: [<BUTTON class='MyButtons' onclick='MyListener();'>], TagNode.tok: [button], TagNode.isClosing: [false]
         // CONTAINS a total of (2) attributes / inner-tag key-value pairs:
         // (KEY, VALUE):   [onclick], [MyListener();]
         // (KEY, VALUE):   [class], [MyButtons]
         
        
        See Also:
        allAV(), tok, isClosing, HTMLNode.toString()
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
         StringBuilder sb = new StringBuilder();
        
         // Basic information.  This info is common to ALL instances of TagNode
         sb.append(
             "TagNode.str: [" + this.str + "], TagNode.tok: [" + this.tok + "], " +
             "TagNode.isClosing: [" + this.isClosing + "]\n"
         );
        
         // Not all instances of TagNode will have attributes.
         Properties attributes = this.allAV(false, true);
         sb.append(
             "CONTAINS a total of (" + attributes.size() + ") attributes / inner-tag " +
             "key-value pairs" + (attributes.size() == 0 ? "." : ":") + "\n"
         );
        
         // If there are inner-tags / attributes, then add them to the output-string, each on a
         // separate line.
         for (String key : attributes.stringPropertyNames())
             sb.append("(KEY, VALUE):\t[" + key + "], [" + attributes.get(key) + "]\n");
        
         // Build the string from the StringBuilder, and return it.
         return sb.toString();
        
      • clone

        public TagNode clone()
        Java's interface Cloneable requirements. This instantiates a new TagNode with identical String str fields, and also identical boolean isClosing and String tok fields.
        Specified by:
        clone in class HTMLNode
        Returns:
        A new TagNode whose internal fields are identical to this one.
        Code:
        Exact Method Body:
        1
         return new TagNode(str);
        
      • compareTo

        public int compareTo​(TagNode n)
        This sorts by:

        1. by String 'tok' fields character-order (ASCII-alphabetical).
          The following final String 'tok' fields are ASCII ordered: 'a', 'button', 'canvas', 'div', 'em', 'figure' ...
        2. then (if 'tok' fields are equal) by the public final boolean 'isClosing' field.
          TagNode's that have a 'isClosing' set to FALSE come before TagNode's whose 'isClosing' field is set to TRUE
        3. finally, if the 'tok' and 'isClosing' fields are equal, they are sorted by the integer-length of final String 'str' field.
        Specified by:
        compareTo in interface java.lang.Comparable<TagNode>
        Parameters:
        n - Any other TagNode to be compared to 'this' TagNode
        Returns:
        An integer that fulfils Java's interface Comparable<T> public boolean compareTo(T t) method requirements.
        See Also:
        tok, isClosing, HTMLNode.str
        Code:
        Exact Method Body:
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
         // Utilize the standard "String.compare(String)" method with the '.tok' string field.
         // All 'tok' fields are stored as lower-case strings.
         int compare1 = this.tok.compareTo(n.tok);
        
         // Comparison #1 will be non-zero if the two TagNode's being compared had different
         // .tok fields
         if (compare1 != 0) return compare1;
        
         // If the '.tok' fields were the same, use the 'isClosing' field for comparison instead.
         // This comparison will only be used if they are different.
         if (this.isClosing != n.isClosing) return (this.isClosing == false) ? -1 : 1;
            
         // Finally try using the entire element '.str' String field, instead.  
         return this.str.length() - n.str.length();