001package Torello.HTML;
002
003import Torello.HTML.helper.AttrRegEx;
004
005import Torello.Java.StringParse;
006import Torello.Java.StrCmpr;
007import Torello.Java.StrFilter;
008
009import Torello.HTML.NodeSearch.CSSStrException;
010import Torello.HTML.NodeSearch.TextComparitor;
011
012import Torello.JavaDoc.LinkJavaSource;
013
014import Torello.JavaDoc.IntoHTMLTable;
015import static Torello.JavaDoc.IntoHTMLTable.Background.GreenDither;
016import static Torello.JavaDoc.IntoHTMLTable.Background.BlueDither;
017
018import static Torello.JavaDoc.Entity.METHOD;
019import static Torello.JavaDoc.Entity.FIELD;
020
021import java.util.Vector;
022import java.util.Properties;
023import java.util.Map;
024
025import java.util.regex.Pattern;
026import java.util.regex.Matcher;
027
028import java.util.stream.Stream;
029
030import javax.management.AttributeNotFoundException;
031
032import java.util.function.Predicate;
033
034/**
035 * Represents an HTML Element Tag, and is the flagship class of the Java-HTML Library.
036 * 
037 * <EMBED CLASS='external-html' DATA-FILE-ID=TAG_NODE>
038 * <EMBED CLASS='external-html' DATA-FILE-ID=HTML_NODE_SUB_IMG>
039 * 
040 * @see TextNode
041 * @see CommentNode
042 * @see HTMLNode
043 */
044@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="HTML_NODE_SUBCLASS")
045public final class TagNode 
046    extends HTMLNode 
047    implements CharSequence, java.io.Serializable, Cloneable, Comparable<TagNode>
048{
049    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
050    public static final long serialVersionUID = 1;
051
052
053    // ********************************************************************************************
054    // ********************************************************************************************
055    // NON-STATIC FIELDS
056    // ********************************************************************************************
057    // ********************************************************************************************
058
059
060    /** <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_TOK> */
061    public final String tok;
062
063    /** <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_IS_CLOSING> */
064    public final boolean isClosing;
065
066
067
068    // ********************************************************************************************
069    // ********************************************************************************************
070    // Package-Private Constructors - NO ERROR CHECKING DONE WHATSOEVER
071    // ********************************************************************************************
072    // ********************************************************************************************
073
074
075    // ONLY USED BY THE "TagNodeHelpers" and in conjunction with "Generate Element String"
076    // 
077    // It presumes that the node was properly constructed and needs to error-checking
078    // It is only used for opening TagNode's
079
080    TagNode(String tok, String str)
081    {
082        super(str);
083
084        this.tok        = HTMLTags.getTag_MEM_HEAP_CHECKOUT_COPY(tok);
085        this.isClosing  = false;
086    }
087
088
089    // USED-INTERNALLY - bypasses all checks.  used when creating new HTML Element-Names
090    // ONLY: class 'HTMLTags' via method 'addTag(...)' shall ever invoke this constructor.
091    //
092    // NOTE: This only became necessary because of the MEM_COPY_HEAP optimization.  This
093    //       optimization expects that there is already a TagNode with element 'tok' in
094    //       the TreeSet, which is always OK - except for the method that CREATES NEW HTML
095    //       TAGS... a.k.a. HTMLTags.addTag(String).
096
097    TagNode(String token, TC openOrClosed)
098    {
099        super("<" + ((openOrClosed == TC.ClosingTags) ? "/" : "") + token + ">");
100
101        // ONLY CHANGE CASE HERE, NOT IN PREVIOUS-LINE.  PAY ATTENTION.  
102        this.tok = token.toLowerCase();
103
104        this.isClosing = (openOrClosed == TC.ClosingTags) ? true : false;
105    }
106
107
108    // ********************************************************************************************
109    // ********************************************************************************************
110    // Public Constructors
111    // ********************************************************************************************
112    // ********************************************************************************************
113
114
115    /**
116     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_C_DESC_1>
117     * 
118     * @param s Any valid HTML tag, for instance: {@code <H1>, <A HREF="somoe url">,
119     * <DIV ID="some id">} etc...
120     * 
121     * @throws MalformedTagNodeException If the passed {@code String} wasn't valid - meaning <I>it
122     * did not match the regular-expression {@code parser}.</I> 
123     * 
124     * @throws HTMLTokException If the {@code String} found where the usual HTML token-element is
125     * situated <I>is not a valid HTML element</I> then the {@code HTMLTokException} will be
126     * thrown.
127     * 
128     * @see HTMLTags#getTag_MEM_HEAP_CHECKOUT_COPY(String)
129     */
130    public TagNode(String s)
131    {
132        super(s);
133
134        // If the second character of the string is a forward-slash, this must be a closing-element
135        // For Example: </SPAN>, </DIV>, </A>, etc...
136
137        isClosing = s.charAt(1) == '/';
138
139        // This is the Element & Attribute Matcher used by the RegEx Parser.  If this Matcher
140        // doesn't find a match, the parameter 's' cannot be a valid HTML Element.  NOTE: The
141        // results of this matcher are also used to retrieve attribute-values, but here below,
142        // its results are ignored.
143
144        Matcher m = HTMLRegEx.P1.matcher(s);
145
146        if (! m.find()) throw new MalformedTagNodeException(
147            "The parser's regular-expression did not match the constructor-string.\n" +
148            "The exact input-string was: [" + s + "]\n" +
149            "NOTE:  The parameter-string is included as a field (ex.str) to this Exception.", s
150        );
151
152        if ((m.start() != 0) || (m.end() != s.length()))
153
154            throw new MalformedTagNodeException(
155                "The parser's regular-expression did not match the entire-string-length of the " +
156                "string-parameter to this constructor: m.start()=" + m.start() + ", m.end()=" + 
157                m.end() + ".\nHowever, the length of the Input-Parameter String was " +
158                '[' + s.length() + "]\nThe exact input-string was: [" + s + "]\nNOTE: The " +
159                "parameter-string is included as a field (ex.str) to this Exception.", s
160            );
161
162        // MINOR/MAJOR IMPROVEMENT... REUSE THE "ALLOCATED STRING TOKEN" from HTMLTag's class
163        // THINK: Let the Garbage Collector take out as many duplicate-strings as is possible..
164        // AND SOONER.  DECEMBER 2019: "Optimization" or ... "Improvement"
165        // 
166        // Get a copy of the 'tok' string that was already allocated on the heap; (OPTIMIZATON)
167        //
168        // NOTE: There are already myriad strings for the '.str' field.
169        // 
170        // ALSO: Don't pay much attention to this line if it doesn't make sense... it's not
171        //       that important.  If the HTML Token found was not a valid HTML5 token, this field
172        //       will be null.
173        // 
174        // Java 14+ has String.intern() - that's what this is....
175
176        this.tok = HTMLTags.getTag_MEM_HEAP_CHECKOUT_COPY(m.group(1));
177
178        // Now do the usual error check.
179        if (this.tok == null) throw new HTMLTokException(
180            "The HTML Tag / Token Element that is specified by the input string " +
181            "[" + m.group(1).toLowerCase() + "] is not a valid HTML Element Name.\n" +
182            "The exact input-string was: [" + s + "]"
183        );
184    }
185
186    /**
187     * Convenience Constructor.
188     * <BR />Invokes: {@link #TagNode(String, Properties, Iterable, SD, boolean)}
189     * <BR />Passes: null to the Boolean / Key-Only Attributes {@code Iterable}
190     */
191    public TagNode(
192            String      tok,
193            Properties  attributes,
194            SD          quotes,
195            boolean     addEndingForwardSlash
196        ) 
197    {
198        this(
199            tok,
200            GeneralPurpose.generateElementString(
201                tok,
202                attributes,
203                null, // keyOnlyAttributes,
204                quotes,
205                addEndingForwardSlash
206            ));
207    }
208
209    /**
210     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_C_DESC_2>
211     * @param tok                   <EMBED CLASS='external-html' DATA-FILE-ID=TN_C_TOK>
212     * @param attributes            <EMBED CLASS='external-html' DATA-FILE-ID=TN_C_ATTRIBUTES>
213     * @param keyOnlyAttributes     <EMBED CLASS='external-html' DATA-FILE-ID=TN_C_KO_ATTRIBUTES>
214     * @param quotes                <EMBED CLASS='external-html' DATA-FILE-ID=TN_C_QUOTES>
215     * @param addEndingForwardSlash <EMBED CLASS='external-html' DATA-FILE-ID=TN_C_AEFS>
216     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=IT_KEY_EX_PROP_TN>
217     * @throws QuotesException      <EMBED CLASS='external-html' DATA-FILE-ID=QEX>
218     * 
219     * @throws HTMLTokException if an invalid HTML 4 or 5 token is not present
220     * <B>(check is {@code CASE_INSENSITIVE})</B>, or a token which has been registered with class
221     * {@code HTMLTags}.
222     * 
223     * @see InnerTagKeyException#check(String, String)
224     * @see QuotesException#check(String, SD, String)
225     */
226    public TagNode(
227            String              tok,
228            Properties          attributes,
229            Iterable<String>    keyOnlyAttributes,
230            SD                  quotes,
231            boolean             addEndingForwardSlash
232        )
233    {
234        this(
235            tok,
236            GeneralPurpose.generateElementString
237                (tok, attributes, keyOnlyAttributes, quotes, addEndingForwardSlash)
238        );
239    }
240
241
242    // ********************************************************************************************
243    // ********************************************************************************************
244    // HTMLNode Overidden - Loop & Stream Optimization Methods
245    // ********************************************************************************************
246    // ********************************************************************************************
247
248
249    /**
250     * This method identifies that {@code 'this'} instance of (abstract parent-class)
251     * {@link HTMLNode} is, indeed, an instance of sub-class {@code TagNode}.
252     *
253     * <BR /><BR /><B CLASS=JDDescLabel>Final Method:</B>
254     * 
255     * <BR />This method is final, and cannot be modified by sub-classes.
256     * 
257     * @return This method shall always return {@code TRUE}  It overrides the parent-class
258     * {@code HTMLNode} method {@link #isTagNode()}, which always returns {@code FALSE}.
259     */
260    @Override
261    public final boolean isTagNode() { return true; }
262
263    /**
264     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_IF_TN_DESC>
265     * <BR /><BR /><B CLASS=JDDescLabel>Final Method:</B>
266     * <BR />This method is final, and cannot be modified by sub-classes.
267     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_IF_TN_RET>
268     */
269    @Override
270    public final TagNode ifTagNode() { return this; }
271
272    /**
273     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_OPENTAG_PWA_DESC>
274     * <BR /><BR /><B CLASS=JDDescLabel>Final Method:</B>
275     * <BR />This method is final, and cannot be modified by sub-classes.
276     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_OPENTAG_PWA_RET>
277     */
278    @Override
279    public final TagNode openTagPWA()
280    {
281        // Closing TagNode's simply may not have attributes
282        if (this.isClosing) return null;
283
284        // A TagNode whose '.str' field is not AT LEAST 4 characters LONGER than the length of the
285        // HTML-Tag / Token, simply cannot have an attribute.
286        //
287        // NOTE: Below is the shortest possible HTML tag that could have an attribute.
288        // COMPUTE: '<' + TOK.LENGTH + SPACE + 'c' + '>'
289
290        if (this.str.length() < (this.tok.length() + 4)) return null;
291
292        // This TagNode is an opening HTML tag (like <DIV ...>, rather than </DIV>),
293        // and there are at least two additional characters after the token, such as: <DIV A...>
294        // It is not guaranteed that this tag has attributes, but it is possibly - based on these
295        /// optimization methods, and further investigation would have merit.
296
297        return this;
298    }
299
300    /**
301     * This is a loop-optimization method that makes finding opening {@code TagNode's} - <B>with
302     * attribute-</B><B STYLE='color: red;'>values</B> - quites a bit faster.  All {@link HTMLNode}
303     * subclasses implement this method, but only {@code TagNode} instances will ever return a
304     * non-null value.
305     * 
306     * <BR /><BR /><B CLASS=JDDescLabel>Final Method:</B>
307     * 
308     * <BR />This method is final, and cannot be modified by sub-classes.
309     * 
310     * @return Returns null if and only if {@code 'this'} instance' {@link #isClosing} field is
311     * false.  When a non-null return-value is acheived, that value will always be {@code 'this'}
312     * instance.
313     */
314    @Override
315    public final TagNode openTag()
316    { return isClosing ? null : this; }
317
318    /**
319     * This method is an optimization method that overrides the one by the same name in class
320     * {@link HTMLNode}.
321     * 
322     * {@inheritdoc}
323     */
324    @Override
325    public boolean isOpenTagPWA()
326    {
327        if (this.isClosing) return false;
328        if (this.str.length() < (this.tok.length() + 4)) return false;
329        return true;
330    }
331
332    /**
333     * This method is an optimization method that overrides the one by the same name in class
334     * {@link HTMLNode}.
335     * 
336     * {@inheritdoc}
337     */
338    @Override
339    public boolean isOpenTag()
340    { return ! isClosing; }
341
342
343    // ********************************************************************************************
344    // ********************************************************************************************
345    // isTag
346    // ********************************************************************************************
347    // ********************************************************************************************
348
349
350    /**
351     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG1_DESC>
352     * @param possibleTags  A non-null list of potential HTML tags to be checked again {@link #tok}
353     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG1_RET>
354     * @see                 #tok
355     */
356    public boolean isTag(String... possibleTags)
357    { 
358        for (String htmlTag : possibleTags) if (htmlTag.equalsIgnoreCase(this.tok)) return true;
359        return false;
360    }
361
362    /**
363     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG_EX1_DESC>
364     * @param possibleTags  A non-null list of potential HTML tags to be checked again {@link #tok}
365     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG_EX1_RET>
366     * @see                 #tok
367     * @see                 #isTag(String[])
368     */
369    public boolean isTagExcept(String... possibleTags)
370    { 
371        for (String htmlTag : possibleTags) if (htmlTag.equalsIgnoreCase(this.tok)) return false;
372        return true;
373    }
374
375    /**
376     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG2_DESC>
377     * @param tagCriteria   <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG2_PARAM>
378     * @param possibleTags  A non-null list of potential HTML tags to be checked again {@link #tok}
379     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG2_RET>
380     * @see                 #tok
381     */
382    @LinkJavaSource(handle="IsTag", name="isTag")
383    public boolean isTag(TC tagCriteria, String... possibleTags)
384    { return IsTag.isTag(this, tagCriteria, possibleTags); }
385
386    /**
387     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG_EX2_DESC>
388     * @param tagCriteria   <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG_EX2_PARAM>
389     * @param possibleTags  A non-null list of potential HTML tags to be checked again {@link #tok}
390     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_IS_TAG_EX2_RET>
391     * @see                 #tok
392     */
393    @LinkJavaSource(handle="IsTag", name="isTagExcept")
394    public boolean isTagExcept(TC tagCriteria, String... possibleTags)
395    { return IsTag.isTagExcept(this, tagCriteria, possibleTags); }
396
397
398    // ********************************************************************************************
399    // ********************************************************************************************
400    // Main Method 'AV'
401    // ********************************************************************************************
402    // ********************************************************************************************
403
404
405    /**
406     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_AV_DESC>
407     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_AV_DESC_EXAMPLE>
408     * @param innerTagAttribute <EMBED CLASS='external-html' DATA-FILE-ID=TN_AV_ITA>
409     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_AV_RET>
410     * @see StringParse#ifQuotesStripQuotes(String)
411     */
412    @LinkJavaSource(handle="GetSetAttr", name="AV")
413    public String AV(String innerTagAttribute)
414    { return GetSetAttr.AV(this, innerTagAttribute, false); }
415
416    /**
417     * Identical to {@link #AV(String)}, except that if the
418     * Attribute-<B STYLE='color: red;'>value</B> had quotes surrounding it, those are included in
419     * the returned {@code String}.
420     */
421    // To-Do, finish this after brekfast
422    // public String preserveQuotesAV(String innerTagAttribute)
423    // { return GetSetAttr.AV(this, innerTagAttribute, true); }
424
425    /**
426     * <B STYLE='color: red;'>AVOPT: Attribute-Value - Optimized</B>
427     * 
428     * <BR /><BR /> This is an "optimized" version of method {@link #AV(String)}.  This method does
429     * the exact same thing as {@code AV(...)}, but leaves out parameter-checking and
430     * error-checking. This is used internally (and repeatedly) by the NodeSearch Package Search
431     * Loops.
432     * 
433     * @param innerTagAttribute This is the inner-tag / attribute <B STYLE='color: red;'>name</B>
434     * whose <B STYLE='color: red;'>value</B> is hereby being requested.
435     * 
436     * @return {@code String}-<B STYLE='color: red;'>value</B> of this inner-tag / attribute.
437     * 
438     * @see StringParse#ifQuotesStripQuotes(String)
439     * @see #str
440     */
441    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
442    public String AVOPT(String innerTagAttribute)
443    {
444        // COPIED DIRECTLY FROM class TagNode, leaves off initial tests.
445
446        // Matches "Attribute / Inner-Tag Key-Value" Pairs.
447        Matcher m = AttrRegEx.KEY_VALUE_REGEX.matcher(this.str);
448
449        // This loop iterates the KEY_VALUE PAIRS THAT HAVE BEEN FOUND.
450        /// NOTE: The REGEX Matches on Key-Value Pairs.
451
452        while (m.find())
453
454            // m.group(2) is the "KEY" of the Attribute KEY-VALUE Pair
455            // m.group(3) is the "VALUE" of the Attribute.
456
457            if (m.group(2).equalsIgnoreCase(innerTagAttribute))
458                return StringParse.ifQuotesStripQuotes(m.group(3));
459
460        // This means the attribute name provided to parameter 'innerTagAttribute' was not found.
461        return null;
462    }
463
464
465    // ********************************************************************************************
466    // ********************************************************************************************
467    // Attribute Modify-Value methods
468    // ********************************************************************************************
469    // ********************************************************************************************
470
471
472    /**
473     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV_DESC>
474     * @param attribute <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV_ATTR>
475     * 
476     * @param value Any valid attribute-<B STYLE='color: red;'>value</B>.  This parameter may not
477     * be null, or a {@code NullPointerException} will throw.
478     * 
479     * @param quote                     <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV_QUOTE>
480     * @throws InnerTagKeyException     <EMBED CLASS='external-html' DATA-FILE-ID=IT_KEY_EX_TN>
481     * @throws QuotesException          <EMBED CLASS='external-html' DATA-FILE-ID=QEX>
482     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
483     * 
484     * @throws HTMLTokException If an invalid HTML 4 or 5 token is not present 
485     * (<B>{@code CASE_INSENSITIVE}</B>).
486     * 
487     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV_RET>
488     * @see ClosingTagNodeException#check(TagNode)
489     * @see #setAV(Properties, SD)
490     * @see #str
491     * @see #isClosing
492     */
493    @LinkJavaSource(handle="GetSetAttr", name="setAV", paramCount=4)
494    public TagNode setAV(String attribute, String value, SD quote)
495    { return GetSetAttr.setAV(this, attribute, value, quote); }
496
497    /**
498     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV_NQ_DESC>
499     * @param attribute <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV_ATTR>
500     * @param value <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV_NQ_VALUE>
501     * @return An updated instance, with the new Attribute-<B STYLE='color: red;'>value</B> set.
502     * @throws InnerTagValueException   If <B STYLE='color:red'>value</B> contains whitespace.
503     * @throws InnerTagKeyException     <EMBED CLASS='external-html' DATA-FILE-ID=IT_KEY_EX_TN>
504     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
505     * @see ClosingTagNodeException#check(TagNode)
506     * @see StringParse#hasWhiteSpace(String)
507     */
508    @LinkJavaSource(handle="GetSetAttr", name="setAVNoQuotes")
509    public TagNode setAVNoQuotes(String attribute, String value)
510    { return GetSetAttr.setAVNoQuotes(this, attribute, value); }
511
512    /**
513     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV2_DESC>
514     * @param attributes These are the new attribute <B STYLE='color: red;'>key-value</B> pairs to
515     * be inserted.
516     * 
517     * @param defaultQuote <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV2_DQ_PARAM>
518     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=IT_KEY_EX_PROP_TN>
519     * 
520     * @throws QuotesException if there are "quotes within quotes" problems, due to the
521     * <B STYLE='color: red;'>values</B> of the <B STYLE='color: red;'>key-value</B> pairs.
522     * 
523     * @throws HTMLTokException if an invalid HTML 4 or 5 token is not present 
524     * <B>({@code CASE_INSENSITIVE})</B>
525     * 
526     * @throws ClosingTagNodeException <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
527     *
528     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV2_RET>
529     * @see ClosingTagNodeException#check(TagNode)
530     * @see #setAV(String, String, SD)
531     * @see #isClosing
532     */
533    @LinkJavaSource(handle="GetSetAttr", name="setAV", paramCount=3)
534    public TagNode setAV(Properties attributes, SD defaultQuote)
535    { return GetSetAttr.setAV(this, attributes, defaultQuote); }
536
537    /**
538     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_APD_AV_DESC>
539     * @param attribute                 <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_ATTR>
540     * @param appendStr                 <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_APDSTR>
541     * @param startOrEnd                <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_S_OR_E>
542     * @param quote                     <EMBED CLASS=external-html DATA-FILE-ID=TGND_QUOTE_EXPL>
543     *                                  <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_QXTRA>
544     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_RET>
545     * @see                             #AV(String)
546     * @see                             #setAV(String, String, SD)
547     * @see                             ClosingTagNodeException#check(TagNode)
548     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
549     * 
550     * @throws QuotesException The <B><A HREF=#QUOTEEX>rules</A></B> for quotation usage apply
551     * here too, and see that explanation for how how this exception could be thrown.
552     */
553    @LinkJavaSource(handle="GetSetAttr", name="appendToAV")
554    public TagNode appendToAV(String attribute, String appendStr, boolean startOrEnd, SD quote)
555    { return GetSetAttr.appendToAV(this, attribute, appendStr, startOrEnd, quote); }   
556
557
558    // ********************************************************************************************
559    // ********************************************************************************************
560    // Attribute Removal Operations
561    // ********************************************************************************************
562    // ********************************************************************************************
563
564
565    /**
566     * Convenience Method.
567     * <BR />See Documentation: {@link #removeAttributes(String[])}
568     */
569    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
570    public TagNode remove(String attributeName)
571    {
572        return RemoveAttributes.removeAttributes
573            (this, (String attr) -> ! attr.equalsIgnoreCase(attributeName));
574    }
575
576    /**
577     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ATTR_DESC>
578     * @param attributes                <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ATTR_ATTR>
579     * @return                          <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ATTR_RET>
580     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
581     * @see                             ClosingTagNodeException#check(TagNode)
582     */
583    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
584    public TagNode removeAttributes(String... attributes)
585    {
586        return RemoveAttributes.removeAttributes
587            (this, (String attr) -> StrCmpr.equalsNAND_CI(attr, attributes));
588    }
589
590    /**
591     * Filter's attributes using an Attribute-<B STYLE='color:red'>Name</B>
592     * {@code String-Predicate}
593     * 
594     * @param attrNameTest Any Java {@code String-Predicate}.  It will be used to test whether or
595     * not to keep or filter/reject an attribute from {@code 'this' TagNode}.
596     * 
597     * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Like all filter-{@code Predicate's}, this
598     * test's expected behavior is such that it should return {@code TRUE} when it would like to 
599     * keep an attribute having a particular <B STYLE='color: red;'>name</B>, and return
600     * {@code FALSE} when it would like to see the attribute removed from the HTML Tag.
601     * 
602     * @return Removes any Attributes whoe <B STYLE='color: red;'>name</B> as per the rules of the
603     * User-Provided {@code String-Predicate} parameter {@code 'attrNameTest'}.  As with all 
604     * {@code TagNode} modification operations, if any changes are, indeed, made to a new instance 
605     * of {@code TagNode} will be created and returned.
606     */
607    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
608    public TagNode removeAttributes(Predicate<String> attrNameTest)
609    { return RemoveAttributes.removeAttributes(this, attrNameTest); }
610
611    /**
612     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ALL_AV_DESC>
613     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_REM_ALL_AV_RET>
614     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
615     * @see                             ClosingTagNodeException#check(TagNode)
616     * @see                             #getInstance(String, TC)
617     * @see                             TC#OpeningTags
618     */
619    public TagNode removeAllAV()
620    {
621        ClosingTagNodeException.check(this);
622
623        // NOTE: We *CANNOT* use the 'tok' field to instantiate the TagNode here, because the 'tok'
624        // String-field is *ALWAYS* guaranteed to be in a lower-case format.  The 'str'
625        // String-field, however uses the original case that was found on the HTML Document by the
626        // parser (or in the Constructor-Parameters that were passed to construct 'this' instance
627        // of TagNode.
628
629        return getInstance(this.str.substring(1, 1 + tok.length()), TC.OpeningTags);
630    }
631
632
633    // ********************************************************************************************
634    // ********************************************************************************************
635    // Retrieve all attributes
636    // ********************************************************************************************
637    // ********************************************************************************************
638
639
640    /**
641     * Convenience Method.
642     * <BR />See Documentation: {@link #allAV(boolean, boolean)}
643     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case.
644     */
645    @LinkJavaSource(handle="RetrieveAllAttr", name="allAV")
646    public Properties allAV()
647    { return RetrieveAllAttr.allAV(this, false, false);  }
648
649    /**
650     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_DESC>
651     * @param keepQuotes        <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_KQ_PARAM>
652     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_PKC_PARAM>
653     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
654     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_RET>
655     * @see                     StringParse#ifQuotesStripQuotes(String)
656     */
657    @LinkJavaSource(handle="RetrieveAllAttr", name="allAV")
658    public Properties allAV(boolean keepQuotes, boolean preserveKeysCase)
659    { return RetrieveAllAttr.allAV(this, keepQuotes, preserveKeysCase); }
660
661    /**
662     * Convenience Method.
663     * <BR />See Documentation: {@link #allAN(boolean, boolean)}
664     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case
665     */
666    @LinkJavaSource(handle="RetrieveAllAttr", name="allAN")
667    public Stream<String> allAN()
668    { return RetrieveAllAttr.allAN(this, false, false); }
669
670    /**
671     * This method will only return a list of attribute-<B STYLE='color: red;'>names</B>.  The
672     * attribute-<B STYLE="color: red">values</B> shall <B>NOT</B> be included in the result.  The
673     * {@code String's} returned can have their "case-preserved" by passing {@code TRUE} to the
674     * input boolean parameter {@code 'preserveKeysCase'}.
675     *
676     * @param preserveKeysCase If this is parameter receives {@code TRUE} then the case of the
677     * attribute-<B STYLE='color: red;'>names</B> shall be preserved.
678     *
679     * <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
680     *
681     * @param includeKeyOnlyAttributes When this parameter receives {@code TRUE}, then any
682     * "Boolean Attributes" or "Key-Only, No-Value-Assignment" Inner-Tags will <B>ALSO</B> be
683     * included in the {@code Stream<String>} returned by this method.
684     *
685     * @return an instance of {@code Stream<String>} containing all
686     * attribute-<B STYLE='color: red;'>names</B> identified in {@code 'this'} instance of
687     * {@code TagNode}.  A {@code java.util.stream.Stream} is used because it's contents can easily
688     * be converted to just about any data-type.  
689     *
690     * <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
691     *
692     * <BR /><B>NOTE:</B> This method shall never return {@code 'null'} - even if there are no 
693     * attribute <B STYLE='color: red;'>key-value</B> pairs contained by {@code 'this' TagNode}.
694     * If there are strictly zero attributes, an empty {@code Stream} shall be returned, instead.
695     * 
696     * @see #allKeyOnlyAttributes(boolean)
697     * @see #allAN()
698     */
699    @LinkJavaSource(handle="RetrieveAllAttr", name="allAN")
700    public Stream<String> allAN(boolean preserveKeysCase, boolean includeKeyOnlyAttributes)
701    { return RetrieveAllAttr.allAN(this, preserveKeysCase, includeKeyOnlyAttributes); }
702
703
704    // ********************************************************************************************
705    // ********************************************************************************************
706    // Key only attributes
707    // ********************************************************************************************
708    // ********************************************************************************************
709
710
711    /**
712     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_KOA_DESC>
713     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_KOA_PKC>
714     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
715     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_KOA_RET>
716     *                          <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
717     */
718    @LinkJavaSource(handle="KeyOnlyAttributes", name="allKeyOnlyAttributes")
719    public Stream<String> allKeyOnlyAttributes(boolean preserveKeysCase)
720    { return KeyOnlyAttributes.allKeyOnlyAttributes(this, preserveKeysCase); }
721
722    /**
723     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_DESC>
724     * @param keyOnlyAttribute          <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_KOA>
725     * @return                          <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_RET>
726     * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_IAEX>
727     */
728    @LinkJavaSource(handle="KeyOnlyAttributes", name="hasKeyOnlyAttribute")
729    public boolean hasKeyOnlyAttribute(String keyOnlyAttribute)
730    { return KeyOnlyAttributes.hasKeyOnlyAttribute(this, keyOnlyAttribute); }
731
732
733    // ********************************************************************************************
734    // ********************************************************************************************
735    // testAV
736    // ********************************************************************************************
737    // ********************************************************************************************
738
739
740    /**
741     * <BR>See Documentation: {@link #testAV(String, Predicate)}
742     * <BR>Passes: {@code String.equalsIgnoreCase(attributeValue)} to the Test-{@code Predicate}
743     */
744    @IntoHTMLTable(background=GreenDither, title="Test an Attribute-Value using String-Equality")
745    @LinkJavaSource(handle="HasAndTest", name="testAV")
746    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
747    public boolean testAV(String attributeName, String attributeValue)
748    {
749        return HasAndTest.testAV
750            (this, attributeName, (String s) -> s.equalsIgnoreCase(attributeValue));
751    }
752
753    /**
754     * <BR>See Documentation: {@link #testAV(String, Predicate)}
755     * <BR>Passes: {@code attributeValueTest.asPredicate()} to the Test-{@code Predicate}
756     */
757    @IntoHTMLTable(background=BlueDither, title="Test an Attribute-Value using a Reg-Ex")
758    @LinkJavaSource(handle="HasAndTest", name="testAV")
759    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
760    public boolean testAV(String attributeName, Pattern attributeValueTest)
761    { return HasAndTest.testAV(this, attributeName, attributeValueTest.asPredicate()); }
762
763    /**
764     * <BR>See Documentation: {@link #testAV(String, Predicate)}
765     * <BR>Passes: {@link TextComparitor#test(String, String[])} to the Test-{@code Predicate}
766     */
767    @IntoHTMLTable(background=GreenDither, title="Test an Attribute-Value using a Text-Comparitor")
768    @LinkJavaSource(handle="HasAndTest", name="testAV")
769    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
770    public boolean testAV
771        (String attributeName, TextComparitor attributeValueTester, String... compareStrs)
772    {
773        return HasAndTest.testAV
774            (this, attributeName, (String s) -> attributeValueTester.test(s, compareStrs));
775    }
776
777    /**
778     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TEST_AV_DESC>
779     * @param attributeName         <EMBED CLASS='external-html' DATA-FILE-ID=TN_TEST_AV_PARAM>
780     * @param attributeValueTest    Any {@code java.util.function.Predicate<String>}
781     * @return                      <EMBED CLASS='external-html' DATA-FILE-ID=TN_TEST_AV_RET>
782     * @see                         StringParse#ifQuotesStripQuotes(String)
783     */
784    @LinkJavaSource(handle="HasAndTest", name="testAV")
785    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
786    public boolean testAV(String attributeName, Predicate<String> attributeValueTest)
787    { return HasAndTest.testAV(this, attributeName, attributeValueTest); }
788
789
790    // ********************************************************************************************
791    // ********************************************************************************************
792    // has-attribute boolean-logic methods
793    // ********************************************************************************************
794    // ********************************************************************************************
795
796
797    /**
798     * <BR>See Documentation: {@link #hasXOR(boolean, String...)}
799     * <BR>Passes: AND Boolean Logic
800     * <BR>COL-SPAN-2:
801     * Check that <B STYLE='color: red;'><I>all</I></B> Attributes are Found
802     */
803    @IntoHTMLTable(background=GreenDither,
804        title="Check that <B><I>every</I></B> Attribute which has been specified is Present")
805    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
806    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
807    public boolean hasAND(boolean checkAttributeStringsForErrors, String... attributes)
808    {
809        // First-Function:  Tells the logic to *IGNORE* intermediate matches (returns NULL)
810        //                  (This is *AND*, so wait until all attributes have been found, or at
811        //                  the very least all tags in the element tested, and failed.
812        //
813        // Second-Function: At the End of the Loops, all Attributes have either been found, or
814        //                  at least all attributes in 'this' tag have been tested.  Note that the
815        //                  first-function is only called on a MATCH, and that 'AND' requires to
816        //                  defer a response until all attributes have been tested..  Here, simply
817        //                  RETURN WHETHER OR NOT the MATCH-COUNT equals the number of matches in
818        //                  the user-provided String-array.
819
820        return HasAndTest.hasLogicOp(
821            this,
822            checkAttributeStringsForErrors,
823            (int matchCount) -> null,
824            (int matchCount) -> (matchCount == attributes.length),
825            attributes
826        );
827    }
828
829    /**
830     * <BR>See Documentation: {@link #hasXOR(boolean, String...)}
831     * <BR>Passes: OR Boolean Logic
832     * <BR>COL-SPAN-2:
833     * Checks that <B STYLE='color: red;'><I>at least one</I></B> of the Attributes Match
834     */
835    @IntoHTMLTable(background=BlueDither,
836        title="Check whether or not <B><I>any</I></B> of the Specified-Attributes are Present")
837    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
838    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
839    public boolean hasOR(boolean checkAttributeStringsForErrors, String... attributes)
840    {
841        // First-Function:  Tells the logic to return TRUE on any match IMMEDIATELY
842        //
843        // Second-Function: At the End of the Loops, all Attributes have been tested.  SINCE the
844        //                  previous function returns on match immediately, AND SINCE this is an
845        //                  OR, therefore FALSE must be returned (since there were no matches!)
846
847        return HasAndTest.hasLogicOp(
848            this,
849            checkAttributeStringsForErrors,
850            (int matchCount) -> true,
851            (int matchCount) -> false,
852            attributes
853        );
854    }
855
856    /**
857     * <BR>See Documentation: {@link #hasXOR(boolean, String...)}
858     * <BR>Passes: NAND Boolean Logic
859     * <BR>COL-SPAN-2:
860     * Check that <B STYLE='color: red;'><I>none</I></B> of the Attributes Match
861     */
862    @IntoHTMLTable(background=GreenDither,
863        title="Check that <B><I>none</I></B> of the Specified-Attributes are Present")
864    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
865    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
866    public boolean hasNAND(boolean checkAttributeStringsForErrors, String... attributes)
867    {
868        // First-Function: Tells the logic to return FALSE on any match IMMEDIATELY
869        //
870        // Second-Function: At the End of the Loops, all Attributes have been tested.  SINCE
871        //                  the previous function returns on match immediately, AND SINCE this is
872        //                  a NAND, therefore TRUE must be returned (since there were no matches!)
873
874        return HasAndTest.hasLogicOp(
875            this,
876            checkAttributeStringsForErrors,
877            (int matchCount) -> false,
878            (int matchCount) -> true,
879            attributes
880        );
881    }
882
883    /**
884     * <BR />Passes: XOR Boolean Logic
885     * <BR />Checks that <B STYLE='color: red;'><I>precisely-one</I></B> Attribute is found
886     * <BR /><BR /><IMG SRC='doc-files/img/hasAND.png' CLASS=JDIMG ALT=Example>
887     * 
888     * @param checkAttributeStringsForErrors
889     * <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_HAS_BOOL>
890     * 
891     * <BR /><BR /><B>NOTE:</B> If this method is passed a zero-length {@code String}-array to the
892     * {@code 'attributes'} parameter, this method shall exit immediately and return {@code FALSE}.
893     * 
894     * @throws InnerTagKeyException If any of the {@code 'attributes'} are not valid HTML
895     * attributes, <I><B>and</B></I> the user has passed {@code TRUE} to parameter
896     * {@code checkAttributeStringsForErrors}.
897     * 
898     * @throws NullPointerException     If any of the {@code 'attributes'} are null.
899     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
900     * @throws IllegalArgumentException If the {@code 'attributes'} parameter has length zero.
901     * @see                             InnerTagKeyException#check(String[])
902     */
903    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
904    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
905    public boolean hasXOR(boolean checkAttributeStringsForErrors, String... attributes)
906    {
907        // First-Function: Tells the logic to IGNORE the FIRST MATCH, and any matches afterwards
908        //                 should produce a FALSE result immediately
909        //                 (XOR means ==> one-and-only-one)
910        //
911        // Second-Function: At the End of the Loops, all Attributes have been tested.  Just
912        //                  return whether or not the match-count is PRECISELY ONE.
913
914        return HasAndTest.hasLogicOp(
915            this,
916            checkAttributeStringsForErrors,
917            (int matchCount) -> (matchCount == 1) ? null : false,
918            (int matchCount) -> (matchCount == 1),
919            attributes
920        );
921    }
922
923
924    // ********************************************************************************************
925    // ********************************************************************************************
926    // has methods - extended, variable attribute-names
927    // ********************************************************************************************
928    // ********************************************************************************************
929
930
931    /**
932     * <BR>See Documentation: {@link #has(Predicate)}
933     * <BR>Passes: {@code String.equalsIgnoreCase(attributeName)} as the test-{@code Predicate}
934     */
935    @IntoHTMLTable(background=GreenDither,
936        title="Check whether or not an Attribute is Present, using String-Equality")
937    @LinkJavaSource(handle="HasAndTest", name="has")
938    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
939    public boolean has(String attributeName)
940    { return HasAndTest.has(this, (String s) -> s.equalsIgnoreCase(attributeName)); }
941
942    /**
943     * <BR>See Documentation: {@link #has(Predicate)}
944     * <BR>Passes: {@code Pattern.asPredicate()}
945     */
946    @IntoHTMLTable(background=BlueDither,
947        title="Check whether or not an Attribute is Present, by means of a Reg-Ex")
948    @LinkJavaSource(handle="HasAndTest", name="has")
949    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
950    public boolean has(Pattern attributeNameRegExTest)
951    { return HasAndTest.has(this, attributeNameRegExTest.asPredicate()); }
952
953    /**
954     * <BR>See Documentation: {@link #has(Predicate)}
955     * <BR>Passes: {@link TextComparitor#test(String, String[])} as the test-{@code Predicate}
956     */
957    @IntoHTMLTable(background=GreenDither,
958        title="Check whether or not an Attribute is Present, by means of a TextComparitor")
959    @LinkJavaSource(handle="HasAndTest", name="has")
960    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
961    public boolean has(TextComparitor tc, String... compareStrs)
962    { return HasAndTest.has(this, (String s) -> tc.test(s, compareStrs)); }
963
964    /**
965     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_DESC2>
966     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_NOTE>
967     * @param attributeNameTest <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_ANT>
968     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_RET2>
969     * @see                     StrFilter
970     */
971    @LinkJavaSource(handle="HasAndTest", name="has")
972    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
973    public boolean has(Predicate<String> attributeNameTest)
974    { return HasAndTest.has(this, attributeNameTest); }
975
976
977    // ********************************************************************************************
978    // ********************************************************************************************
979    // hasValue(...) methods
980    // ********************************************************************************************
981    // ********************************************************************************************
982
983
984    /**
985     * <BR>See Documentation: {@link #hasValue(Predicate, boolean, boolean)}
986     * <BR>Passes: {@code String.equals(attributeValue)} as the test-{@code Predicate}
987     */
988    @IntoHTMLTable(background=GreenDither,
989        title="Find an Attribute by Attribute-Value, using String-Equality")
990    @LinkJavaSource(handle="HasAndTest", name="hasValue")
991    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
992    public Map.Entry<String, String> hasValue
993        (String attributeValue, boolean retainQuotes, boolean preserveKeysCase)
994    {
995        return HasAndTest.hasValue
996            (this, (String s) -> attributeValue.equals(s), retainQuotes, preserveKeysCase);
997    }
998
999    /**
1000     * <BR>See Documentation: {@link #hasValue(Predicate, boolean, boolean)}
1001     * <BR>Passes: {@code attributeValueRegExTest.asPredicate()}
1002     */
1003    @IntoHTMLTable(background=BlueDither,
1004        title="Find an Attribute by Attribute-Value, by means of a Reg-Ex")
1005    @LinkJavaSource(handle="HasAndTest", name="hasValue")
1006    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1007    public Map.Entry<String, String> hasValue
1008        (Pattern attributeValueRegExTest, boolean retainQuotes, boolean preserveKeysCase)
1009    {
1010        return HasAndTest.hasValue
1011            (this, attributeValueRegExTest.asPredicate(), retainQuotes, preserveKeysCase);
1012    }
1013
1014    /**
1015     * <BR>See Documentation: {@link #hasValue(Predicate, boolean, boolean)}
1016     * <BR>Passes: {@link TextComparitor#test(String, String[])} as the test-{@code Predicate}
1017     */
1018    @IntoHTMLTable(background=GreenDither,
1019        title="Find an Attribute by Attribute-Value, by means of a Text-Comparitor")
1020    @LinkJavaSource(handle="HasAndTest", name="hasValue")
1021    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1022    public Map.Entry<String, String> hasValue(
1023            boolean retainQuotes, boolean preserveKeysCase, TextComparitor attributeValueTester,
1024            String... compareStrs
1025        )
1026    {
1027        return HasAndTest.hasValue(
1028            this,
1029            (String s) -> attributeValueTester.test(s, compareStrs),
1030            retainQuotes,
1031            preserveKeysCase
1032        );
1033    }
1034
1035    /**
1036     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_DESC2>
1037     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_DNOTE>
1038     * @param attributeValueTest    <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_AVT>
1039     * @param retainQuotes          <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_RQ>
1040     * @param preserveKeysCase      <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_PKC>
1041     *                              <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
1042     * @return                      <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_RET2>
1043     * @see                         StrFilter
1044     */
1045    @LinkJavaSource(handle="HasAndTest", name="hasValue")
1046    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1047    public Map.Entry<String, String> hasValue
1048        (Predicate<String> attributeValueTest, boolean retainQuotes, boolean preserveKeysCase)
1049    { return HasAndTest.hasValue(this, attributeValueTest, retainQuotes, preserveKeysCase); }
1050
1051
1052    // ********************************************************************************************
1053    // ********************************************************************************************
1054    // getInstance()
1055    // ********************************************************************************************
1056    // ********************************************************************************************
1057
1058
1059    /**
1060     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_GETINST_DESC>
1061     * @param tok Any valid HTML tag.
1062     * @param openOrClosed <EMBED CLASS='external-html' DATA-FILE-ID=TN_GETINST_OOC>
1063     * @return An instance of this class
1064     * 
1065     * @throws IllegalArgumentException If parameter {@code TC openOrClose} is {@code null} or
1066     * {@code TC.Both}
1067     * 
1068     * @throws HTMLTokException If the parameter {@code String tok} is not a valid HTML-tag
1069     * 
1070     * @throws SingletonException If the token requested is a {@code singleton} (self-closing) tag,
1071     * but the Tag-Criteria {@code 'TC'} parameter is requesting a closing-version of the tag.
1072     * 
1073     * @see HTMLTags#hasTag(String, TC)
1074     * @see HTMLTags#isSingleton(String)
1075     */
1076    public static TagNode getInstance(String tok, TC openOrClosed)
1077    {
1078        if (openOrClosed == null)
1079            throw new NullPointerException("The value of openOrClosed cannot be null.");
1080
1081        if (openOrClosed == TC.Both)
1082            throw new IllegalArgumentException("The value of openOrClosed cannot be TC.Both.");
1083
1084        if (HTMLTags.isSingleton(tok) && (openOrClosed == TC.ClosingTags))
1085
1086            throw new SingletonException(
1087                "The value of openOrClosed is TC.ClosingTags, but unfortunately you have asked " +
1088                "for a [" + tok + "] HTML element, which is a singleton element, and therefore " +
1089                "cannot have a closing-tag instance."
1090            );
1091
1092        TagNode ret = HTMLTags.hasTag(tok, openOrClosed);
1093
1094        if (ret == null)
1095            throw new HTMLTokException
1096                ("The HTML-Tag provided isn't valid!\ntok: " + tok + "\nTC: " + openOrClosed);
1097
1098        return ret;
1099    }
1100
1101
1102    // ********************************************************************************************
1103    // ********************************************************************************************
1104    // Methods for "CSS Classes"
1105    // ********************************************************************************************
1106    // ********************************************************************************************
1107
1108
1109    /**
1110     * Convenience Method.
1111     * <BR />Invokes: {@link #cssClasses()}
1112     * <BR />Catches-Exception
1113     */
1114    public Stream<String> cssClassesNOCSE()
1115    { try { return cssClasses(); } catch (CSSStrException e) { return Stream.empty(); } }
1116
1117    /**
1118     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_CL_DESC>
1119     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_CL_RET>
1120     *                          <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
1121     * @throws CSSStrException  <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_CL_CSSSE>
1122     * @see                     #cssClasses()
1123     * @see                     #AV(String)
1124     * @see                     StringParse#WHITE_SPACE_REGEX
1125     * @see                     CSSStrException#check(Stream)
1126     */
1127    @LinkJavaSource(handle="ClassIDStyle", name="cssClasses")
1128    public Stream<String> cssClasses()
1129    { return ClassIDStyle.cssClasses(this); }
1130
1131    /**
1132     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_DESC>
1133     * @param quote             <EMBED CLASS='external-html' DATA-FILE-ID=TGND_QUOTE_EXPL>
1134     * @param appendOrClobber   <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_AOC>
1135     * @param cssClasses        <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_CCL>
1136     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_RET>
1137     * 
1138     * @throws CSSStrException This exception shall throw if any of the {@code 'cssClasses'} in the
1139     * var-args {@code String...} parameter do not meet the HTML 5 CSS {@code Class} naming rules.
1140     * 
1141     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
1142     * @throws QuotesException          <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_CL_QEX>
1143     * @see                             CSSStrException#check(String[])
1144     * @see                             CSSStrException#VALID_CSS_CLASS_OR_NAME_TOKEN
1145     * @see                             #appendToAV(String, String, boolean, SD)
1146     * @see                             #setAV(String, String, SD)
1147     */
1148    @LinkJavaSource(handle="ClassIDStyle", name="setCSSClasses")
1149    public TagNode setCSSClasses(SD quote, boolean appendOrClobber, String... cssClasses)
1150    { return ClassIDStyle.setCSSClasses(this, quote, appendOrClobber, cssClasses); }
1151
1152    /**
1153     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_APD_CSS_CL_DESC>
1154     * @param cssClass This is the CSS-{@code Class} name that is being inserted into
1155     * {@code 'this'} instance of {@code TagNode}
1156     * 
1157     * @param quote                     <EMBED CLASS=external-html DATA-FILE-ID=TGND_QUOTE_EXPL>
1158     * @return                          A new {@code TagNode} with updated CSS {@code Class} Name(s)
1159     * @throws CSSStrException          <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_CSS_CL_CSSSE>
1160     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
1161     * @throws QuotesException          <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_CSS_CL_QEX>
1162     * @see                             CSSStrException#check(String[])
1163     * @see                             #setAV(String, String, SD)
1164     * @see                             #appendToAV(String, String, boolean, SD)
1165     */
1166    @LinkJavaSource(handle="ClassIDStyle", name="appendCSSClass")
1167    public TagNode appendCSSClass(String cssClass, SD quote)
1168    { return ClassIDStyle.appendCSSClass(this, cssClass, quote); }
1169
1170
1171    // ********************************************************************************************
1172    // ********************************************************************************************
1173    // Methods for "CSS Style"
1174    // ********************************************************************************************
1175    // ********************************************************************************************
1176
1177
1178    /**
1179     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_STYLE_DESC>
1180     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_STYLE_DESCEX>
1181     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_STYLE_RET>
1182     */
1183    @LinkJavaSource(handle="ClassIDStyle", name="cssStyle")
1184    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="CSS_INLINE_STYLE_REGEX")
1185    public Properties cssStyle()
1186    { return ClassIDStyle.cssStyle(this); }
1187
1188    /**
1189     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_STY_DESC>
1190     * @param p                         <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_STY_P>
1191     * @param quote                     <EMBED CLASS=external-html DATA-FILE-ID=TGND_QUOTE_EXPL>
1192     * @param appendOrClobber           <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_STY_AOC>
1193     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_STY_RET>
1194     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
1195     * @throws CSSStrException          If there is an invalid CSS Style Property Name.
1196     * 
1197     * @throws QuotesException If the style-element's quotation marks are incompatible with any
1198     * and all quotation marks employed by the style-element definitions.
1199     * 
1200     * @see CSSStrException#VALID_CSS_CLASS_OR_NAME_TOKEN
1201     * @see #appendToAV(String, String, boolean, SD)
1202     * @see #setAV(String, String, SD)
1203     */
1204    @LinkJavaSource(handle="ClassIDStyle", name="setCSSStyle")
1205    public TagNode setCSSStyle(Properties p, SD quote, boolean appendOrClobber)
1206    { return ClassIDStyle.setCSSStyle(this, p, quote, appendOrClobber); }
1207
1208
1209    // ********************************************************************************************
1210    // ********************************************************************************************
1211    // Methods for "CSS ID"
1212    // ********************************************************************************************
1213    // ********************************************************************************************
1214
1215
1216    /**
1217     * Convenience Method.
1218     * <BR />Invokes: {@link #AV(String)}
1219     * <BR />Passes: {@code String "id"}, the CSS-ID attribute-<B STYLE='color: red;'>name</B>
1220     */
1221    public String getID()
1222    {
1223        String id = AV("ID");
1224        return (id == null) ? null : id.trim();
1225    }
1226
1227    /**
1228     * This merely sets the current CSS {@code 'ID'} Attribute <B STYLE='color: red;'>Value</B>.
1229     *
1230     * @param id This is the new CSS {@code 'ID'} attribute-<B STYLE='color: red;'>value</B> that
1231     * the user would like applied to {@code 'this'} instance of {@code TagNode}.
1232     * 
1233     * @param quote <EMBED CLASS='external-html' DATA-FILE-ID=TGND_QUOTE_EXPL>
1234     *
1235     * @return Returns a new instance of {@code TagNode} that has an updated {@code 'ID'} 
1236     * attribute-<B STYLE='color: red;'>value</B>.
1237     * 
1238     * @throws IllegalArgumentException This exception shall throw if an invalid
1239     * {@code String}-token has been passed to parameter {@code 'id'}.
1240     *
1241     * <BR /><BR /><B>BYPASS NOTE:</B> If the user would like to bypass this exception-check, for
1242     * instance because he / she is using a CSS Pre-Processor, then applying the general-purpose
1243     * method {@code TagNode.setAV("id", "some-new-id")} ought to suffice.  This other method will
1244     * not apply validity checking, beyond scanning for the usual "quotes-within-quotes" problems,
1245     * which is always disallowed.
1246     *
1247     * @throws ClosingTagNodeException <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
1248     * 
1249     * @see CSSStrException#VALID_CSS_CLASS_OR_NAME_TOKEN
1250     * @see #setAV(String, String, SD)
1251     */
1252    public TagNode setID(String id, SD quote)
1253    {
1254        if (! CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN_PRED.test(id))
1255
1256            throw new IllegalArgumentException(
1257                "The id parameter provide: [" + id + "], does not conform to the standard CSS " +
1258                "Names.\nEither try using the generic TagNode.setAV(\"id\", yourNewId, quote); " +
1259                "method to bypass this check, or change the value passed to the 'id' parameter " +
1260                "here."
1261            );
1262
1263        return setAV("id", id.trim(), quote);
1264    }
1265
1266
1267    // ********************************************************************************************
1268    // ********************************************************************************************
1269    // Attributes that begin with "data-..."
1270    // ********************************************************************************************
1271    // ********************************************************************************************
1272
1273
1274    /**
1275     * Convenience Method.
1276     * <BR />See Documentation: {@link #AV(String)}
1277     * <BR />Passes: {@code "data-"} prepended to parameter {@code 'dataName'} for the
1278     * attribute-<B STYLE='color:red'>name</B>
1279     */
1280    @LinkJavaSource(handle="GetSetAttr", name="AV")
1281    public String dataAV(String dataName)
1282    { return GetSetAttr.AV(this, "data-" + dataName, false); }
1283
1284    /**
1285     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_DATTR_DESC>
1286     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_REM_DATTR_RET>
1287     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=TN_REM_DATTR_CTNEX>
1288     */
1289    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
1290    public TagNode removeDataAttributes() 
1291    {
1292        return RemoveAttributes.removeAttributes
1293            (this, (String attr) -> ! StrCmpr.startsWithIgnoreCase(attr, "data-") );
1294    }
1295
1296    /**
1297     * Convenience Method.
1298     * <BR />See Documentation: {@link #getDataAV(boolean)}
1299     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case
1300     */
1301    @LinkJavaSource(handle="DataAttributes", name="getDataAV")
1302    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="DATA_ATTRIBUTE_REGEX")
1303    public Properties getDataAV()
1304    { return DataAttributes.getDataAV(this, false); }
1305
1306    /**
1307     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AV_DESC>
1308     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AV_PAR>
1309     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
1310     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AV_RET>
1311     */
1312    @LinkJavaSource(handle="DataAttributes", name="getDataAV")
1313    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="DATA_ATTRIBUTE_REGEX")
1314    public Properties getDataAV(boolean preserveKeysCase) 
1315    { return DataAttributes.getDataAV(this, preserveKeysCase); }
1316
1317    /**
1318     * Convenience Method.
1319     * <BR />See Documentation: {@link #getDataAN(boolean)}
1320     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case
1321     */
1322    public Stream<String> getDataAN()
1323    { return DataAttributes.getDataAN(this, false); }
1324
1325    /**
1326     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AN_DESC>
1327     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AN_PAR>
1328     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
1329     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AN_RET>
1330     *                          <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
1331     */
1332    @LinkJavaSource(handle="DataAttributes", name="getDataAN")
1333    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="DATA_ATTRIBUTE_REGEX")
1334    public Stream<String> getDataAN(boolean preserveKeysCase) 
1335    { return DataAttributes.getDataAN(this, preserveKeysCase); }
1336
1337
1338    // ********************************************************************************************
1339    // ********************************************************************************************
1340    // Java Methods
1341    // ********************************************************************************************
1342    // ********************************************************************************************
1343
1344
1345    /**
1346     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TOSTR_AV_DESC>
1347     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_TOSTR_AV_RET>
1348     * @see HTMLNode#toString()
1349     */
1350    @LinkJavaSource(handle="GeneralPurpose", name="toStringAV")
1351    public String toStringAV()
1352    { return GeneralPurpose.toStringAV(this); }
1353
1354    /**
1355     * Java's {@code interface Cloneable} requirements.  This instantiates a new {@code TagNode}
1356     * with identical <SPAN STYLE='color: red;'>{@code String str}</SPAN> fields, and also
1357     * identical <SPAN STYLE='color: red;'>{@code boolean isClosing}</SPAN> and
1358     * <SPAN STYLE='color: red;'>{@code String tok}</SPAN> fields.
1359     * 
1360     * @return A new {@code TagNode} whose internal fields are identical to this one.
1361     */
1362    public TagNode clone() { return new TagNode(str); }
1363
1364    /**
1365     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_COMPARETO_DESC>
1366     * @param n Any other {@code TagNode} to be compared to {@code 'this' TagNode}
1367     * @return An integer that fulfils Java's {@code Comparable} interface-method requirements.
1368     */
1369    public int compareTo(TagNode n)
1370    {
1371        // Utilize the standard "String.compare(String)" method with the '.tok' string field.
1372        // All 'tok' fields are stored as lower-case strings.
1373        int compare1 = this.tok.compareTo(n.tok);
1374
1375        // Comparison #1 will be non-zero if the two TagNode's being compared had different
1376        // .tok fields
1377        if (compare1 != 0) return compare1;
1378
1379        // If the '.tok' fields were the same, use the 'isClosing' field for comparison instead.
1380        // This comparison will only be used if they are different.
1381        if (this.isClosing != n.isClosing) return (this.isClosing == false) ? -1 : 1;
1382    
1383        // Finally try using the entire element '.str' String field, instead.  
1384        return this.str.length() - n.str.length();
1385    }
1386
1387
1388    // ********************************************************************************************
1389    // ********************************************************************************************
1390    // toUpperCase 
1391    // ********************************************************************************************
1392    // ********************************************************************************************
1393
1394
1395    /**
1396     * <EMBED CLASS=defs DATA-CASE=Upper DATA-CAPITAL="">
1397     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_DESC>
1398     * @param justTag_Or_TagAndAttributeNames 
1399     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_PARAM>
1400     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_RET>
1401     */
1402    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=3)
1403    public TagNode toUpperCase(boolean justTag_Or_TagAndAttributeNames)
1404    {
1405        return CaseChange.toCaseInternal
1406            (this, justTag_Or_TagAndAttributeNames, String::toUpperCase);
1407    }
1408
1409    /**
1410     * Convenience Method.
1411     * <BR />See Documentation: {@link #toUpperCase(boolean, Predicate)}
1412     * <BR />Passes: {@code StrCmpr.equalsXOR_CI(attrName, attributeNames)}
1413     * @see StrCmpr#equalsXOR_CI(String, String...)
1414     */
1415    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1416    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1417    public TagNode toUpperCase(boolean tag, String... attributeNames)
1418    {
1419        return CaseChange.toCaseInternal(
1420            this,
1421            tag,
1422            (String attrName) -> StrCmpr.equalsXOR_CI(attrName, attributeNames),
1423            String::toUpperCase
1424        );
1425    }
1426
1427    /**
1428     * <EMBED CLASS=defs DATA-CASE=Upper DATA-CAPITAL="">
1429     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_DESC>
1430     * @param tag           Indicates whether or not the Tag-Name should be capitalized
1431     * @param attrNameTest  <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_PARAM>
1432     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_RET>
1433     */
1434    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1435    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1436    public TagNode toUpperCase(boolean tag, Predicate<String> attrNameTest)
1437    { return CaseChange.toCaseInternal(this, tag, attrNameTest, String::toUpperCase); }
1438
1439
1440    // ********************************************************************************************
1441    // ********************************************************************************************
1442    // toLowerCase 
1443    // ********************************************************************************************
1444    // ********************************************************************************************
1445
1446
1447    /**
1448     * <EMBED CLASS=defs DATA-CASE=Lower DATA-CAPITAL="de-">
1449     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_DESC>
1450     * @param justTag_Or_TagAndAttributeNames 
1451     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_PARAM>
1452     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_RET>
1453     */
1454    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=3)
1455    public TagNode toLowerCase(boolean justTag_Or_TagAndAttributeNames)
1456    {
1457        return CaseChange.toCaseInternal
1458            (this, justTag_Or_TagAndAttributeNames, String::toLowerCase);
1459    }
1460
1461    /**
1462     * Convenience Method.
1463     * <BR />See Documentation: {@link #toLowerCase(boolean, Predicate)}
1464     * <BR />Passes: {@code StrCmpr.equalsXOR_CI(attrName, attributeNames)}
1465     * @see StrCmpr#equalsXOR_CI(String, String...)
1466     */
1467    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1468    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1469    public TagNode toLowerCase(boolean tag, String... attributeNames)
1470    {
1471        return CaseChange.toCaseInternal(
1472            this,
1473            tag,
1474            (String attrName) -> StrCmpr.equalsXOR_CI(attrName, attributeNames),
1475            String::toLowerCase
1476        );
1477    }
1478
1479    /**
1480     * <EMBED CLASS=defs DATA-CASE=Lower DATA-CAPITAL="de-">
1481     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_DESC>
1482     * @param tag           Indicates whether or not the Tag-Name should be decapitalized
1483     * @param attrNameTest  <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_PARAM>
1484     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_RET>
1485     */
1486    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1487    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1488    public TagNode toLowerCase(boolean tag, Predicate<String> attrNameTest)
1489    { return CaseChange.toCaseInternal(this, tag, attrNameTest, String::toLowerCase); }
1490
1491
1492    // ********************************************************************************************
1493    // ********************************************************************************************
1494    // Attribute-Value Quotation-Marks - REMOVE
1495    // ********************************************************************************************
1496    // ********************************************************************************************
1497
1498
1499    /**
1500     * Convenience Method.
1501     * <BR />See Documentation: {@link #removeAVQuotes(Predicate)}
1502     * <BR />Removes Quotation-Marks from <B STYLE='color: red;'>Value</B> whose Inner-Tag 
1503     * <B STYLE='color: red;'>Name</B> matches {@code 'attributeName'}
1504     */
1505    @LinkJavaSource(handle="QuotationMarks", name="removeAVQuotes")
1506    public TagNode removeAVQuotes(String attributeName)
1507    {
1508        return QuotationMarks.removeAVQuotes
1509            (this, (String attr) -> attr.equalsIgnoreCase(attributeName));
1510    }
1511
1512    /**
1513     * Convenience Method.
1514     * <BR />See Documentation: {@link #removeAVQuotes(Predicate)}
1515     * <BR />Removes Quotation-Marks from all Inner-Tag <B STYLE='color: red;'>Values</B>
1516     */
1517    @LinkJavaSource(handle="QuotationMarks", name="removeAVQuotes")
1518    public TagNode removeAllAVQuotes()
1519    { return QuotationMarks.removeAVQuotes(this, (String attr) -> true); }
1520
1521    /**
1522     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_QUOTES_DESC>
1523     * @param attrNameTest      <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_QUOTES_PARAM>
1524     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_QUOTES_RET>
1525     * @throws QuotesException  If the resulting <CODE>TagNode</CODE> contains Quotation-Errors
1526     */
1527    @LinkJavaSource(handle="QuotationMarks", name="removeAVQuotes")
1528    public TagNode removeAVQuotes(Predicate<String> attrNameTest)
1529    { return QuotationMarks.removeAVQuotes(this, attrNameTest); }
1530
1531
1532    // ********************************************************************************************
1533    // ********************************************************************************************
1534    // Attribute-Value Quotation-Marks - SET
1535    // ********************************************************************************************
1536    // ********************************************************************************************
1537
1538
1539    /**
1540     * Convenience Method.
1541     * <BR />See Documentation: {@link #setAVQuotes(Predicate, SD)}}
1542     * <BR />Set Quotation-Marks for the Attribute whose <B STYLE='color: red;'>Name</B> matches
1543     * {@code 'attributeName'}
1544     */
1545    @LinkJavaSource(handle="QuotationMarks", name="setAVQuotes")
1546    public TagNode setAVQuotes(String attributeName, SD quote)
1547    {
1548        return QuotationMarks.setAVQuotes
1549            (this, (String attr) -> attr.equalsIgnoreCase(attributeName), quote);
1550    }
1551
1552    /**
1553     * Convenience Method.
1554     * <BR />See Documentation: {@link #setAVQuotes(Predicate, SD)}}
1555     * <BR />Set the Quotation-Marks which are used with all Attribute's contained by
1556     * {@code 'this'} Tag.
1557     */
1558    @LinkJavaSource(handle="QuotationMarks", name="setAVQuotes")
1559    public TagNode setAllAVQuotes(SD quote)
1560    { return QuotationMarks.setAVQuotes(this, (String attr) -> true, quote); }
1561
1562    /**
1563     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_QUOTES_DESC>
1564     * @param attrNameTest          <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_QUOTES_PARAM>
1565     * @param quote                 The new Quotation-Mark to apply
1566     * @return                      <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_QUOTES_RET>
1567     * @throws QuotesException      If the resulting <CODE>TagNode</CODE> contains Quotation-Errors
1568     * @throws NullPointerException If either parameter is passed null.
1569     */
1570    @LinkJavaSource(handle="QuotationMarks", name="setAVQuotes")
1571    public TagNode setAVQuotes(Predicate<String> attrNameTest, SD quote)
1572    { return QuotationMarks.setAVQuotes(this, attrNameTest, quote); }
1573
1574
1575    // ********************************************************************************************
1576    // ********************************************************************************************
1577    // Attribute-Value Quotation-Marks - GET
1578    // ********************************************************************************************
1579    // ********************************************************************************************
1580
1581}