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