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