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_AV2_DESC>
499     * @param attributes These are the new attribute <B STYLE='color: red;'>key-value</B> pairs to
500     * be inserted.
501     * 
502     * @param defaultQuote <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV2_DQ_PARAM>
503     * @throws InnerTagKeyException <EMBED CLASS='external-html' DATA-FILE-ID=IT_KEY_EX_PROP_TN>
504     * 
505     * @throws QuotesException if there are "quotes within quotes" problems, due to the
506     * <B STYLE='color: red;'>values</B> of the <B STYLE='color: red;'>key-value</B> pairs.
507     * 
508     * @throws HTMLTokException if an invalid HTML 4 or 5 token is not present 
509     * <B>({@code CASE_INSENSITIVE})</B>
510     * 
511     * @throws ClosingTagNodeException <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
512     *
513     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_AV2_RET>
514     * @see ClosingTagNodeException#check(TagNode)
515     * @see #setAV(String, String, SD)
516     * @see #isClosing
517     */
518    @LinkJavaSource(handle="GetSetAttr", name="setAV", paramCount=3)
519    public TagNode setAV(Properties attributes, SD defaultQuote)
520    { return GetSetAttr.setAV(this, attributes, defaultQuote); }
521
522    /**
523     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_APD_AV_DESC>
524     * @param attribute                 <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_ATTR>
525     * @param appendStr                 <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_APDSTR>
526     * @param startOrEnd                <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_S_OR_E>
527     * @param quote                     <EMBED CLASS=external-html DATA-FILE-ID=TGND_QUOTE_EXPL>
528     *                                  <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_P_QXTRA>
529     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_AV_RET>
530     * @see                             #AV(String)
531     * @see                             #setAV(String, String, SD)
532     * @see                             ClosingTagNodeException#check(TagNode)
533     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
534     * 
535     * @throws QuotesException The <B><A HREF=#QUOTEEX>rules</A></B> for quotation usage apply
536     * here too, and see that explanation for how how this exception could be thrown.
537     */
538    @LinkJavaSource(handle="GetSetAttr", name="appendToAV")
539    public TagNode appendToAV(String attribute, String appendStr, boolean startOrEnd, SD quote)
540    { return GetSetAttr.appendToAV(this, attribute, appendStr, startOrEnd, quote); }   
541
542
543    // ********************************************************************************************
544    // ********************************************************************************************
545    // Attribute Removal Operations
546    // ********************************************************************************************
547    // ********************************************************************************************
548
549
550    /**
551     * Convenience Method.
552     * <BR />See Documentation: {@link #removeAttributes(String[])}
553     */
554    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
555    public TagNode remove(String attributeName)
556    {
557        return RemoveAttributes.removeAttributes
558            (this, (String attr) -> ! attr.equalsIgnoreCase(attributeName));
559    }
560
561    /**
562     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ATTR_DESC>
563     * @param attributes                <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ATTR_ATTR>
564     * @return                          <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ATTR_RET>
565     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
566     * @see                             ClosingTagNodeException#check(TagNode)
567     */
568    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
569    public TagNode removeAttributes(String... attributes)
570    {
571        return RemoveAttributes.removeAttributes
572            (this, (String attr) -> StrCmpr.equalsNAND_CI(attr, attributes));
573    }
574
575    /**
576     * Filter's attributes using an Attribute-<B STYLE='color:red'>Name</B>
577     * {@code String-Predicate}
578     * 
579     * @param attrNameTest Any Java {@code String-Predicate}.  It will be used to test whether or
580     * not to keep or filter/reject an attribute from {@code 'this' TagNode}.
581     * 
582     * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Like all filter-{@code Predicate's}, this
583     * test's expected behavior is such that it should return {@code TRUE} when it would like to 
584     * keep an attribute having a particular <B STYLE='color: red;'>name</B>, and return
585     * {@code FALSE} when it would like to see the attribute removed from the HTML Tag.
586     * 
587     * @return Removes any Attributes whoe <B STYLE='color: red;'>name</B> as per the rules of the
588     * User-Provided {@code String-Predicate} parameter {@code 'attrNameTest'}.  As with all 
589     * {@code TagNode} modification operations, if any changes are, indeed, made to a new instance 
590     * of {@code TagNode} will be created and returned.
591     */
592    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
593    public TagNode removeAttributes(Predicate<String> attrNameTest)
594    { return RemoveAttributes.removeAttributes(this, attrNameTest); }
595
596    /**
597     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_ALL_AV_DESC>
598     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_REM_ALL_AV_RET>
599     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
600     * @see                             ClosingTagNodeException#check(TagNode)
601     * @see                             #getInstance(String, TC)
602     * @see                             TC#OpeningTags
603     */
604    public TagNode removeAllAV()
605    {
606        ClosingTagNodeException.check(this);
607
608        // NOTE: We *CANNOT* use the 'tok' field to instantiate the TagNode here, because the 'tok'
609        // String-field is *ALWAYS* guaranteed to be in a lower-case format.  The 'str'
610        // String-field, however uses the original case that was found on the HTML Document by the
611        // parser (or in the Constructor-Parameters that were passed to construct 'this' instance
612        // of TagNode.
613
614        return getInstance(this.str.substring(1, 1 + tok.length()), TC.OpeningTags);
615    }
616
617
618    // ********************************************************************************************
619    // ********************************************************************************************
620    // Retrieve all attributes
621    // ********************************************************************************************
622    // ********************************************************************************************
623
624
625    /**
626     * Convenience Method.
627     * <BR />See Documentation: {@link #allAV(boolean, boolean)}
628     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case.
629     */
630    @LinkJavaSource(handle="RetrieveAllAttr", name="allAV")
631    public Properties allAV()
632    { return RetrieveAllAttr.allAV(this, false, false);  }
633
634    /**
635     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_DESC>
636     * @param keepQuotes        <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_KQ_PARAM>
637     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_PKC_PARAM>
638     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
639     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_AV_RET>
640     * @see                     StringParse#ifQuotesStripQuotes(String)
641     */
642    @LinkJavaSource(handle="RetrieveAllAttr", name="allAV")
643    public Properties allAV(boolean keepQuotes, boolean preserveKeysCase)
644    { return RetrieveAllAttr.allAV(this, keepQuotes, preserveKeysCase); }
645
646    /**
647     * Convenience Method.
648     * <BR />See Documentation: {@link #allAN(boolean, boolean)}
649     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case
650     */
651    @LinkJavaSource(handle="RetrieveAllAttr", name="allAN")
652    public Stream<String> allAN()
653    { return RetrieveAllAttr.allAN(this, false, false); }
654
655    /**
656     * This method will only return a list of attribute-<B STYLE='color: red;'>names</B>.  The
657     * attribute-<B STYLE="color: red">values</B> shall <B>NOT</B> be included in the result.  The
658     * {@code String's} returned can have their "case-preserved" by passing {@code TRUE} to the
659     * input boolean parameter {@code 'preserveKeysCase'}.
660     *
661     * @param preserveKeysCase If this is parameter receives {@code TRUE} then the case of the
662     * attribute-<B STYLE='color: red;'>names</B> shall be preserved.
663     *
664     * <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
665     *
666     * @param includeKeyOnlyAttributes When this parameter receives {@code TRUE}, then any
667     * "Boolean Attributes" or "Key-Only, No-Value-Assignment" Inner-Tags will <B>ALSO</B> be
668     * included in the {@code Stream<String>} returned by this method.
669     *
670     * @return an instance of {@code Stream<String>} containing all
671     * attribute-<B STYLE='color: red;'>names</B> identified in {@code 'this'} instance of
672     * {@code TagNode}.  A {@code java.util.stream.Stream} is used because it's contents can easily
673     * be converted to just about any data-type.  
674     *
675     * <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
676     *
677     * <BR /><B>NOTE:</B> This method shall never return {@code 'null'} - even if there are no 
678     * attribute <B STYLE='color: red;'>key-value</B> pairs contained by {@code 'this' TagNode}.
679     * If there are strictly zero attributes, an empty {@code Stream} shall be returned, instead.
680     * 
681     * @see #allKeyOnlyAttributes(boolean)
682     * @see #allAN()
683     */
684    @LinkJavaSource(handle="RetrieveAllAttr", name="allAN")
685    public Stream<String> allAN(boolean preserveKeysCase, boolean includeKeyOnlyAttributes)
686    { return RetrieveAllAttr.allAN(this, preserveKeysCase, includeKeyOnlyAttributes); }
687
688
689    // ********************************************************************************************
690    // ********************************************************************************************
691    // Key only attributes
692    // ********************************************************************************************
693    // ********************************************************************************************
694
695
696    /**
697     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_KOA_DESC>
698     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_KOA_PKC>
699     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
700     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_ALL_KOA_RET>
701     *                          <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
702     */
703    @LinkJavaSource(handle="KeyOnlyAttributes", name="allKeyOnlyAttributes")
704    public Stream<String> allKeyOnlyAttributes(boolean preserveKeysCase)
705    { return KeyOnlyAttributes.allKeyOnlyAttributes(this, preserveKeysCase); }
706
707    /**
708     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_DESC>
709     * @param keyOnlyAttribute          <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_KOA>
710     * @return                          <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_RET>
711     * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_KOA_IAEX>
712     */
713    @LinkJavaSource(handle="KeyOnlyAttributes", name="hasKeyOnlyAttribute")
714    public boolean hasKeyOnlyAttribute(String keyOnlyAttribute)
715    { return KeyOnlyAttributes.hasKeyOnlyAttribute(this, keyOnlyAttribute); }
716
717
718    // ********************************************************************************************
719    // ********************************************************************************************
720    // testAV
721    // ********************************************************************************************
722    // ********************************************************************************************
723
724
725    /**
726     * <BR>See Documentation: {@link #testAV(String, Predicate)}
727     * <BR>Passes: {@code String.equalsIgnoreCase(attributeValue)} to the Test-{@code Predicate}
728     */
729    @IntoHTMLTable(background=GreenDither, title="Test an Attribute-Value using String-Equality")
730    @LinkJavaSource(handle="HasAndTest", name="testAV")
731    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
732    public boolean testAV(String attributeName, String attributeValue)
733    {
734        return HasAndTest.testAV
735            (this, attributeName, (String s) -> s.equalsIgnoreCase(attributeValue));
736    }
737
738    /**
739     * <BR>See Documentation: {@link #testAV(String, Predicate)}
740     * <BR>Passes: {@code attributeValueTest.asPredicate()} to the Test-{@code Predicate}
741     */
742    @IntoHTMLTable(background=BlueDither, title="Test an Attribute-Value using a Reg-Ex")
743    @LinkJavaSource(handle="HasAndTest", name="testAV")
744    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
745    public boolean testAV(String attributeName, Pattern attributeValueTest)
746    { return HasAndTest.testAV(this, attributeName, attributeValueTest.asPredicate()); }
747
748    /**
749     * <BR>See Documentation: {@link #testAV(String, Predicate)}
750     * <BR>Passes: {@link TextComparitor#test(String, String[])} to the Test-{@code Predicate}
751     */
752    @IntoHTMLTable(background=GreenDither, title="Test an Attribute-Value using a Text-Comparitor")
753    @LinkJavaSource(handle="HasAndTest", name="testAV")
754    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
755    public boolean testAV
756        (String attributeName, TextComparitor attributeValueTester, String... compareStrs)
757    {
758        return HasAndTest.testAV
759            (this, attributeName, (String s) -> attributeValueTester.test(s, compareStrs));
760    }
761
762    /**
763     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TEST_AV_DESC>
764     * @param attributeName         <EMBED CLASS='external-html' DATA-FILE-ID=TN_TEST_AV_PARAM>
765     * @param attributeValueTest    Any {@code java.util.function.Predicate<String>}
766     * @return                      <EMBED CLASS='external-html' DATA-FILE-ID=TN_TEST_AV_RET>
767     * @see                         StringParse#ifQuotesStripQuotes(String)
768     */
769    @LinkJavaSource(handle="HasAndTest", name="testAV")
770    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
771    public boolean testAV(String attributeName, Predicate<String> attributeValueTest)
772    { return HasAndTest.testAV(this, attributeName, attributeValueTest); }
773
774
775    // ********************************************************************************************
776    // ********************************************************************************************
777    // has-attribute boolean-logic methods
778    // ********************************************************************************************
779    // ********************************************************************************************
780
781
782    /**
783     * <BR>See Documentation: {@link #hasXOR(boolean, String...)}
784     * <BR>Passes: AND Boolean Logic
785     * <BR>COL-SPAN-2:
786     * Check that <B STYLE='color: red;'><I>all</I></B> Attributes are Found
787     */
788    @IntoHTMLTable(background=GreenDither,
789        title="Check that <B><I>every</I></B> Attribute which has been specified is Present")
790    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
791    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
792    public boolean hasAND(boolean checkAttributeStringsForErrors, String... attributes)
793    {
794        // First-Function:  Tells the logic to *IGNORE* intermediate matches (returns NULL)
795        //                  (This is *AND*, so wait until all attributes have been found, or at
796        //                  the very least all tags in the element tested, and failed.
797        //
798        // Second-Function: At the End of the Loops, all Attributes have either been found, or
799        //                  at least all attributes in 'this' tag have been tested.  Note that the
800        //                  first-function is only called on a MATCH, and that 'AND' requires to
801        //                  defer a response until all attributes have been tested..  Here, simply
802        //                  RETURN WHETHER OR NOT the MATCH-COUNT equals the number of matches in
803        //                  the user-provided String-array.
804
805        return HasAndTest.hasLogicOp(
806            this,
807            checkAttributeStringsForErrors,
808            (int matchCount) -> null,
809            (int matchCount) -> (matchCount == attributes.length),
810            attributes
811        );
812    }
813
814    /**
815     * <BR>See Documentation: {@link #hasXOR(boolean, String...)}
816     * <BR>Passes: OR Boolean Logic
817     * <BR>COL-SPAN-2:
818     * Checks that <B STYLE='color: red;'><I>at least one</I></B> of the Attributes Match
819     */
820    @IntoHTMLTable(background=BlueDither,
821        title="Check whether or not <B><I>any</I></B> of the Specified-Attributes are Present")
822    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
823    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
824    public boolean hasOR(boolean checkAttributeStringsForErrors, String... attributes)
825    {
826        // First-Function:  Tells the logic to return TRUE on any match IMMEDIATELY
827        //
828        // Second-Function: At the End of the Loops, all Attributes have been tested.  SINCE the
829        //                  previous function returns on match immediately, AND SINCE this is an
830        //                  OR, therefore FALSE must be returned (since there were no matches!)
831
832        return HasAndTest.hasLogicOp(
833            this,
834            checkAttributeStringsForErrors,
835            (int matchCount) -> true,
836            (int matchCount) -> false,
837            attributes
838        );
839    }
840
841    /**
842     * <BR>See Documentation: {@link #hasXOR(boolean, String...)}
843     * <BR>Passes: NAND Boolean Logic
844     * <BR>COL-SPAN-2:
845     * Check that <B STYLE='color: red;'><I>none</I></B> of the Attributes Match
846     */
847    @IntoHTMLTable(background=GreenDither,
848        title="Check that <B><I>none</I></B> of the Specified-Attributes are Present")
849    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
850    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
851    public boolean hasNAND(boolean checkAttributeStringsForErrors, String... attributes)
852    {
853        // First-Function: Tells the logic to return FALSE on any match IMMEDIATELY
854        //
855        // Second-Function: At the End of the Loops, all Attributes have been tested.  SINCE
856        //                  the previous function returns on match immediately, AND SINCE this is
857        //                  a NAND, therefore TRUE must be returned (since there were no matches!)
858
859        return HasAndTest.hasLogicOp(
860            this,
861            checkAttributeStringsForErrors,
862            (int matchCount) -> false,
863            (int matchCount) -> true,
864            attributes
865        );
866    }
867
868    /**
869     * <BR />Passes: XOR Boolean Logic
870     * <BR />Checks that <B STYLE='color: red;'><I>precisely-one</I></B> Attribute is found
871     * <BR /><BR /><IMG SRC='doc-files/img/hasAND.png' CLASS=JDIMG ALT=Example>
872     * 
873     * @param checkAttributeStringsForErrors
874     * <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_HAS_BOOL>
875     * 
876     * <BR /><BR /><B>NOTE:</B> If this method is passed a zero-length {@code String}-array to the
877     * {@code 'attributes'} parameter, this method shall exit immediately and return {@code FALSE}.
878     * 
879     * @throws InnerTagKeyException If any of the {@code 'attributes'} are not valid HTML
880     * attributes, <I><B>and</B></I> the user has passed {@code TRUE} to parameter
881     * {@code checkAttributeStringsForErrors}.
882     * 
883     * @throws NullPointerException     If any of the {@code 'attributes'} are null.
884     * @throws ClosingTagNodeException  <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
885     * @throws IllegalArgumentException If the {@code 'attributes'} parameter has length zero.
886     * @see                             InnerTagKeyException#check(String[])
887     */
888    @LinkJavaSource(handle="HasAndTest", name="hasLogicOp")
889    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
890    public boolean hasXOR(boolean checkAttributeStringsForErrors, String... attributes)
891    {
892        // First-Function: Tells the logic to IGNORE the FIRST MATCH, and any matches afterwards
893        //                 should produce a FALSE result immediately
894        //                 (XOR means ==> one-and-only-one)
895        //
896        // Second-Function: At the End of the Loops, all Attributes have been tested.  Just
897        //                  return whether or not the match-count is PRECISELY ONE.
898
899        return HasAndTest.hasLogicOp(
900            this,
901            checkAttributeStringsForErrors,
902            (int matchCount) -> (matchCount == 1) ? null : false,
903            (int matchCount) -> (matchCount == 1),
904            attributes
905        );
906    }
907
908
909    // ********************************************************************************************
910    // ********************************************************************************************
911    // has methods - extended, variable attribute-names
912    // ********************************************************************************************
913    // ********************************************************************************************
914
915
916    /**
917     * <BR>See Documentation: {@link #has(Predicate)}
918     * <BR>Passes: {@code String.equalsIgnoreCase(attributeName)} as the test-{@code Predicate}
919     */
920    @IntoHTMLTable(background=GreenDither,
921        title="Check whether or not an Attribute is Present, using String-Equality")
922    @LinkJavaSource(handle="HasAndTest", name="has")
923    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
924    public boolean has(String attributeName)
925    { return HasAndTest.has(this, (String s) -> s.equalsIgnoreCase(attributeName)); }
926
927    /**
928     * <BR>See Documentation: {@link #has(Predicate)}
929     * <BR>Passes: {@code Pattern.asPredicate()}
930     */
931    @IntoHTMLTable(background=BlueDither,
932        title="Check whether or not an Attribute is Present, by means of a Reg-Ex")
933    @LinkJavaSource(handle="HasAndTest", name="has")
934    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
935    public boolean has(Pattern attributeNameRegExTest)
936    { return HasAndTest.has(this, attributeNameRegExTest.asPredicate()); }
937
938    /**
939     * <BR>See Documentation: {@link #has(Predicate)}
940     * <BR>Passes: {@link TextComparitor#test(String, String[])} as the test-{@code Predicate}
941     */
942    @IntoHTMLTable(background=GreenDither,
943        title="Check whether or not an Attribute is Present, by means of a TextComparitor")
944    @LinkJavaSource(handle="HasAndTest", name="has")
945    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
946    public boolean has(TextComparitor tc, String... compareStrs)
947    { return HasAndTest.has(this, (String s) -> tc.test(s, compareStrs)); }
948
949    /**
950     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_DESC2>
951     * <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_HAS_NOTE>
952     * @param attributeNameTest <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_ANT>
953     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_HAS_RET2>
954     * @see                     StrFilter
955     */
956    @LinkJavaSource(handle="HasAndTest", name="has")
957    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
958    public boolean has(Predicate<String> attributeNameTest)
959    { return HasAndTest.has(this, attributeNameTest); }
960
961
962    // ********************************************************************************************
963    // ********************************************************************************************
964    // hasValue(...) methods
965    // ********************************************************************************************
966    // ********************************************************************************************
967
968
969    /**
970     * <BR>See Documentation: {@link #hasValue(Predicate, boolean, boolean)}
971     * <BR>Passes: {@code String.equals(attributeValue)} as the test-{@code Predicate}
972     */
973    @IntoHTMLTable(background=GreenDither,
974        title="Find an Attribute by Attribute-Value, using String-Equality")
975    @LinkJavaSource(handle="HasAndTest", name="hasValue")
976    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
977    public Map.Entry<String, String> hasValue
978        (String attributeValue, boolean retainQuotes, boolean preserveKeysCase)
979    {
980        return HasAndTest.hasValue
981            (this, (String s) -> attributeValue.equals(s), retainQuotes, preserveKeysCase);
982    }
983
984    /**
985     * <BR>See Documentation: {@link #hasValue(Predicate, boolean, boolean)}
986     * <BR>Passes: {@code attributeValueRegExTest.asPredicate()}
987     */
988    @IntoHTMLTable(background=BlueDither,
989        title="Find an Attribute by Attribute-Value, by means of a Reg-Ex")
990    @LinkJavaSource(handle="HasAndTest", name="hasValue")
991    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
992    public Map.Entry<String, String> hasValue
993        (Pattern attributeValueRegExTest, boolean retainQuotes, boolean preserveKeysCase)
994    {
995        return HasAndTest.hasValue
996            (this, attributeValueRegExTest.asPredicate(), retainQuotes, preserveKeysCase);
997    }
998
999    /**
1000     * <BR>See Documentation: {@link #hasValue(Predicate, boolean, boolean)}
1001     * <BR>Passes: {@link TextComparitor#test(String, String[])} as the test-{@code Predicate}
1002     */
1003    @IntoHTMLTable(background=GreenDither,
1004        title="Find an Attribute by Attribute-Value, by means of a Text-Comparitor")
1005    @LinkJavaSource(handle="HasAndTest", name="hasValue")
1006    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1007    public Map.Entry<String, String> hasValue(
1008            boolean retainQuotes, boolean preserveKeysCase, TextComparitor attributeValueTester,
1009            String... compareStrs
1010        )
1011    {
1012        return HasAndTest.hasValue(
1013            this,
1014            (String s) -> attributeValueTester.test(s, compareStrs),
1015            retainQuotes,
1016            preserveKeysCase
1017        );
1018    }
1019
1020    /**
1021     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_DESC2>
1022     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_DNOTE>
1023     * @param attributeValueTest    <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_AVT>
1024     * @param retainQuotes          <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_RQ>
1025     * @param preserveKeysCase      <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_PKC>
1026     *                              <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
1027     * @return                      <EMBED CLASS='external-html' DATA-FILE-ID=TN_HASVAL_RET2>
1028     * @see                         StrFilter
1029     */
1030    @LinkJavaSource(handle="HasAndTest", name="hasValue")
1031    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1032    public Map.Entry<String, String> hasValue
1033        (Predicate<String> attributeValueTest, boolean retainQuotes, boolean preserveKeysCase)
1034    { return HasAndTest.hasValue(this, attributeValueTest, retainQuotes, preserveKeysCase); }
1035
1036
1037    // ********************************************************************************************
1038    // ********************************************************************************************
1039    // getInstance()
1040    // ********************************************************************************************
1041    // ********************************************************************************************
1042
1043
1044    /**
1045     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_GETINST_DESC>
1046     * @param tok Any valid HTML tag.
1047     * @param openOrClosed <EMBED CLASS='external-html' DATA-FILE-ID=TN_GETINST_OOC>
1048     * @return An instance of this class
1049     * 
1050     * @throws IllegalArgumentException If parameter {@code TC openOrClose} is {@code null} or
1051     * {@code TC.Both}
1052     * 
1053     * @throws HTMLTokException If the parameter {@code String tok} is not a valid HTML-tag
1054     * 
1055     * @throws SingletonException If the token requested is a {@code singleton} (self-closing) tag,
1056     * but the Tag-Criteria {@code 'TC'} parameter is requesting a closing-version of the tag.
1057     * 
1058     * @see HTMLTags#hasTag(String, TC)
1059     * @see HTMLTags#isSingleton(String)
1060     */
1061    public static TagNode getInstance(String tok, TC openOrClosed)
1062    {
1063        if (openOrClosed == null)
1064            throw new NullPointerException("The value of openOrClosed cannot be null.");
1065
1066        if (openOrClosed == TC.Both)
1067            throw new IllegalArgumentException("The value of openOrClosed cannot be TC.Both.");
1068
1069        if (HTMLTags.isSingleton(tok) && (openOrClosed == TC.ClosingTags))
1070
1071            throw new SingletonException(
1072                "The value of openOrClosed is TC.ClosingTags, but unfortunately you have asked " +
1073                "for a [" + tok + "] HTML element, which is a singleton element, and therefore " +
1074                "cannot have a closing-tag instance."
1075            );
1076
1077        TagNode ret = HTMLTags.hasTag(tok, openOrClosed);
1078
1079        if (ret == null)
1080            throw new HTMLTokException
1081                ("The HTML-Tag provided isn't valid!\ntok: " + tok + "\nTC: " + openOrClosed);
1082
1083        return ret;
1084    }
1085
1086
1087    // ********************************************************************************************
1088    // ********************************************************************************************
1089    // Methods for "CSS Classes"
1090    // ********************************************************************************************
1091    // ********************************************************************************************
1092
1093
1094    /**
1095     * Convenience Method.
1096     * <BR />Invokes: {@link #cssClasses()}
1097     * <BR />Catches-Exception
1098     */
1099    public Stream<String> cssClassesNOCSE()
1100    { try { return cssClasses(); } catch (CSSStrException e) { return Stream.empty(); } }
1101
1102    /**
1103     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_CL_DESC>
1104     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_CL_RET>
1105     *                          <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
1106     * @throws CSSStrException  <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_CL_CSSSE>
1107     * @see                     #cssClasses()
1108     * @see                     #AV(String)
1109     * @see                     StringParse#WHITE_SPACE_REGEX
1110     * @see                     CSSStrException#check(Stream)
1111     */
1112    @LinkJavaSource(handle="ClassIDStyle", name="cssClasses")
1113    public Stream<String> cssClasses()
1114    { return ClassIDStyle.cssClasses(this); }
1115
1116    /**
1117     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_DESC>
1118     * @param quote             <EMBED CLASS='external-html' DATA-FILE-ID=TGND_QUOTE_EXPL>
1119     * @param appendOrClobber   <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_AOC>
1120     * @param cssClasses        <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_CCL>
1121     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_CL_RET>
1122     * 
1123     * @throws CSSStrException This exception shall throw if any of the {@code 'cssClasses'} in the
1124     * var-args {@code String...} parameter do not meet the HTML 5 CSS {@code Class} naming rules.
1125     * 
1126     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
1127     * @throws QuotesException          <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_CL_QEX>
1128     * @see                             CSSStrException#check(String[])
1129     * @see                             CSSStrException#VALID_CSS_CLASS_OR_NAME_TOKEN
1130     * @see                             #appendToAV(String, String, boolean, SD)
1131     * @see                             #setAV(String, String, SD)
1132     */
1133    @LinkJavaSource(handle="ClassIDStyle", name="setCSSClasses")
1134    public TagNode setCSSClasses(SD quote, boolean appendOrClobber, String... cssClasses)
1135    { return ClassIDStyle.setCSSClasses(this, quote, appendOrClobber, cssClasses); }
1136
1137    /**
1138     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_APD_CSS_CL_DESC>
1139     * @param cssClass This is the CSS-{@code Class} name that is being inserted into
1140     * {@code 'this'} instance of {@code TagNode}
1141     * 
1142     * @param quote                     <EMBED CLASS=external-html DATA-FILE-ID=TGND_QUOTE_EXPL>
1143     * @return                          A new {@code TagNode} with updated CSS {@code Class} Name(s)
1144     * @throws CSSStrException          <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_CSS_CL_CSSSE>
1145     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
1146     * @throws QuotesException          <EMBED CLASS=external-html DATA-FILE-ID=TN_APD_CSS_CL_QEX>
1147     * @see                             CSSStrException#check(String[])
1148     * @see                             #setAV(String, String, SD)
1149     * @see                             #appendToAV(String, String, boolean, SD)
1150     */
1151    @LinkJavaSource(handle="ClassIDStyle", name="appendCSSClass")
1152    public TagNode appendCSSClass(String cssClass, SD quote)
1153    { return ClassIDStyle.appendCSSClass(this, cssClass, quote); }
1154
1155
1156    // ********************************************************************************************
1157    // ********************************************************************************************
1158    // Methods for "CSS Style"
1159    // ********************************************************************************************
1160    // ********************************************************************************************
1161
1162
1163    /**
1164     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_STYLE_DESC>
1165     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_STYLE_DESCEX>
1166     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_CSS_STYLE_RET>
1167     */
1168    @LinkJavaSource(handle="ClassIDStyle", name="cssStyle")
1169    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="CSS_INLINE_STYLE_REGEX")
1170    public Properties cssStyle()
1171    { return ClassIDStyle.cssStyle(this); }
1172
1173    /**
1174     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_CSS_STY_DESC>
1175     * @param p                         <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_STY_P>
1176     * @param quote                     <EMBED CLASS=external-html DATA-FILE-ID=TGND_QUOTE_EXPL>
1177     * @param appendOrClobber           <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_STY_AOC>
1178     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_SET_CSS_STY_RET>
1179     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=CTNEX>
1180     * @throws CSSStrException          If there is an invalid CSS Style Property Name.
1181     * 
1182     * @throws QuotesException If the style-element's quotation marks are incompatible with any
1183     * and all quotation marks employed by the style-element definitions.
1184     * 
1185     * @see CSSStrException#VALID_CSS_CLASS_OR_NAME_TOKEN
1186     * @see #appendToAV(String, String, boolean, SD)
1187     * @see #setAV(String, String, SD)
1188     */
1189    @LinkJavaSource(handle="ClassIDStyle", name="setCSSStyle")
1190    public TagNode setCSSStyle(Properties p, SD quote, boolean appendOrClobber)
1191    { return ClassIDStyle.setCSSStyle(this, p, quote, appendOrClobber); }
1192
1193
1194    // ********************************************************************************************
1195    // ********************************************************************************************
1196    // Methods for "CSS ID"
1197    // ********************************************************************************************
1198    // ********************************************************************************************
1199
1200
1201    /**
1202     * Convenience Method.
1203     * <BR />Invokes: {@link #AV(String)}
1204     * <BR />Passes: {@code String "id"}, the CSS-ID attribute-<B STYLE='color: red;'>name</B>
1205     */
1206    public String getID()
1207    {
1208        String id = AV("ID");
1209        return (id == null) ? null : id.trim();
1210    }
1211
1212    /**
1213     * This merely sets the current CSS {@code 'ID'} Attribute <B STYLE='color: red;'>Value</B>.
1214     *
1215     * @param id This is the new CSS {@code 'ID'} attribute-<B STYLE='color: red;'>value</B> that
1216     * the user would like applied to {@code 'this'} instance of {@code TagNode}.
1217     * 
1218     * @param quote <EMBED CLASS='external-html' DATA-FILE-ID=TGND_QUOTE_EXPL>
1219     *
1220     * @return Returns a new instance of {@code TagNode} that has an updated {@code 'ID'} 
1221     * attribute-<B STYLE='color: red;'>value</B>.
1222     * 
1223     * @throws IllegalArgumentException This exception shall throw if an invalid
1224     * {@code String}-token has been passed to parameter {@code 'id'}.
1225     *
1226     * <BR /><BR /><B>BYPASS NOTE:</B> If the user would like to bypass this exception-check, for
1227     * instance because he / she is using a CSS Pre-Processor, then applying the general-purpose
1228     * method {@code TagNode.setAV("id", "some-new-id")} ought to suffice.  This other method will
1229     * not apply validity checking, beyond scanning for the usual "quotes-within-quotes" problems,
1230     * which is always disallowed.
1231     *
1232     * @throws ClosingTagNodeException <EMBED CLASS='external-html' DATA-FILE-ID=CTNEX>
1233     * 
1234     * @see CSSStrException#VALID_CSS_CLASS_OR_NAME_TOKEN
1235     * @see #setAV(String, String, SD)
1236     */
1237    public TagNode setID(String id, SD quote)
1238    {
1239        if (! CSSStrException.VALID_CSS_CLASS_OR_NAME_TOKEN_PRED.test(id))
1240
1241            throw new IllegalArgumentException(
1242                "The id parameter provide: [" + id + "], does not conform to the standard CSS " +
1243                "Names.\nEither try using the generic TagNode.setAV(\"id\", yourNewId, quote); " +
1244                "method to bypass this check, or change the value passed to the 'id' parameter " +
1245                "here."
1246            );
1247
1248        return setAV("id", id.trim(), quote);
1249    }
1250
1251
1252    // ********************************************************************************************
1253    // ********************************************************************************************
1254    // Attributes that begin with "data-..."
1255    // ********************************************************************************************
1256    // ********************************************************************************************
1257
1258
1259    /**
1260     * Convenience Method.
1261     * <BR />See Documentation: {@link #AV(String)}
1262     * <BR />Passes: {@code "data-"} prepended to parameter {@code 'dataName'} for the
1263     * attribute-<B STYLE='color:red'>name</B>
1264     */
1265    @LinkJavaSource(handle="GetSetAttr", name="AV")
1266    public String dataAV(String dataName)
1267    { return GetSetAttr.AV(this, "data-" + dataName, false); }
1268
1269    /**
1270     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_DATTR_DESC>
1271     * @return                          <EMBED CLASS=external-html DATA-FILE-ID=TN_REM_DATTR_RET>
1272     * @throws ClosingTagNodeException  <EMBED CLASS=external-html DATA-FILE-ID=TN_REM_DATTR_CTNEX>
1273     */
1274    @LinkJavaSource(handle="RemoveAttributes", name="removeAttributes")
1275    public TagNode removeDataAttributes() 
1276    {
1277        return RemoveAttributes.removeAttributes
1278            (this, (String attr) -> ! StrCmpr.startsWithIgnoreCase(attr, "data-") );
1279    }
1280
1281    /**
1282     * Convenience Method.
1283     * <BR />See Documentation: {@link #getDataAV(boolean)}
1284     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case
1285     */
1286    @LinkJavaSource(handle="DataAttributes", name="getDataAV")
1287    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="DATA_ATTRIBUTE_REGEX")
1288    public Properties getDataAV()
1289    { return DataAttributes.getDataAV(this, false); }
1290
1291    /**
1292     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AV_DESC>
1293     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AV_PAR>
1294     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
1295     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AV_RET>
1296     */
1297    @LinkJavaSource(handle="DataAttributes", name="getDataAV")
1298    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="DATA_ATTRIBUTE_REGEX")
1299    public Properties getDataAV(boolean preserveKeysCase) 
1300    { return DataAttributes.getDataAV(this, preserveKeysCase); }
1301
1302    /**
1303     * Convenience Method.
1304     * <BR />See Documentation: {@link #getDataAN(boolean)}
1305     * <BR />Attribute-<B STYLE='color: red;'>names</B> will be in lower-case
1306     */
1307    public Stream<String> getDataAN()
1308    { return DataAttributes.getDataAN(this, false); }
1309
1310    /**
1311     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AN_DESC>
1312     * @param preserveKeysCase  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AN_PAR>
1313     *                          <EMBED CLASS='external-html' DATA-FILE-ID=TAGNODE_PRESERVE_C>
1314     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_GET_DATA_AN_RET>
1315     *                          <EMBED CLASS='external-html' DATA-FILE-ID=STRMCNVT>
1316     */
1317    @LinkJavaSource(handle="DataAttributes", name="getDataAN")
1318    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="DATA_ATTRIBUTE_REGEX")
1319    public Stream<String> getDataAN(boolean preserveKeysCase) 
1320    { return DataAttributes.getDataAN(this, preserveKeysCase); }
1321
1322
1323    // ********************************************************************************************
1324    // ********************************************************************************************
1325    // Java Methods
1326    // ********************************************************************************************
1327    // ********************************************************************************************
1328
1329
1330    /**
1331     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TOSTR_AV_DESC>
1332     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_TOSTR_AV_RET>
1333     * @see HTMLNode#toString()
1334     */
1335    @LinkJavaSource(handle="GeneralPurpose", name="toStringAV")
1336    public String toStringAV()
1337    { return GeneralPurpose.toStringAV(this); }
1338
1339    /**
1340     * Java's {@code interface Cloneable} requirements.  This instantiates a new {@code TagNode}
1341     * with identical <SPAN STYLE='color: red;'>{@code String str}</SPAN> fields, and also
1342     * identical <SPAN STYLE='color: red;'>{@code boolean isClosing}</SPAN> and
1343     * <SPAN STYLE='color: red;'>{@code String tok}</SPAN> fields.
1344     * 
1345     * @return A new {@code TagNode} whose internal fields are identical to this one.
1346     */
1347    public TagNode clone() { return new TagNode(str); }
1348
1349    /**
1350     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_COMPARETO_DESC>
1351     * @param n Any other {@code TagNode} to be compared to {@code 'this' TagNode}
1352     * @return An integer that fulfils Java's {@code Comparable} interface-method requirements.
1353     */
1354    public int compareTo(TagNode n)
1355    {
1356        // Utilize the standard "String.compare(String)" method with the '.tok' string field.
1357        // All 'tok' fields are stored as lower-case strings.
1358        int compare1 = this.tok.compareTo(n.tok);
1359
1360        // Comparison #1 will be non-zero if the two TagNode's being compared had different
1361        // .tok fields
1362        if (compare1 != 0) return compare1;
1363
1364        // If the '.tok' fields were the same, use the 'isClosing' field for comparison instead.
1365        // This comparison will only be used if they are different.
1366        if (this.isClosing != n.isClosing) return (this.isClosing == false) ? -1 : 1;
1367    
1368        // Finally try using the entire element '.str' String field, instead.  
1369        return this.str.length() - n.str.length();
1370    }
1371
1372
1373    // ********************************************************************************************
1374    // ********************************************************************************************
1375    // toUpperCase 
1376    // ********************************************************************************************
1377    // ********************************************************************************************
1378
1379
1380    /**
1381     * <EMBED CLASS=defs DATA-CASE=Upper DATA-CAPITAL="">
1382     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_DESC>
1383     * @param justTag_Or_TagAndAttributeNames 
1384     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_PARAM>
1385     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_RET>
1386     */
1387    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=3)
1388    public TagNode toUpperCase(boolean justTag_Or_TagAndAttributeNames)
1389    {
1390        return CaseChange.toCaseInternal
1391            (this, justTag_Or_TagAndAttributeNames, String::toUpperCase);
1392    }
1393
1394    /**
1395     * Convenience Method.
1396     * <BR />See Documentation: {@link #toUpperCase(boolean, Predicate)}
1397     * <BR />Passes: {@code StrCmpr.equalsXOR_CI(attrName, attributeNames)}
1398     * @see StrCmpr#equalsXOR_CI(String, String...)
1399     */
1400    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1401    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1402    public TagNode toUpperCase(boolean tag, String... attributeNames)
1403    {
1404        return CaseChange.toCaseInternal(
1405            this,
1406            tag,
1407            (String attrName) -> StrCmpr.equalsXOR_CI(attrName, attributeNames),
1408            String::toUpperCase
1409        );
1410    }
1411
1412    /**
1413     * <EMBED CLASS=defs DATA-CASE=Upper DATA-CAPITAL="">
1414     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_DESC>
1415     * @param tag           Indicates whether or not the Tag-Name should be capitalized
1416     * @param attrNameTest  <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_PARAM>
1417     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_RET>
1418     */
1419    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1420    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1421    public TagNode toUpperCase(boolean tag, Predicate<String> attrNameTest)
1422    { return CaseChange.toCaseInternal(this, tag, attrNameTest, String::toUpperCase); }
1423
1424
1425    // ********************************************************************************************
1426    // ********************************************************************************************
1427    // toLowerCase 
1428    // ********************************************************************************************
1429    // ********************************************************************************************
1430
1431
1432    /**
1433     * <EMBED CLASS=defs DATA-CASE=Lower DATA-CAPITAL="de-">
1434     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_DESC>
1435     * @param justTag_Or_TagAndAttributeNames 
1436     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_PARAM>
1437     * @return <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_1_RET>
1438     */
1439    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=3)
1440    public TagNode toLowerCase(boolean justTag_Or_TagAndAttributeNames)
1441    {
1442        return CaseChange.toCaseInternal
1443            (this, justTag_Or_TagAndAttributeNames, String::toLowerCase);
1444    }
1445
1446    /**
1447     * Convenience Method.
1448     * <BR />See Documentation: {@link #toLowerCase(boolean, Predicate)}
1449     * <BR />Passes: {@code StrCmpr.equalsXOR_CI(attrName, attributeNames)}
1450     * @see StrCmpr#equalsXOR_CI(String, String...)
1451     */
1452    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1453    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1454    public TagNode toLowerCase(boolean tag, String... attributeNames)
1455    {
1456        return CaseChange.toCaseInternal(
1457            this,
1458            tag,
1459            (String attrName) -> StrCmpr.equalsXOR_CI(attrName, attributeNames),
1460            String::toLowerCase
1461        );
1462    }
1463
1464    /**
1465     * <EMBED CLASS=defs DATA-CASE=Lower DATA-CAPITAL="de-">
1466     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_DESC>
1467     * @param tag           Indicates whether or not the Tag-Name should be decapitalized
1468     * @param attrNameTest  <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_PARAM>
1469     * @return              <EMBED CLASS='external-html' DATA-FILE-ID=TN_TO_UPLOW_2_RET>
1470     */
1471    @LinkJavaSource(handle="CaseChange", name="toCaseInternal", paramCount=4)
1472    @LinkJavaSource(handle="AttrRegEx", entity=FIELD, name="KEY_VALUE_REGEX")
1473    public TagNode toLowerCase(boolean tag, Predicate<String> attrNameTest)
1474    { return CaseChange.toCaseInternal(this, tag, attrNameTest, String::toLowerCase); }
1475
1476
1477    // ********************************************************************************************
1478    // ********************************************************************************************
1479    // Attribute-Value Quotation-Marks - REMOVE
1480    // ********************************************************************************************
1481    // ********************************************************************************************
1482
1483
1484    /**
1485     * Convenience Method.
1486     * <BR />See Documentation: {@link #removeAVQuotes(Predicate)}
1487     * <BR />Removes Quotation-Marks from <B STYLE='color: red;'>Value</B> whose Inner-Tag 
1488     * <B STYLE='color: red;'>Name</B> matches {@code 'attributeName'}
1489     */
1490    @LinkJavaSource(handle="QuotationMarks", name="removeAVQuotes")
1491    public TagNode removeAVQuotes(String attributeName)
1492    {
1493        return QuotationMarks.removeAVQuotes
1494            (this, (String attr) -> attr.equalsIgnoreCase(attributeName));
1495    }
1496
1497    /**
1498     * Convenience Method.
1499     * <BR />See Documentation: {@link #removeAVQuotes(Predicate)}
1500     * <BR />Removes Quotation-Marks from all Inner-Tag <B STYLE='color: red;'>Values</B>
1501     */
1502    @LinkJavaSource(handle="QuotationMarks", name="removeAVQuotes")
1503    public TagNode removeAllAVQuotes()
1504    { return QuotationMarks.removeAVQuotes(this, (String attr) -> true); }
1505
1506    /**
1507     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_QUOTES_DESC>
1508     * @param attrNameTest      <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_QUOTES_PARAM>
1509     * @return                  <EMBED CLASS='external-html' DATA-FILE-ID=TN_REM_QUOTES_RET>
1510     * @throws QuotesException  If the resulting <CODE>TagNode</CODE> contains Quotation-Errors
1511     */
1512    @LinkJavaSource(handle="QuotationMarks", name="removeAVQuotes")
1513    public TagNode removeAVQuotes(Predicate<String> attrNameTest)
1514    { return QuotationMarks.removeAVQuotes(this, attrNameTest); }
1515
1516
1517    // ********************************************************************************************
1518    // ********************************************************************************************
1519    // Attribute-Value Quotation-Marks - SET
1520    // ********************************************************************************************
1521    // ********************************************************************************************
1522
1523
1524    /**
1525     * Convenience Method.
1526     * <BR />See Documentation: {@link #setAVQuotes(Predicate, SD)}}
1527     * <BR />Set Quotation-Marks for the Attribute whose <B STYLE='color: red;'>Name</B> matches
1528     * {@code 'attributeName'}
1529     */
1530    @LinkJavaSource(handle="QuotationMarks", name="setAVQuotes")
1531    public TagNode setAVQuotes(String attributeName, SD quote)
1532    {
1533        return QuotationMarks.setAVQuotes
1534            (this, (String attr) -> attr.equalsIgnoreCase(attributeName), quote);
1535    }
1536
1537    /**
1538     * Convenience Method.
1539     * <BR />See Documentation: {@link #setAVQuotes(Predicate, SD)}}
1540     * <BR />Set the Quotation-Marks which are used with all Attribute's contained by
1541     * {@code 'this'} Tag.
1542     */
1543    @LinkJavaSource(handle="QuotationMarks", name="setAVQuotes")
1544    public TagNode setAllAVQuotes(SD quote)
1545    { return QuotationMarks.setAVQuotes(this, (String attr) -> true, quote); }
1546
1547    /**
1548     * <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_QUOTES_DESC>
1549     * @param attrNameTest          <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_QUOTES_PARAM>
1550     * @param quote                 The new Quotation-Mark to apply
1551     * @return                      <EMBED CLASS='external-html' DATA-FILE-ID=TN_SET_QUOTES_RET>
1552     * @throws QuotesException      If the resulting <CODE>TagNode</CODE> contains Quotation-Errors
1553     * @throws NullPointerException If either parameter is passed null.
1554     */
1555    @LinkJavaSource(handle="QuotationMarks", name="setAVQuotes")
1556    public TagNode setAVQuotes(Predicate<String> attrNameTest, SD quote)
1557    { return QuotationMarks.setAVQuotes(this, attrNameTest, quote); }
1558
1559
1560    // ********************************************************************************************
1561    // ********************************************************************************************
1562    // Attribute-Value Quotation-Marks - GET
1563    // ********************************************************************************************
1564    // ********************************************************************************************
1565
1566}