001package Torello.HTML.NodeSearch;
002
003import java.util.Vector;
004import java.util.function.Predicate;
005import java.util.regex.Pattern;
006
007import Torello.HTML.*;
008import Torello.Java.StrFilter;
009
010/**
011 * A functional-interface / lambda-target, and several {@code static}-builders for generating
012 * instances of them, which extends {@code java.util.function.Predicate} and encapsulates
013 * search-criteria into a <CODE>Predicate&lt;{@link TagNode}&gt;</CODE>
014 * 
015 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=AVT>
016 */
017@FunctionalInterface
018public interface AVT extends Predicate<TagNode>, java.io.Serializable
019{
020    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDFI>  */
021    public static final long serialVersionUID = 1;
022
023    // ******************************************************************************************
024    // Functional-Interface Method
025    // ******************************************************************************************
026
027    /**
028     * <B><SPAN STYLE="color: red;">FUNCTIONAL-INTERFACE BOOLEAN METHOD:</SPAN></B> This is the 
029     * method that fulfils this {@code functional-interface 'test'} method.
030     *
031     * @param tn This method will be called - once for each {@code TagNode} found inside of a 
032     * vectorized HTML page.
033     * 
034     * @return If the {@code TagNode} meets the test's "inclusion requirements", then this method
035     * should return {@code TRUE}.
036     */
037    public boolean test(TagNode tn);
038
039    // ******************************************************************************************
040    // TextComparitor factory builders
041    // ******************************************************************************************
042
043    /**
044     * This is a {@code static} factory method that generates {@code AVT-Predicate's}
045     * ({@code Predicate<TagNode>}).  It saves the user of typing the lambda information by hand,
046     * and does a validation check too.  The primary use of this class is that the results of one
047     * factory method may be "AND-chained" or "OR-chained" with another to make search requirements
048     * more specific.
049     *
050     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
051     * the <B STYLE="color: red;">name</B> of the attribute, not it's
052     * <B STYLE="color: red;">value</B>. The <B STYLE="color: red;">value</B> will be found (if the
053     * {@code TagNode} contains this attribute), and the parameter {@code 'TextComparitor'} will be
054     * used to compare this <B STYLE="color: red;">value</B> - <I>dependent upon which
055     * {@code 'TextComparitor'} is used</I> against the Compare-Strings
056     *
057     * @param tc This may be any of the listed {@code TextComparitor's} in the class.  There are
058     * quite a few "pre-defined" {@code static} members in the {@link TextComparitor} class.  There
059     * are many that have both long names, and abbreviated names which can be interchangeably used
060     * for readability purposes.
061     * 
062     * @param compareStr These are passed to the {@code 'TextComparitor'} when using to perform
063     * tests on the attribute <B STYLE="color: red;">value</B>.
064     *
065     * @return <EMBED CLASS='external-html' DATA-FILE-ID=RETCMP1>
066     * 
067     * @see ARGCHECK#innerTag(String)
068     * @see ARGCHECK#TC(TextComparitor, String[])
069     * @see TagNode#AV(String)
070     * @see TextComparitor#test(String, String[])
071     *
072     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
073     * @throws NullPointerException If any of the provided input reference parameters are null.
074     */
075    public static AVT cmp(String innerTag, TextComparitor tc, String... compareStr)
076    {
077        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
078        // If these tests fail, the returned predicate would absolutely fail.
079
080        final String innerTagLC = ARGCHECK.innerTag(innerTag);
081        ARGCHECK.TC(tc, compareStr);
082
083        // Minimum length for field TagNode.str to have before it could possible contain the attribute
084        // Obviously, the TagNode would have to have a min-length that includes the
085        // attribute-name length + '< ' and '>'
086
087        final int MIN_LEN = innerTag.length() + 3;
088
089        // Java's "Lambda-Expression" Syntax (like an "anonymous method").
090        // AVT extends functional-interface Predicate<TagNode>
091        return (TagNode tn) ->
092        {
093            // This eliminates testing any TagNode that simply COULD NOT contain the
094            // specified attribute.  (an optimization)
095
096            if (tn.isClosing || (tn.str.length() <= (tn.tok.length() + MIN_LEN))) return false;
097
098            // Retrieve the value of the requested "inner-tag" (HTML Attribute) Key-Value Pair
099            // from the input HTML-Element (TagNode)
100
101            String itv = tn.AV(innerTagLC);
102                // REG-EX MATCHER, MORE EXPENSIVE
103    
104            // If the innerTag's value is null, then the inner-tag was not a key-value 
105            // found inside the TagNode: return false.
106            // Otherwise return the 'tc' test-results on that value using the named 'tc' 
107            // comparison on the compare-strings.
108
109            return (itv == null) ? false : tc.test(itv, compareStr);  
110        };
111    }
112
113    /**
114     * <EMBED CLASS='external-html' DATA-FILE-ID=CMPKIITNF1>
115     * 
116     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
117     * the <B STYLE="color: red;">name</B> of the attribute, not it's
118     * <B STYLE="color: red;">value</B>. The <B STYLE="color: red;">value</B> will be found (if the
119     * {@code TagNode} contains this attribute), and the parameter {@link TextComparitor} will be
120     * used to compare this <B STYLE="color: red;">value</B> - <I>dependent upon which
121     * {@code 'TextComparitor'} is used</I> against the Compare-{@code String's}
122     *
123     * @param tc This may be any of the listed {@code TextComparitor's} in the class.  There are
124     * quite a few "pre-defined" {@code static} members in the {@code TextComparitor} class.  There
125     * are many that have both long names, and abbreviated names which can be interchangeably used
126     * for readability purposes.
127     *
128     * @param compareStr These are passed to the {@code 'TextComparitor'}  to perform tests on the
129     * attribute <B STYLE="color: red;">value</B>.
130     *
131     * @return An instance of {@code 'AVT'} that can be passed to the NodeSearch classes
132     * search-methods via any one of the methods that accepts a {@code Predicate<TagNode>} as a
133     * parameter in the search criteria.
134     *
135     * @see #cmp(String, TextComparitor, String[])
136     *
137     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
138     * @throws NullPointerException If any of the provided input reference parameters are null.
139     */
140    public static AVT cmpKIITNF(String innerTag, TextComparitor tc, String... compareStr)
141    {
142        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
143        // If these tests fail, the returned predicate would absolutely fail.
144
145        final String innerTagLC = ARGCHECK.innerTag(innerTag);
146        ARGCHECK.TC(tc, compareStr);
147
148        // Java's "Lambda-Expression" Syntax (like an "anonymous method").
149        // AVT extends functional-interface Predicate<TagNode>
150
151        return (TagNode tn) ->
152        {
153            // This eliminates testing any TagNode that simply COULD NOT contain
154            // attributes.  (an optimization)
155            //
156            // KIITNF -> Empty Opening HTML TagNode Elements cannot be eliminated!
157            // HOWEVER, Closing TagNodes are never included
158
159            if (tn.isClosing) return false;
160
161            // Retrieve the value of the requested "inner-tag" (HTML Attribute) Key-Value Pair
162            // from the input HTML-Element (TagNode)
163
164            String itv = tn.AV(innerTagLC);
165                // REG-EX MATCHER, MORE EXPENSIVE
166
167            // If the innerTag's value is null, then the inner-tag was not a key-value pair
168            // found inside the TagNode.
169            //
170            // BECAUSE the user requested to "Keep If Inner-Tag Not Found", we must return TRUE
171            //      in that case.
172            //      In Java '||' uses short-circuit boolean-evaluation, while '|' requires
173            //      full-evaluation.
174            //
175            // OTHERWISE return the 'tc' test-results on that value using the named 'tc' comparison
176            //      on the compare-strings.
177
178            return (itv == null) || tc.test(itv, compareStr);
179        };
180    }
181
182    // ********************************************************************************************
183    // Regular-Expression (Pattern) factory builders.
184    // ********************************************************************************************
185
186    /**
187     * This is a {@code static} factory method that generates {@code AVT-Predicate's}
188     * ({@code Predicate<TagNode>}).  It saves the user of typing the lambda information by hand,
189     * and does a validation check too.  The primary use of this class is that the results of one
190     * factory method may be "AND-chained" or "OR-chained" with another to make search requirements
191     * more specific.
192     *
193     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
194     * the <B STYLE="color: red;">name</B> of the attribute, not it's
195     * <B STYLE="color: red;">value</B>. The <B STYLE="color: red;">value</B> will be found (if the
196     * {@code TagNode} contains this attribute), and then tested using the Regular-Expression
197     * {@code p.matcher(tag_value).find()} method.
198     *
199     * @param p This may be any regular expression {@code Pattern}.  This {@code Pattern} will be
200     * executed against the <B STYLE="color: red;">value</B> of the inner-tag specified by
201     * parameter {@code 'innerTag'}.
202     *
203     * @return <EMBED CLASS='external-html' DATA-FILE-ID=RETCMP2>
204     *
205     * @see ARGCHECK#innerTag(String)
206     * @see ARGCHECK#REGEX(Pattern)
207     * @see TagNode#AV(String)
208     *
209     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
210     * @throws NullPointerException If any of the provided input reference parameters are null.
211     */
212    public static AVT cmp(String innerTag, Pattern p)
213    {
214        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
215        // If these tests fail, the returned predicate would absolutely fail.
216
217        final String            innerTagLC  = ARGCHECK.innerTag(innerTag);
218        final Predicate<String> pred        = ARGCHECK.REGEX(p);
219
220        // Minimum length for field TagNode.str to have before it could possible contain the attribute
221        // Obviously, the TagNode would have to have a min-length that includes the attribute-name
222        // length + '< ' and '>'
223
224        final int MIN_LEN = innerTag.length() + 3;
225
226        // Java's "Lambda-Expression" Syntax (like an "anonymous method").
227        // AVT extends functional-interface Predicate<TagNode>
228
229        return (TagNode tn) ->
230        {
231            // This eliminates testing any TagNode that simply COULD NOT contain the
232            // attribute.  (an optimization)
233
234            if (tn.isClosing || (tn.str.length() <= (tn.tok.length() + MIN_LEN))) return false;
235
236            // Retrieve the value of the requested "inner-tag" (HTML Attribute) Key-Value Pair
237            // from the input HTML-Element (TagNode)
238            //
239            // REG-EX MATCHER, MORE EXPENSIVE
240
241            String itv = tn.AV(innerTagLC);
242
243            // If the innerTag's value is null, then the inner-tag was not a key-value pair
244            // found inside the TagNode: return false.
245            // Otherwise return the results of running the Regular-Expression matcher using the
246            // input 'Pattern' instance.
247
248            return (itv == null) ? false : pred.test(itv);
249        };
250    }
251
252    /**
253     * This is a {@code static} factory method that generates {@code AVT-Predicate's}
254     * ({@code Predicate<TagNode>}).  It saves the user of typing the lambda information by hand,
255     * and does a validation check too.  The primary use of this class is that the results of one
256     * factory method may be "AND-chained" or "OR-chained" with another to make search requirements
257     * more specific.
258     *
259     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
260     * the <B STYLE="color: red;">name</B> of the attribute, not it's
261     * <B STYLE="color: red;">value</B>. The <B STYLE="color: red;">value</B> will be found (if the
262     * {@code TagNode} contains this attribute), and then tested using the Regular-Expression
263     * {@code p.matcher(tag_value).find()} method.
264     *
265     * @param p This may be any regular expression {@code Pattern}.  This {@code Pattern} will be
266     * executed against the <B STYLE="color: red;">value</B> of the inner-tag specified by
267     * parameter {@code 'innerTag'}.
268     *
269     * @param keepOnMatch There may be times when it is necessary to specify that a
270     * Regular-Expression match should cause the search-filter to reject a {@code TagNode}, rather
271     * than keeping it as a search-result match.  In this case, the programmer can utilize this
272     * variable to indicate whether matches should cause this method to return {@code TRUE} or
273     * {@code FALSE}.  If this variable is set to {@code FALSE}, then the {@code Predicate<TagNode>}
274     * that is generated will return {@code FALSE}, whenever the regular-expression matches the
275     * Attribute-<B STYLE="color: red;">Value</B>.
276     *
277     * <BR /><BR /><B><SPAN STYLE="color: red;">DEFAULT BEHAVIOR NOTE:</B></SPAN> The classes and 
278     * methods in this Node Search Package that accept regular-expressions as search-parameters
279     * will always treat a match to indicate that the {@code TagNode} (or {@code TextNode}) in
280     * question <B><I>has passed</I></B> the search-filter criteria.  This method, therefore,
281     * provides a way to bypass this default behavior.
282     *
283     * @param keepOnNull This parameter allows the user to specify whether the absence of an HTML
284     * Inner-Tag should indicate that the TagNode being tested should pass or fail (keep or
285     * reject) the search-filter criteria.
286     *
287     * <BR /><BR /><B><SPAN STYLE="color: red;">DEFAULT BEHAVIOR NOTE:</B></SPAN> The default 
288     * filter-results for the search classes and search methods of the Node-Search Package are such
289     * that if an inner-tag is simply not available ... or 'not present' within an HTML Element,
290     * then that element <I><B>will not be included</I></B> in the search results for that class or
291     * method. <B><I>By using this particular {@code AVT} factory-method, a programmer can by-pass
292     * that default behavior.</I></B>
293     *
294     * @return An instance of {@code 'AVT'} that can be passed to the NodeSearch classes
295     * search-methods via any one of the methods that accepts a {@code Predicate<TagNode>} as a
296     * parameter in the search criteria.
297     *
298     * @see ARGCHECK#innerTag(String)
299     * @see ARGCHECK#REGEX(Pattern)
300     * @see TagNode#AV(String)
301     *
302     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
303     * @throws NullPointerException If any of the provided input reference parameters are null.
304     */
305    public static AVT cmp
306        (String innerTag, Pattern p, final boolean keepOnMatch, final boolean keepOnNull)
307    {
308        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
309        // If these tests fail, the returned predicate would absolutely fail.
310
311        final String            innerTagLC  = ARGCHECK.innerTag(innerTag);
312        final Predicate<String> pred        = ARGCHECK.REGEX(p);
313
314        // Java's "Lambda-Expression" Syntax (like an "anonymous method").
315        // AVT extends functional-interface Predicate<TagNode>
316
317        return (TagNode tn) ->
318        {
319            // This eliminates testing any TagNode that simply COULD NOT contain 
320            // attributes.  (an optimization)
321            //
322            // keepOnNull -> Empty Opening HTML TagNode Elements cannot be eliminated!
323            // HOWEVER,     Closing TagNodes are never included
324
325            if (tn.isClosing) return false;
326
327            // Retrieve the value of the requested "inner-tag" (HTML Attribute) Key-Value Pair
328            // from the input HTML-Element (TagNode)
329            //
330            // REG-EX MATCHER, MORE EXPENSIVE
331
332            String itv = tn.AV(innerTagLC);
333
334            // If the Attribute is simply not present in the HTML Element
335            if (itv == null)    return keepOnNull;   
336
337            if (pred.test(itv)) return keepOnMatch;     // if the Regular-Expression succeeded
338            else                return ! keepOnMatch;   // If the Regular-Expression failed
339        };
340    }
341
342    /**
343     * <EMBED CLASS='external-html' DATA-FILE-ID=CMPKIITNF2>
344     *
345     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
346     * the <B STYLE="color: red;">name</B> of the attribute, not it's
347     * <B STYLE="color: red;">value</B>. The <B STYLE="color: red;">value</B> will be found (if the
348     * {@code TagNode} contains this attribute), and then tested using the Regular-Expression
349     * {@code p.matcher(tag_value).find()} method.
350     *
351     * @param p This may be any regular expression {@code Pattern}.  This {@code Pattern} will be
352     * executed against the <B STYLE="color: red;">value</B> of the inner-tag specified by
353     * parameter {@code 'innerTag'}.
354     *
355     * @return An instance of {@code 'AVT'} that can be passed to the NodeSearch classes
356     * search-methods via any one of the methods that accepts a {@code Predicate<TagNode>} as a
357     * parameter in the search parameter-list.
358     *
359     * @see #cmp(String, Pattern)
360     *
361     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
362     * @throws NullPointerException If any of the provided input reference parameters are null.
363     */
364    public static AVT cmpKIITNF(String innerTag, Pattern p)
365    {
366        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
367        // If these tests fail, the returned predicate would absolutely fail.
368
369        final String            innerTagLC  = ARGCHECK.innerTag(innerTag);
370        final Predicate<String> pred        = ARGCHECK.REGEX(p);
371
372        // Java's "Lambda-Expression" Syntax (like an "anonymous method").
373        // AVT extends functional-interface Predicate<TagNode>
374
375        return (TagNode tn) ->
376        {
377            // This eliminates testing any TagNode that simply COULD NOT contain
378            // attributes.  (an optimization)
379            //
380            // KIITNF -> Empty Opening HTML TagNode Elements cannot be eliminated!
381            // HOWEVER, Closing TagNodes are never included
382
383            if (tn.isClosing) return false;
384
385            // Retrieve the value of the requested "inner-tag" (HTML Attribute) Key-Value Pair
386            // from the input HTML-Element (TagNode)
387
388            String itv = tn.AV(innerTagLC);
389                // REG-EX MATCHER, MORE EXPENSIVE
390
391            // If the innerTag's value is null, then the inner-tag was not a key-value pair
392            // found inside the TagNode.
393            //
394            // BECAUSE the user requested to "Keep If Inner-Tag Not Found", we must return 
395            //      in that case.
396            //      In Java '||' uses short-circuit boolean-evaluation, while '|' requires
397            //      full-evaluation.
398            //
399            // OTHERWISE return the results of running the Regular-Expression matcher using the
400            //      input 'Pattern' instance.
401
402            return (itv == null) || pred.test(itv);
403        };
404    }
405
406    // ********************************************************************************************
407    // Predicate<String> factory builders.
408    // ********************************************************************************************
409
410    /**
411     * Convenience Method.
412     * <BR />Invokes: {@link #cmp(String, Predicate)}
413     * <BR />Converts: {@link StrFilter} to simple {@code String-Predicate}
414     */
415    public static AVT cmp(String innerTag, StrFilter innerTagValueTest)
416    { return cmp(innerTag, (Predicate<String>) innerTagValueTest::test); }
417
418    /**
419     * This is a {@code static} factory method that generates {@code AVT-Predicate's} - 
420     * ({@code Predicate<TagNode>}).  It saves the user of typing the lambda information by hand,
421     * and does a validation check too.  The primary use of this class is that the results of one
422     * factory method may be "AND-chained" or "OR-chained" with another to make search requirements
423     * more specific.
424     *
425     * <BR /><BR /><B>NOTE:</B> The astute observer might wonder why change from a 
426     * {@code String-Predicate} to a {@code TagNode-Predicate}, with the answer being that 
427     * predicate-chaining on <I>different, multiple inner-tags (and their
428     * <B STYLE="color: red;">values</B>)</I> can only be accomplished by using a
429     * {@code TagNode-Predicate}, rather than a {@code String-Predicate}
430     *
431     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
432     * the <B STYLE="color: red;">name</B> of the attribute, not it's
433     * <B STYLE="color: red;">value</B>. The <B STYLE="color: red;">value</B> will be found (if the
434     * {@code TagNode} contains this attribute), and then tested against the
435     * {@code String-Predicate} in parameter {@code 'innerTagValueTest'}.
436     *
437     * @param innerTagValueTest  This may be any Java {@code String-Predicate} with a 
438     * {@code test(...) / accept} method.  It will be used to accept or reject the inner-tag's
439     * <B STYLE="color: red;">value</B>
440     *
441     * @return <EMBED CLASS='external-html' DATA-FILE-ID=RETCMP3>
442     *
443     * @see InnerTagFind
444     * @see ARGCHECK#innerTag(String)
445     * @see TagNode#AV(String)
446     *
447     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
448     * @throws NullPointerException If any of the provided input reference parameters are null.
449     */
450    public static AVT cmp(String innerTag, Predicate<String> innerTagValueTest)
451    {
452        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
453        // If these tests fail, the returned predicate would absolutely fail.
454
455        final String innerTagLC = ARGCHECK.innerTag(innerTag);
456        if (innerTagValueTest == null) throw new NullPointerException
457            ("Parameter innerTagValueTest was passed null, but this is not allowed here.");
458
459        // Minimum length for field TagNode.str to have before it could possible contain the attribute
460        // Obviously, the TagNode would have to have a min-length that includes the attribute-name
461        // length + '< ' and '>'
462
463        final int MIN_LEN = innerTag.length() + 3;
464
465        // Java's "Lambda-Expression" Syntax (like an "anonymous method").
466        // AVT extends functional-interface Predicate<TagNode>
467
468        return (TagNode tn) ->
469        {
470            // This eliminates testing any TagNode that simply COULD NOT contain the
471            // attribute.  (an optimization)
472
473            if (tn.isClosing || (tn.str.length() <= (tn.tok.length() + MIN_LEN))) return false;
474
475            // Retrieve the value of the requested "inner-tag" (HTML Attribute) Key-Value Pair
476            // from the input HTML-Element (TagNode)
477
478            String itv = tn.AV(innerTagLC);
479                // REG-EX MATCHER, MORE EXPENSIVE
480
481            // If the innerTag's value is null, then the inner-tag was not a key-value pair
482            // found inside the TagNode: return false.
483            // Otherwise return the results of the Predicate<String> provided on that
484            // attribute-value.
485
486            return (itv == null) ? false : innerTagValueTest.test(itv);
487        };
488    }
489
490    /**
491     * Convenience Method.
492     * <BR />Invokes: {@link #cmpKIITNF(String, Predicate)}
493     * <BR />Converts: {@link StrFilter} to {@code String-Predicate}
494     */
495    public static AVT cmpKIITNF(String innerTag, StrFilter innerTagValueTest)
496    { return cmpKIITNF(innerTag, (Predicate<String>) innerTagValueTest::test); }
497
498    /**
499     * <EMBED CLASS='external-html' DATA-FILE-ID=CMPKIITNF3>
500     *
501     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
502     * the <B STYLE="color: red;">name</B> of the attribute, not it's
503     * <B STYLE="color: red;">value</B>. The <B STYLE="color: red;">value</B> will be found (if the
504     * {@code TagNode} contains this attribute), and then tested against the
505     * {@code String-Predicate} parameter {@code 'innerTagValueTest'}.
506     *
507     * @param innerTagValueTest  This may be any Java {@code String-Predicate} with a 
508     * {@code test(...) / accept} method.  It will be used to accept or reject the inner-tag's
509     * value.
510     *
511     * @return An instance of {@code 'AVT'} that can be passed to the NodeSearch classes
512     * search-methods via any one of the methods that accepts a {@code Predicate<TagNode>} as a
513     * parameter in the search criteria.
514     *
515     * @see #cmp(String, Predicate)
516     *
517     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
518     * @throws NullPointerException If any of the provided input reference parameters are null.
519     */
520    public static AVT cmpKIITNF(String innerTag, Predicate<String> innerTagValueTest)
521    {
522        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
523        // If these tests fail, the returned predicate would absolutely fail.
524
525        final String innerTagLC = ARGCHECK.innerTag(innerTag);
526
527        if (innerTagValueTest == null) throw new NullPointerException
528            ("Parameter innerTagValueTest was passed null, but this is not allowed here.");
529
530        // Java's "Lambda-Expression" Syntax (like an "anonymous method").
531        // AVT extends functional-interface Predicate<TagNode>
532
533        return (TagNode tn) ->
534        {
535            // This eliminates testing any TagNode that simply COULD NOT contain
536            // attributes.  (an optimization)
537            //
538            // KIITNF -> Empty Opening HTML TagNode Elements cannot be eliminated!
539            // HOWEVER, Closing TagNodes are never included
540
541            if (tn.isClosing) return false;
542
543            // Retrieve the value of the requested "inner-tag" (HTML Attribute) Key-Value Pair
544            // from the input HTML-Element (TagNode)
545            //
546            // REG-EX MATCHER, MORE EXPENSIVE
547
548            String itv = tn.AV(innerTagLC);
549
550            // If the innerTag's value is null, then the inner-tag was not a key-value pair
551            // found inside the TagNode.
552            //
553            // BECAUSE the user requested to "Keep If Inner-Tag Not Found", we must return TRUE
554            //          in that case.
555            //          In Java '||' uses short-circuit boolean-evaluation, while '|' requires
556            //          full-evaluation.
557            //
558            // OTHERWISE return the results of the Predicate<String> provided on that
559            //           attribute-value.
560
561            return (itv == null) || innerTagValueTest.test(itv);
562        };
563    }
564
565    // ********************************************************************************************
566    // Simple Present-Or-Not-Present test
567    // ********************************************************************************************
568
569    /**
570     * This is a {@code static} factory method that generates {@code AVT-Predicate's} - 
571     * ({@code Predicate<TagNode>}).  It saves the user of typing the lambda information by hand,
572     * and does a validation check too.  The primary use of this class is that the results of one
573     * factory method may be "AND-chained" or "OR-chained" with another to make search requirements
574     * more specific.
575     *
576     * @param innerTag This also goes by the term "attribute" in many HTML specifications.  It is
577     * the <B STYLE="color: red;">name</B> of the attribute, not it's
578     * <B STYLE="color: red;">value</B>.  If this attribute is found, this {@code Predicate} will
579     * always return {@code TRUE} regardless of it's <B STYLE="color: red;">value</B> - so long as
580     * it is not null.
581     *
582     * <BR /><BR /><B><SPAN STYLE="color:red;">IMPORTANT NOTE:</B></SPAN> There is a subtlety here
583     * between inner-tag's that have a <B STYLE="color: red;">value</B> of {@code the-empty-string,
584     * a zero-length-string}, and attributes that are "null" or not found at all.  Though rare, it
585     * is sometimes the case that an HTML Attribute may have a <B STYLE="color: red;">value</B> of
586     * {@code <SOME-TAG SOME-INNER-TAG="">}.  There can be other versions that leave the quotes off
587     * entirely such as: {@code <OTHER-ELEMENT OTHER-ATTRIBUTE=>} - where there are no quotes at
588     * all. If the attribute is found, <I>with an equals sign</I> it will evaluate to the
589     * <B><I>{@code the zero-length-string}</I></B>, but if the attribute is not found at all,
590     * searching for it will return null, and this {@code Predicate} will return {@code FALSE}.
591     *
592     * @return <EMBED CLASS='external-html' DATA-FILE-ID=RETCMP4>
593     *
594     * @see InnerTagFind
595     * @see ARGCHECK#innerTag(String)
596     * @see TagNode#AV(String)
597     *
598     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=ITKEYEX>
599     * 
600     * @throws NullPointerException If any of the provided input reference parameters are null.
601     */
602    public static AVT cmp(String innerTag)
603    {
604        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
605        // If this test fails, the returned predicate would absolutely fail.
606
607        final String innerTagLC = ARGCHECK.innerTag(innerTag);
608
609        // SIMPLIFIED LAMBDA: The contents of this "anonymous method" can be expressed in a
610        // single-statement.  No need for 'return' or 'curly-braces'
611        // Returns TRUE if the HTML Element contained a copy of the named inner-tag, and false
612        // otherwise.
613
614        return (TagNode tn) -> tn.AV(innerTagLC) != null;
615    }
616
617    // ******************************************************************************************
618    // The basic-required methods, of a "Functional-Interface Predicate"
619    // ******************************************************************************************
620
621    /**
622     * Generates a new {@code 'AVT'} predicate test that {@code logically-AND's} the results of
623     * {@code 'this' Predicate} with the results of the new, additional passed parameter
624     * {@code Predicate 'additionalTest'}. 
625     *
626     * @param additionalTest This is an additional test of the inner-tag
627     * <B STYLE="color: red;">key-value pair</B> (also known as the
628     * <B STYLE="color: red;">"attribute-value pair"</B>) of HTML {@code TagNode's}.
629     *
630     * @return A new {@code Predicate<TagNode>} that will use two tests:
631     * {@code 'this'} and {@code 'additionalTest'} and subsequently perform a logical-AND on the
632     * result.  Short-circuit evaluation is used (specifically, the {@code '&&'} operator,
633     * rather than the {@code '&'} operator are utilized).  The {@code Predicate} that is returned
634     * will perform the {@code 'this'} test first, and then the {@code 'additionalTest'} second.  
635     * The returned {@code Predicate} will return the {@code 'AND'} of both of them.
636     *
637     * @see TagNode
638     *
639     * @throws NullPointerException If any of the provided input reference parameters are null.
640     */
641    default AVT and(AVT additionalTest)
642    {
643        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
644        // If this test fails, the returned predicate would absolutely fail.
645
646        if (additionalTest == null) throw new NullPointerException
647            ("The parameter 'additionalTest' passed to method 'AVT.and(additionalTest)' was null");
648
649        // SIMPLIFIED LAMBDA: The contents of this "anonymous method" can be expressed in a
650        // single-statement.  No need for 'return' or 'curly-braces'
651        // Returns TRUE if both 'this' evaluates to true on an input HTML Element,
652        // and 'other' also evaluates to true for the same element.
653
654        return (TagNode tn) -> this.test(tn) && additionalTest.test(tn);
655    }
656
657    /**
658     * Generates a new {@code 'AVT'} predicate test that {@code logically-OR's} the results of
659     * {@code 'this' Predicate} with the results of the new, additional passed parameter
660     * {@code Predicate 'additionalTest'}. 
661     *
662     * @param additionalTest This is an additional test of the inner-tag
663     * <B STYLE="color: red;">key-value pair</B> (also known as the
664     * <B STYLE="color: red;">"attribute-value pair"</B>) of HTML {@code TagNode's}.
665     *
666     * @return A new {@code Predicate<TagNode>} that will use two tests:
667     * {@code 'this'} and {@code 'additionalTest'} and subsequently perform a logical-OR on the
668     * result.  Short-circuit evaluation is used (specifically, the {@code '||'} operator,
669     * rather than the {@code '|'} operator are utilized).  The {@code Predicate} that is returned
670     * will perform the {@code 'this'} test first, and then the {@code 'additionalTest'} second.  
671     * The returned {@code Predicate} will return the {@code 'OR'} of both of them.
672     *
673     * @see TagNode
674     *
675     * @throws NullPointerException If any of the provided input reference parameters are null.
676     */
677    default AVT or(AVT additionalTest)
678    {
679        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
680        // If this test fails, the returned predicate would absolutely fail.
681
682        if (additionalTest == null) throw new NullPointerException
683            ("The parameter 'additionalTest' passed to method 'AVT.or(additionalTest)' was null");
684
685        // SIMPLIFIED LAMBDA: The contents of this "anonymous method" can be expressed in a
686        // single-statement.  No need for 'return' or 'curly-braces'
687        // Returns TRUE if either 'this' evaluates to true on an input HTML Element,
688        // and 'other' also evaluates to true for the same element.
689
690        return (TagNode tn) -> this.test(tn) || additionalTest.test(tn);
691    }
692
693    /**
694     * Generates a new {@code 'AVT'} predicate test that is the {@code logical-NOT} of
695     * {@code 'this' Predicate}.
696     *
697     * @return A new {@code Predicate<TagNode>} that will simply just calls {@code 'this'
698     * Predicate}, and puts an exclamation point ({@code logical 'NOT'}) in front of the result.
699     *
700     * @see TagNode
701     */
702    default AVT negate()
703    {
704        // SIMPLIFIED LAMBDA: The contents of this "anonymous method" can be expressed in a
705        // single-statement.  No need for 'return' or 'curly-braces'
706        // Returns the opposite of whatever result 'this' evaluates using the input HTML Element.
707
708        return (TagNode tn) -> ! this.test(tn);
709    }
710
711    /**
712     * This is a {@code static} factory method that generates {@code AVT-Predicate's} 
713     * ({@code Predicate<TagNode>}).  It saves the user of typing the lambda information by hand,
714     * and does a validation check too. 
715     *
716     * <BR /><BR />If the {@code expectedTN.equals(tn)} fails - specifically using the
717     * java-built-in equality-test method {@code 'equals(...)'}, then the generated / returned
718     * {@code Predicate} would return {@code TRUE}, and the {@code TagNode} in question would be
719     * included in the results.
720     *
721     * @param expectedTN This is compared against {@code TagNode's} found in the
722     * page-{@code Vector} for equality.
723     *
724     * @return A {@code Predicate<TagNode>} that compares for equality with parameter
725     * {@code 'expectedTN'}
726     *
727     * @see TagNode
728     *
729     * @throws NullPointerException If any of the provided input reference parameters are null.
730     */
731    public static AVT isEqualKEEP(TagNode expectedTN)
732    {
733        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
734        // If this test fails, the returned predicate would absolutely fail.
735
736        if (expectedTN == null) throw new NullPointerException
737            ("The parameter 'expectedTN' passed to method 'AVT.isEqualKEEP(expectedTN)' was null");
738
739        // SIMPLIFIED LAMBDA: The contents of this "anonymous method" can be expressed in a
740        // single-statement.  No need for 'return' or 'curly-braces'
741        // Returns true if the HTML Element passed to this (anonymous) method is the same as the
742        // one passed to 'isEqualsKEEP'
743        // Identical to:  (TagNode tn) -> tn.str.equals(expectedTN.str);
744
745        return (TagNode tn) -> tn.equals(expectedTN);
746    }
747
748    /**
749     * This is a {@code static} factory method that generates {@code AVT-Predicate's}
750     * ({@code Predicate<TagNode>}).  It saves the user of typing the lambda information by hand,
751     * and does a validation check too. 
752     *
753     * <BR /><BR />If the {@code expectedTN.equals(tn)} fails - specifically using the
754     * java-built-in equality-test method {@code equals(...)} - then the generated / returned
755     * {@code Predicate} would return {@code FALSE}, and the {@code TagNode} in question would be
756     * filtered from the results.
757     *
758     * @param expectedTN This is compared against {@code TagNode's} found in the
759     * page-{@code Vector} for equality.
760     *
761     * @return A {@code Predicate<TagNode>} that compares for equality with parameter
762     * {@code 'expectedTN'}
763     *
764     * @see TagNode
765     *
766     * @throws NullPointerException If any of the provided input reference parameters are null.
767     */
768    public static AVT isEqualREJECT(TagNode expectedTN)
769    {
770        // FAIL-FAST: It is helpful for the user to test the data before building the Predicate.
771        // If this test fails, the returned predicate would absolutely fail.
772
773        if (expectedTN == null) throw new NullPointerException(
774            "The parameter 'expectedTN' passed to method 'AVT.isEqualREJECT(expectedTN)' "+
775            "was null"
776        );
777
778        // SIMPLIFIED LAMBDA: The contents of this "anonymous method" can be expressed in a
779        // single-statement.  No need for 'return' or 'curly-braces'
780        // Returns TRUE if the HTML Element passed to this (anonymous) method is the same as the
781        // one passed to 'isEqualsKEEP'
782        // Identical to:  (TagNode tn) -> ! tn.str.equals(expectedTN.str);
783
784        return (TagNode tn) -> ! tn.equals(expectedTN);
785    }
786}