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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
package Torello.HTML.NodeSearch;

import java.util.regex.*;
import java.util.stream.Stream;

import java.util.function.Predicate;

import Torello.HTML.TagNode;

/**
 * An exception used for problems generated by either HTML-Tag <B>in-line</B> CSS
 * <CODE>STYLE='&#46;&#46;&#46;'</CODE> attributes, or by explicitly declared CSS
 * <CODE>&lt;STYLE TYPE='text/css'&gt;</CODE> <B>style-blocks</B>.
 * 
 * <BR /><BR />
 * This class does some passed-parameter checking for the HTML-Search routines.  If one is
 * using the {@code class TextComparitor}, it is important to remember that it usually works in 
 * coordination with Java's var-args syntax which allows anywhere from {@code '0'} to
 * {@code 'n' String's}.  This class of exception will be thrown if any one of the
 * {@code String's} passed to the var-args parameter with the {@code TextComparitor} them has an
 * invalid CSS Token according to the definition of valid CSS Naming-Convention Tokens.
 *
 * <BR /><BR /><B CLASS=JDDescLabel>NOTE:</B>
 * 
 * <BR />This exception test is only performed when a {@link TextComparitor} is used with one of
 * the {@link TagNode} CSS {@code 'class'} getter and setter methods, for example:
 * {@link TagNode#setCSSClasses​(SD, boolean, String[])}
 *
 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=EXPM>
 */
public class CSSStrException extends TCCompareStrException
{
    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDEX>  */
    public static final long serialVersionUID = 1;

    /**
     * This regular-expression can be used to identify valid versus invalid CSS Class-Names, and
     * also CSS ID-Names. In English, this regular-expression says: A valid name should start with
     * an underscore (_), a hyphen (-) or a letter (a-z) which is followed by any numbers, hyphens,
     * underscores, letters. A name should be at least two characters long. Cannot start with a
     * digit, two hyphens or a hyphen followed by a number.
     */
    public static final Pattern VALID_CSS_CLASS_OR_NAME_TOKEN = 
        Pattern.compile("^[_\\-a-zA-Z]+[_\\-a-zA-Z0-9]*$");

    /**
     * Regular-Expression as {@code Predicate<String>}
     * @see #VALID_CSS_CLASS_OR_NAME_TOKEN
     */
    public static final Predicate<String> VALID_CSS_CLASS_OR_NAME_TOKEN_PRED =
        VALID_CSS_CLASS_OR_NAME_TOKEN.asPredicate();

    /**
     * This constructor just calls the line: {@code super(message, compareStr, i);}
     * 
     * @param message the detail message.
     * 
     * @param compareStr This <I>SHOULD BE</I> the list of compare-strings that were passed to a
     * TextComparitor that may have contained a null value, or was empty.  It is important not to
     * pass null for this parameter, because checking the input to an exception's constructor is
     * not very wise... "Exceptions about Exceptions" is usually unintelligible to a programmer.
     * 
     * @param i This is the index into the compareStr var-args parameter list that cause the
     * exception to throw.
     */
    public CSSStrException(String message, String[] compareStr, int i)
    { super(message, compareStr, i); }

    /**
     * This constructor just calls the line: {@code super(message, cause, compareStr, i);}
     * 
     * @param message The detail message (which is saved for later retrieval by the
     * {@code Throwable.getMessage()} method).
     * 
     * @param cause This is sometimes used to "chain" exceptions in multi-threaded or heavily I/O
     * related code.
     * 
     * @param compareStr This <I>SHOULD BE</I> the list of compare-strings that were passed to a
     * TextComparitor that may have contained a null value, or was empty.  It is important not to
     * pass null for this parameter, because checking the input to an exception's constructor is
     * not very wise... "Exceptions about Exceptions" is usually unintelligible to a programmer.
     * 
     * @param i This is the index into the compareStr var-args parameter list that cause the
     * exception to throw.
     */
    public CSSStrException(String message, Throwable cause, String[] compareStr, int i)
    { super(message, cause, compareStr, i); }

