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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package Torello.HTML;

import Torello.HTML.helper.AttrRegEx;

import Torello.Java.StringParse;

import java.util.Properties;
import java.util.regex.Matcher;
import java.util.stream.Stream;

class RetrieveAllAttr
{
    static Properties allAV(
            final TagNode tn,
            final boolean keepQuotes,
            final boolean preserveKeysCase
        )
    {
        Properties ret = new Properties();


        // NOTE:    OPTIMIZED, "closing-versions" of the TagNode, and TagNode's whose 'str' field is
        //          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 (tn.isClosing || (tn.str.length() <= (tn.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).  Not w/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(tn.str);

        // MORE-CODE, but MORE-EFFICIENT (slightly)

        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;
    }

    public static Stream<String> allAN(
            final TagNode tn,
            final boolean preserveKeysCase,
            final boolean includeKeyOnlyAttributes
        )
    {
        // 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 (tn.isClosing || (tn.str.length() <= (tn.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(tn.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, KeyOnlyAttributes.allKeyOnlyAttributes(tn, preserveKeysCase));

        return ret;
    }

}