    /**
     * This will do a simple test of the compare-{@code String's} to make sure none of them are
     * null, and that there are at least one {@code String} in the array.
     *
     * <!-- This Explanation has been Copied from Torello.Java.StrCmprException -->
     * <BR /><BR /><B CLASS=JDDescLabel>Explanation:</B>
     * 
     * <BR />It is a subtle issue, but likely better to throw exceptions when one of the Varargs to
     * a {@code String} comparison is {@code 'null'}.  Since there is likely no general-convention
     * or agreement on what {@code null} in the presence of {@code logical AND, OR, NOT, XOR, NAND}
     * really means, throwing a {@code StrCmprException} <I>when even one var-args string-parameter
     * is {@code 'null'}</I> actually makes the most sense.
     * 
     * @param compareStr This should be the var-args parameter that was passed to a search method
     * 
     * @throws CSSStrException If any of the elements of Var-Args parameter {@code 'compareStr'}
     * is null, or if the array is zero-length.
     * 
     * @see #VALID_CSS_CLASS_OR_NAME_TOKEN
     * @see TCCompareStrException#check(String[])
     */
    public static void check(String... compareStr)
    {
        // Repackages the Exception into one with the name CSSStrException
        try
            { TCCompareStrException.check(compareStr); }
        catch (TCCompareStrException e)
        {
            String[] tempV = new String[compareStr.length];
            throw new CSSStrException(
                e.getMessage() + "\nSee getCause() for details.",
                e, e.compareStrVec.toArray(tempV), e.i
            );
        }

        for (int i=0; i < compareStr.length; i++)

            if (! VALID_CSS_CLASS_OR_NAME_TOKEN.matcher(compareStr[i]).find())

                throw new CSSStrException(
                    "One of the compare-strings passed to a search-method's var-args String " +
                    "parameter 'compareStr': [" + compareStr[i] + "]\n" +
                    "Did not pass the CSS Token-Naming Testing Regular-Expression: " +
                    "[" + VALID_CSS_CLASS_OR_NAME_TOKEN.pattern() + "]\n" +
                    "And this means it has been identified as an invalid CSS Token.  " +
                    "This is not allowed here.\n" +
                    "If you are using TagNode.cssClasses(), switch to TagNode.AV('class').\n" +
                    "If you are using TextComparitor.CONTAINS_CSS_CLASS*, switch to " +
                    "TextComparitor.EQ_CI_TRM",
                    compareStr, i
                );
    }

    /**
     * This will do a simple test of the compare-{@code String's} to make sure none of them are
     * null, and that there are at least one {@code String} in the array.
     *
     * <!-- This Explanation has been Copied from Torello.Java.StrCmprException -->
     * <BR /><BR /><B CLASS=JDDescLabel>Explanation:</B>
     * 
     * <BR />It is a subtle issue, but likely better to throw exceptions when one of the Varargs to
     * a {@code String} comparison is {@code 'null'}.  Since there is likely no general-convention
     * or agreement on what {@code null} in the presence of {@code logical AND, OR, NOT, XOR, NAND}
     * really means, throwing a {@code StrCmprException} <I>when even one var-args string-parameter
     * is {@code 'null'}</I> actually makes the most sense.
     * 
     * @param compareStrs This should be the any instance of {@code Stream<String>}
     * 
     * @throws CSSStrException If any of the elements of the {@code compareStrs} stream are null,
     * or if the stream is empty.
     * 
     * @see #VALID_CSS_CLASS_OR_NAME_TOKEN
     * @see TCCompareStrException#check(String[])
     */
    public static Stream<String> check(Stream<String> compareStrs)
    {
        // Repackages the Exception into one with the name CSSStrException
        String[] compareStr = compareStrs.toArray(String[]::new);

        try
            { TCCompareStrException.check(compareStr); }
        catch (TCCompareStrException e)
        {
            String[] tempV = new String[compareStr.length];

            throw new CSSStrException(
                e.getMessage() + "\nSee getCause() for details.",
                e, e.compareStrVec.toArray(tempV), e.i
            );
        }

        for (int i=0; i < compareStr.length; i++) 

            if (! VALID_CSS_CLASS_OR_NAME_TOKEN.matcher(compareStr[i]).find())

                throw new CSSStrException(
                    "One of the compare-strings passed to a search-method's var-args String " +
                    "parameter 'compareStr': [" + compareStr[i] + "]\n" +
                    "Did not pass the CSS Token-Naming Testing Regular-Expression: " +
                    "[" + VALID_CSS_CLASS_OR_NAME_TOKEN.pattern() + "]\n" +
                    "And this means it has been identified as an invalid CSS Token.  " +
                    "This is not allowed here.\n" +
                    "If you are using TagNode.cssClasses(), switch to TagNode.AV('class').\n" +
                    "If you are using TextComparitor.CONTAINS_CSS_CLASS*, switch to " +
                    "TextComparitor.EQ_CI_TRM",
                    compareStr, i
                );

        return Stream.of(compareStr);
        // The original stream is now empty!  We must re-build it!
    }
}