001package Torello.HTML; 002 003import java.util.*; 004 005import java.util.function.ObjIntConsumer; 006 007// This is only needed for the Java Doc {@link ...} taglets. 008import Torello.HTML.NodeSearch.TagNodePeekL1Inclusive; 009 010import Torello.Java.LV; 011import Torello.Java.StringParse; 012import Torello.Java.Additional.Ret2; 013 014/** 015 * Methods for quickly & efficiently replacing the nodes on a Web-Page. 016 * 017 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=RN> 018 */ 019@Torello.JavaDoc.StaticFunctional 020public class ReplaceNodes 021{ 022 private ReplaceNodes() { } 023 024 /** 025 * <EMBED CLASS='external-html' DATA-FILE-ID=RN_R01_DESC> 026 * 027 * @param page <EMBED CLASS='external-html' DATA-FILE-ID=RN_R01_PAGE> 028 * @param updatedReplaceables <EMBED CLASS='external-html' DATA-FILE-ID=RN_R01_UPDATEREPL> 029 * @param updateReplaceablesAfterBuild <EMBED CLASS='external-html' DATA-FILE-ID=RN_R01_URAB> 030 * 031 * @return <EMBED CLASS='external-html' DATA-FILE-ID=RN_R01_RET> 032 * 033 * @throws ReplaceableOutOfBoundsException If any of the {@link Replaceable} instances returned 034 * by the {@code 'updatedReplaceables'} iterator-parameter have <B><I>original location</I></B> 035 * {@code Vector}-indices that are not within the bounds of the HTML page-{@code Vector} 036 * (parameter {@code 'page'}). 037 * 038 * @throws ReplaceablesOverlappingException If any of the {@link Replaceable} instances returned 039 * by the {@code 'updatedReplaceables'} iterator-parameter have <B><I>original location</I></B> 040 * {@code Vector}-indices that overlap. 041 * 042 * @throws ReplaceablesUnsortedException If any of the {@link Replaceable} instances returned 043 * by the {@code 'updatedReplaceables'} iterator-parameter have <B><I>original-starting 044 * locations</I></B> that are non-consecutive (out of order!) 045 * 046 * <!-- In the JD Comments 'external-html/' directory, this is filed under 'r0/' --> 047 */ 048 public static 049 Ret2<Vector<HTMLNode>, Vector<Replaceable>> 050 r( 051 Vector<HTMLNode> page, Iterable<? extends Replaceable> updatedReplaceables, 052 boolean updateReplaceablesAfterBuild 053 ) 054 { 055 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 056 // First check for the case that 'updatedReplaceables' is empty 057 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 058 059 final int SIZE = page.size(); 060 061 // This entire loop is merely done for nothing more than error/exception checking. 062 // It is mandatory that the SubSections which are passed are all 'in-order', that 063 // none of them overlap, and that they all fit inside the 'page' vector parameter. 064 065 Iterator<? extends Replaceable> iter = updatedReplaceables.iterator(); 066 067 // If there are no Replaceables in the Iterable, return the original page. 068 if (! iter.hasNext()) 069 { 070 if (! updateReplaceablesAfterBuild) return new Ret2<>(page, null); 071 072 Vector<Replaceable> ret = new Vector<>(); 073 for (Replaceable r : updatedReplaceables) ret.add(r); 074 return new Ret2<>(page, ret); 075 } 076 077 078 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 079 // Initialize the Loop variables 080 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 081 082 Replaceable replaceable = iter.next(); 083 Replaceable previousReplaceable = null; 084 085 // These are used, specifically, for the error-checking part of the loop 086 int start1 = replaceable.originalLocationStart(); 087 int end1 = replaceable.originalLocationEnd() - 1; // Value is Exclusive 088 int start2 = -1; 089 int end2 = -1; 090 091 // This is used for the exception messages only. It is incremented on the last line of the 092 // loop body. 093 094 int i=0; 095 096 // These are used, specifically, for the part that computes the size the final vector 097 int size = 0; // Total (Future) Size of the Return / Output Vector 098 int last = 0; // Temp Variable, it is easier to have a separate one for this 099 100 while (iter.hasNext()) 101 { 102 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 103 // Compute what the size of the returned HTML-Vector is going to be. 104 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 105 // 106 // This is done by looking at the locations of all the replacements, and the number of 107 // nodes between each replacement. 108 // 109 // For the part of this loop that is computing the size of the final vector, there 110 // start2 and end2 pointers should just be ignored. The start1, end1 pointer pair 111 // are sufficient, as during each iteration, start2 and end2 are assigned to start1 and 112 // end1 in the very next step anyways. 113 114 size += (start1 - last); // Size of the previous "in-between chunk" 115 size += replaceable.currentSize(); // Size of the next SubSection 116 last = end1; // advance the 'last' pointer 117 118 119 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 120 // Advance the Validity-Checking Pointer Pairs 121 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 122 123 // Advance pointer-pair #1 (but DONT'T do this one the VERY FIRST ITERATION) 124 if (i > 0) 125 { 126 start1 = start2; 127 end1 = end2; 128 } 129 130 // Advance Pointer Pair #2 131 previousReplaceable = replaceable; 132 replaceable = iter.next(); 133 start2 = replaceable.originalLocationStart(); 134 end2 = replaceable.originalLocationEnd() - 1; 135 136 137 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 138 // NOW... THE VALIDITY-CHECKING IF-STATEMENTS 139 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 140 141 /* 142 System.out.println( 143 "previousReplaceable: " + previousReplaceable + 144 ", replaceable: " + replaceable + '\n' + 145 "start1: " + start1 + ", end1: " + end1 + ", start2: " + start2 + ", end2: " + end2 146 ); 147 */ 148 149 if (start2 < start1) 150 151 throw new ReplaceablesUnsortedException( 152 "'updatedReplaceables' contains at least one Replaceable Element-Pair " + 153 "which is not sorted from first to last:\n" + 154 "The " + (i+1) + StringParse.ordinalIndicator(i+1) + " Replaceable returned " + 155 "by 'updatedReplaceables' starts at page-index " + start1 + '\n' + 156 "The " + (i+2) + StringParse.ordinalIndicator(i+2) + " Replaceable returned " + 157 "by 'updatedReplaceables' starts at page-index " + start2, 158 previousReplaceable, replaceable 159 ); 160 161 if ( (start2 == start1) // New section starts at same place as the previous section 162 || (start2 <= end1) // New section begins before the previous section ended 163 164 // !!! Whenever a user has created a zero-length-replaceable (zero original length) 165 // then the "end" of that replaceable will be "start-1". Sounds a little silly, 166 // right? Well inserting a zero-length replaceable happens a lot in JavaDoc 167 // Upgrader. The two cases of the if-statement are both important. Remember, the 168 // iterator must be returning sorted elements, or else the previous if statement 169 // would have already failed. 170 ) 171 172 throw new ReplaceablesOverlappingException( 173 "'updatedReplaceables' contains at least one Replaceable Element-Pair " + 174 "that overlap each-other:\n" + 175 "The " + (i+1) + StringParse.ordinalIndicator(i+1) + " Replaceable returned " + 176 "by 'updatedReplaceables' has original-location " + 177 "[" + start1 + ", " + end1 + "]\n" + 178 "The " + (i+2) + StringParse.ordinalIndicator(i+2) + " Replaceable returned " + 179 "by 'updatedReplaceables' has original-location " + 180 "[" + start2 + ", " + end2 + ']', 181 previousReplaceable, replaceable 182 ); 183 184 if (end1 > SIZE) 185 186 throw new ReplaceableOutOfBoundsException( 187 "There was a Replaceable Element whose original-location was not within the " + 188 "bounds of page:\n" + 189 "The " + (i+1) + StringParse.ordinalIndicator(i+1) + " Replaceable returned " + 190 "by 'updatedReplaceables' has original-location " + 191 "[" + start1 + ", " + end1 + "]\n" + 192 "While page.size() is: " + SIZE, 193 replaceable 194 ); 195 196 i++; 197 } 198 199 200 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 201 // POST-LOOP FINISHING TOUCHES 202 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 203 204 // "Ending Read" Check. The very last location is not checked, because the loop breaks 205 // before it gets to check pointer-pair-2 (on the last iteration) 206 // 207 // NOTE: The patholigical-cae where there is **ONLY ONE** SubSection in the updatedReplaceables 208 // input Collection. If pointer-pair-2 is -1, there is no need to check it... :) 209 // If (pointer-pair-2 == -1), the loop body was never entered 210 211 if (end2 != -1) if (end2 >= SIZE) 212 213 throw new ReplaceableOutOfBoundsException( 214 "There was a Replaceable Element whose original-location was not within the " + 215 "bounds of page:\n" + 216 "The " + (i+2) + StringParse.ordinalIndicator(i+2) + " Replaceable returned by " + 217 "'updatedReplaceables' has original-location [" + start2 + ", " + end2 + "]\n" + 218 "While page.size() is: " + SIZE, 219 replaceable 220 ); 221 222 // the very-last replaceable was not added to the size. 223 size += (start1 - last); // Size of the previous "in-between chunk" 224 size += replaceable.currentSize(); // Size of the next SubSection 225 last = end1; 226 227 228 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 229 // Build the Return Vector - NOTE - We just computed its final size! 230 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 231 // 232 // ALSO: If the user has requested the DP's be udated, also build the "newSubSections" Vec 233 // 234 // The purpose of the above computation was for instantiating a properly-sized vector 235 // at construction time. This will save quite a bit of time that would be wasted on 236 // vector resizing. 237 238 Vector<HTMLNode> ret = new Vector<>(size); 239 240 // By user request, only! This really isn't *THAT* important. All that the 241 // 'newSubSections' Vector will have shall be the exact same-subsections that are passed 242 // as a parameter to this method through the 'updatedReplaceables' parameter - *EXCEPT* that 243 // their SubSection.location fields will be updated to hold the *ACTUAL* / *NEW* locations 244 245 Vector<Replaceable> newReplaceables = 246 updateReplaceablesAfterBuild ? new Vector<>() : null; 247 248 249 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 250 // MAIN-LOOP: Iterate each of the Replaceables that was passed as input to this method. 251 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 252 // 253 // Add their contents to Output-Vector, and make sure to add all "in-between" nodes too! 254 255 // The index-pointer to the **ORIGINAL-VECTOR** (a.k.a. the input vector) 256 int pagePos=0; 257 258 // This loop does the replacement. It is quick and easy if you understand what replacing 259 // a list of subsections involves. 260 261 for (Replaceable r : updatedReplaceables) 262 { 263 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 264 // Add all of the MOST-RECENT "In-Between Nodes" (These are all nodes before next SS) 265 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 266 // 267 // AFTERWARDS: Add all nodes in the next Sub-Section 268 269 // Retrieve all of the 'in-between' nodes 270 while (pagePos < r.originalLocationStart()) ret.add(page.elementAt(pagePos++)); 271 272 // Add this Replaceable to the returned output list! 273 r.addAllInto(ret); 274 275 // Skip over the old nodes. 276 pagePos = r.originalLocationEnd(); // don't add one, value is exclusive 277 278 279 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 280 // User may request that the Sub-Section 'Locations' be updated, rather than discarded 281 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 282 283 // This is done for convenience so that the user knows where the sections are all 284 // located in the new build. 285 // 286 // NOTE: All this is doing is changing the 'location' field of the old subsection 287 // which has changed to contain the new 'location' 288 289 if (updateReplaceablesAfterBuild) 290 { 291 int ePos = ret.size(); 292 int sPos = ePos - r.currentSize() + 1; 293 294 newReplaceables.add(r.moveAndUpdate(sPos)); 295 } 296 } 297 298 299 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 300 // IMPORTANT: Add the last / final Elements that occur *AFTER* the *LAST* Sub-Section 301 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 302 // 303 // This part should also be called "The Tail" of the Page. (Put the Page-Tail back) 304 305 while (pagePos < page.size()) ret.add(page.elementAt(pagePos++)); 306 307 308 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 309 // AGAIN: User may request that Sub-Section 'Locations' be updated, rather than discarded 310 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 311 312 return updateReplaceablesAfterBuild 313 ? new Ret2<>(ret, newReplaceables) 314 : new Ret2<>(ret, null); 315 } 316 317 /** 318 * Allows a user to quickly alter each <B STYLE='color: red;'>row</B> in an HTML 319 * <B STYLE='color: red;'>table</B>, iteratively, in a manner that offers a tremendous 320 * efficiency-improvement over the HTML {@code Iterator's} in the Node Seach Package. 321 * 322 * <EMBED CLASS='external-html' DATA-FILE-ID=RN_R02_R03_FAST DATA-ITEM='table-row'> 323 * 324 * <BR /><BR /><UL CLASS=JDUL> 325 * <LI> {@code 'page'} is not null. 326 * </LI> 327 * <LI> {@code 'table'} is non-null, and has {@link DotPair#start} and {@link DotPair#end} 328 * indices with integer-values smaller than the size of {@code 'page'} 329 * </LI> 330 * <LI> {@code 'table'} has indices which point to {@code TagNode's} in {@code 'page'} that 331 * are opening {@code <TABLE>} and closing {@code </TABLE>} tags. 332 * </LI> 333 * </UL> 334 * 335 * @param page Any HTML Page or sub-page that has a table. 336 * 337 * @param table A pointer to the table start and end index bounds. 338 * 339 * @param tableRowModifier <EMBED CLASS='external-html' DATA-TYPE=Table DATA-ENTITY=Row 340 * DATA-FILE-ID=RN_R02_R03_ROW_MOD> 341 * 342 * @return <EMBED CLASS='external-html' DATA-FILE-ID=RN_R01_RET> 343 * <!-- Bororwed from R01 RET --> 344 * 345 * @see TagNodePeekL1Inclusive 346 * @see ReplaceNodes#r(Vector, Iterable, boolean) 347 * @see DotPair#exceptionCheck(Vector, String[]) 348 * @see SubSection 349 * <!-- In the JD Comments 'external-html/' directory, this is filed under 'r02r03/' --> 350 */ 351 public static Vector<HTMLNode> tableTR( 352 Vector<HTMLNode> page, DotPair table, 353 ObjIntConsumer<Vector<HTMLNode>> tableRowModifier 354 ) 355 { 356 // Ensure that page.elementAt(table.start) contains a "<TABLE>" element, and that 357 // page.elementAt(table.end) contains a "</TABLE>" element. 358 // 359 // If some other type of TagNode, or a non-TagNode is present, this method throws one of 360 // several exceptions to inform the user about the error. 361 362 table.exceptionCheck(page, "table"); 363 364 // Retrieve all "<TR> ... </TR>" elements. The "L1Inclusive" stipulates that any potential 365 // inner-table rows (if there are any inner-tables), should be ignored. 366 367 Vector<SubSection> rows = TagNodePeekL1Inclusive.all(page, table.start, table.end, "tr"); 368 369 // All this does is invoke the user-provided function-pointer on each table-row 370 for (int i=0; i < rows.size(); i++) tableRowModifier.accept(rows.elementAt(i).html, i); 371 372 // Update all Table-Rows. Remove the old Rows, and insert the new ones. This replace 373 // operation does this much more efficiently than most replacement-code. 374 375 return ReplaceNodes.r(page, rows, false).a; 376 } 377 378 /** 379 * Allows a user to quickly alter each <B STYLE='color: red;'>item</B> in an HTML 380 * <B STYLE='color: red;'>list</B> ({@code <OL>, <UL>} or {@code <MENU>}), iteratively, in a 381 * manner that offers a tremendous efficiency-improvement over the HTML {@code Iterator's} in 382 * the Node Seach Package. 383 * 384 * <EMBED CLASS='external-html' DATA-FILE-ID=RN_R02_R03_FAST DATA-ITEM='list-item'> 385 * 386 * <BR /><BR /><UL CLASS=JDUL> 387 * <LI> {@code 'page'} is not null. 388 * </LI> 389 * <LI> {@code 'list'} is non-null, and has {@link DotPair#start} and {@link DotPair#end} 390 * indices with integer-values smaller than the size of {@code 'page'} 391 * </LI> 392 * <LI> {@code 'list'} has indices which point to {@code TagNode's} in {@code 'page'} that 393 * are opening-and-closing {@code <UL>, <OL>} or {@code <MENU>} tags. 394 * </LI> 395 * </UL> 396 * 397 * @param page Any HTML Page or sub-page that has an {@code <OL>, <UL>} or {@code <MENU>}. 398 * 399 * @param list A pointer to the list start and end index bounds. 400 * 401 * @param listItemModifier <EMBED CLASS='external-html' DATA-TYPE=List DATA-ENTITY=Item 402 * DATA-FILE-ID=RN_R02_R03_ROW_MOD> 403 * 404 * @return <EMBED CLASS='external-html' DATA-FILE-ID=RN_R01_RET> <!-- Bororwed from R01 RET --> 405 * 406 * @see TagNodePeekL1Inclusive 407 * @see ReplaceNodes#r(Vector, Iterable, boolean) 408 * @see DotPair#exceptionCheck(Vector, String[]) 409 * @see SubSection 410 * <!-- In the JD Comments 'external-html/' directory, this is filed under 'r02r03/' --> 411 */ 412 public static Vector<HTMLNode> listLI( 413 Vector<HTMLNode> page, DotPair list, 414 ObjIntConsumer<Vector<HTMLNode>> listItemModifier 415 ) 416 { 417 // Ensure that page.elementAt(list.start) contains an "<OL>", "<UL>", or "<MENU>" element, 418 // and that page.elementAt(list.end) contains an "</OL>", "</UL>" or "</MENU>" element. 419 // 420 // If some other type of TagNode, or a non-TagNode is present, this method throws one of 421 // several exceptions to inform the user about the error. 422 423 list.exceptionCheck(page, "ol", "ul", "menu"); 424 425 // Retrieve all "<LI> ... </LI>" elements. The "L1Inclusive" stipulates that any potential 426 // inner-list items (if there are any inner-lists), should be ignored. 427 428 Vector<SubSection> items = TagNodePeekL1Inclusive.all(page, list.start, list.end, "li"); 429 430 // All this does is invoke the user-provided function-pointer on each list-item 431 for (int i=0; i < items.size(); i++) listItemModifier.accept(items.elementAt(i).html, i); 432 433 // Update all items. Remove the old-Items, and insert the new ones. This replace 434 // operation does this much more efficiently than most replacement-code. 435 436 return ReplaceNodes.r(page, items, false).a; 437 } 438 439 /** 440 * Iterates the integer-pointer values in {@code int[] posArr}, and replaces the nodes inside 441 * {@code 'html'} that have been identified by the {@code 'posArr'} list with a new node 442 * supplied by the {@code interface ReplaceFunction}. It should be obvious, that lambda 443 * expressions may be used here. 444 * 445 * <BR /><BR /><B CLASS=JDDescLabel>Returning 'null':</B> 446 * 447 * <BR />If the Lambda-Target / Functional-Interface {@link ReplaceFunction} (whose method 448 * is named {@link ReplaceFunction#getReplacement(HTMLNode, int, int)} 449 * <B><I STYLE='color: red;'>actually returns <CODE>'null'</CODE></I></B>, 450 * then null will indeed by inserted into the corresponding {@code Vector} position. 451 * 452 * @param html Any HTML page or section that has been loaded already. 453 * 454 * @param posArr This is usually generated by one of the node-search {@code 'Find.all(...)'} 455 * methods. Each and every one of the {@code 'Find.all(...)'} methods in the search package 456 * will return an array of integers. These integers represent positions/locations in the 457 * passed HTML page {@code Vector}. 458 * 459 * @param rf This is just a class that implements the {@code interface ReplaceFunction}. The 460 * interface has a single-method that will receive the position in the {@code Vector}, along 461 * with the {@code HTMLNode} found at that location. It is expected to produce a new version 462 * of the {@code HTMLNode}. This new node will be substituted into the page or sup-page. 463 * 464 * <BR /><BR />This function-pointer may return null, and when it does, <I>the node located at 465 * the current loop-iteration's {@code Vector}-index will not be replaced</I>. 466 * 467 * @return The number of nodes that were succesfully replaced. This number will be equal to 468 * {@code posArr.length} minus the number of times {@code rf} returned null; 469 * 470 * @throws ArrayIndexOutOfBoundsException <B><SPAN STYLE="color: red;">IMPORTANT 471 * NOTE:</B></SPAN> Usually, a position-{@code array} is generated by one of the search-methods 472 * in the NodeSearch package. If, however, the {@code Vector} has since changed and 473 * {@code posArr} contains stale-data, or if for other reasons there are invalid index-pointers 474 * in {@code posArr}, then an {@code ArrayIndexOutOfBoundsException} will, naturally, be thrown 475 * by java's {@code Vector.setElementAt(...)} method. 476 * 477 * @see ReplaceFunction 478 */ 479 public static int r(Vector<HTMLNode> html, int[] posArr, ReplaceFunction rf) 480 { 481 int counter=0, numReplaced=0; 482 HTMLNode n; 483 484 for (int pos : posArr) 485 486 // pos is the vector-position, counter is the "iteration-count" 487 if ((n = rf.getReplacement(html.elementAt(pos), pos, counter++)) != null) 488 { 489 html.setElementAt(n, pos); 490 numReplaced++; 491 } 492 493 return numReplaced; 494 } 495 496 /** 497 * This will replace <I>each and every node indicated by {@code 'posArr'}</I> with the 498 * exact same replacement node {@code 'n'}. 499 * 500 * @param html Any HTML page or section that has been loaded already. 501 * 502 * @param posArr This is usually generated by one of the node-search {@code 'Find.all(...)'} 503 * methods. Each and every one of the {@code 'Find.all(...)'} methods in the search package 504 * will return an array of integers. These integers represent positions/locations in the 505 * passed HTML page {@code Vector}. 506 * 507 * @param n This may be any non-null {@code HTMLNode}. This node shall be inserted (and will 508 * replace) each node indicated by the parameter {@code 'posArr'}. 509 * 510 * @throws ArrayIndexOutOfBoundsException <B><SPAN STYLE="color: red;">IMPORTANT 511 * NOTE:</B></SPAN> Usually, a position-{@code array} is generated by one of the search-methods 512 * in the NodeSearch package. If, however, the {@code Vector} has since changed and 513 * {@code posArr} contains stale-data, or if for other reasons there are invalid index-pointers 514 * in {@code posArr}, then an {@code ArrayIndexOutOfBoundsException} will, naturally, be thrown 515 * by java's {@code Vector.setElementAt(...)} method. 516 */ 517 public static void r(Vector<HTMLNode> html, int[] posArr, HTMLNode n) 518 { 519 int len= html.size(); 520 521 for (int i=0; i < len; i++) html.setElementAt(n, i); 522 } 523 524 /** 525 * Convenience Method. 526 * <BR />Invokes: {@link #r(Vector, int, int, ReplaceFunction)} 527 * <BR />Passes: Entire Range of {@code html} into {@code sPos & ePos} 528 */ 529 public static int r(Vector<HTMLNode> html, ReplaceFunction rf) 530 { return r(html, 0, -1, rf); } 531 532 /** 533 * Convenience Method. 534 * <BR />Invokes: {@link #r(Vector, int, int, ReplaceFunction)} 535 * <BR />Passes: {@link DotPair#start} & {@link DotPair#end} into {@code sPos & ePos} 536 */ 537 public static int r(Vector<HTMLNode> html, DotPair dp, ReplaceFunction rf) 538 { return r(html, dp.start, dp.end + 1, rf); } 539 540 /** 541 * Iterates <I>the entire html-page, checking every node</I>, replacing them by the 542 * values returned by {@code 'rf'} (the replace function). The {@link ReplaceFunction} 543 * (parameter {@code 'rf'}) is expected to return values that either: 544 * 545 * <BR /><BR /><UL CLASS=JDUL> 546 * <LI>provide a replacement {@code HTMLNode} for the indicated position</LI> 547 * <LI>return null as a value - in which case, <I>no substitution will occur</I></LI> 548 * </UL> 549 * 550 * <BR /><BR /><B CLASS=JDDescLabel>Returning 'null':</B> 551 * 552 * <BR />If the Lambda-Target / Functional-Interface {@link ReplaceFunction} (whose method 553 * is named {@link ReplaceFunction#getReplacement(HTMLNode, int, int)} 554 * <B><I STYLE='color: red;'>returns <CODE>'null'</CODE></I></B>, 555 * then in this particular method (differing from a previous method in this class), the 556 * returned 'null' will be ignored, and no substitution will be performed. 557 * 558 * @param html Any HTML page or section that has been loaded already. 559 * 560 * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSVEC> 561 * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSVEC> 562 * 563 * @param rf This is just a class that implements the {@code interface ReplaceFunction}. The 564 * interface has a single-method (@code getReplacement(...)} that will receive the position in 565 * the {@code Vector}, along with the {@code HTMLNode} found at that location. It is expected 566 * to return a new version of the {@code HTMLNode}. 567 * 568 * <BR /><BR />This function-pointer may return null, and when it does, <I>the node located at 569 * the current loop-iteration's {@code Vector}-index will not be replaced</I>. 570 * 571 * @return The number of nodes that were succesfully replaced. This number will be equal to 572 * {@code html.size()} minus the number of times {@code rf} returned null; 573 * 574 * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX> 575 * 576 * @see ReplaceFunction 577 * @see HTMLNode 578 */ 579 public static int r(Vector<HTMLNode> html, int sPos, int ePos, ReplaceFunction rf) 580 { 581 LV l = new LV(html, sPos, ePos); 582 int numReplaced = 0; 583 584 for (int i=l.start; i < l.end; i++) 585 { 586 // Here the vector-position and iteration-number are the same 587 HTMLNode n = rf.getReplacement(html.elementAt(i), i, i); 588 589 if (n != null) 590 { 591 html.setElementAt(n, i); 592 numReplaced++; 593 } 594 } 595 596 return numReplaced; 597 } 598 599 /** 600 * Iterates the integer-pointer values listed by {@code 'posArr'}, and replaces every position 601 * in {@code 'html'} with an {@code HTMLNode} from the nodes provided by the {@code 'newNodes'} 602 * parameter. 603 * 604 * @param html Any HTML page or section that has been loaded already. 605 * 606 * @param posArr This is usually generated by one of the node-search {@code 'Find.all(...)'} 607 * methods. Each and every one of the {@code 'Find.all(...)'} methods in the search package 608 * will return an {@code int[] array}. These integers represent positions/locations in the 609 * passed HTML page {@code Vector.} 610 * 611 * @param newNodes This list of new nodes must have a length identical to the 612 * {@code int[] posArr} (pointer-Array) length. 613 * 614 * @throws ArrayIndexOutOfBoundsException This exception will throw if any of the elements of 615 * {@code 'posArr'} point to a position in the {@code Vector<HTMLNode> v} parameter that are 616 * out of bounds for that {@code Vector}. 617 * 618 * @throws IllegalArgumentException if the length of the position array (pointer-array) is not 619 * identical to the length of the new-nodes {@code Vector.} 620 */ 621 public static void r(Vector<HTMLNode> html, int[] posArr, Vector<HTMLNode> newNodes) 622 { 623 if (posArr.length != newNodes.size()) throw new ArrayIndexOutOfBoundsException( 624 "The pointer array 'posArr', and the replacement-node array 'newNodes' do not have " + 625 "equal lengths!\n" + 626 "posArr.length=" + posArr.length + ", newNodes.size()=" + newNodes.size() 627 ); 628 629 int newNodesPos = 0; 630 631 for (int pos : posArr) html.setElementAt(newNodes.elementAt(newNodesPos++), pos); 632 } 633 634 /** 635 * Convenience Method. 636 * <BR />Invokes: {@link #r(Vector, int, int, Vector)} 637 * <BR />Assumes: {@code rangeWithUpdates.html} is located in {@code html} at 638 * {@code rangeWithUpdates.location} 639 * <BR />And: That {@code rangeWithUpdates.html} has been changed, and needs updating. 640 */ 641 public static int r(Vector<HTMLNode> html, SubSection rangeWithUpdates) 642 { 643 return r( 644 html, rangeWithUpdates.location.start, rangeWithUpdates.location.end, 645 rangeWithUpdates.html 646 ); 647 } 648 649 /** 650 * Convenience Method. 651 * <BR />Invokes: {@link #r(Vector, int, int, Vector)} 652 */ 653 public static int r(Vector<HTMLNode> html, DotPair range, Vector<HTMLNode> newNodes) 654 { return r(html, range.start, range.end + 1, newNodes); } 655 656 /** 657 * Replaces the nodes currently within the vectorized HTML parameter {@code 'html'}, in the 658 * sub-range provided by the {@code 'sPos'} and {@code 'ePos'} parameters - <I>using the new 659 * nodes provided by {@code Vector}-Parameter {@code 'newNodes'}.</I> This is, essentially, 660 * a sub-range array-replacement operation. 661 * 662 * <BR /><BR />Unless exactly the same number of nodes that are in the {@code 'replaceRange'} 663 * are also in {@code 'newNodes'}, this method shall have to shorten or lengthen the size of 664 * the HTML {@code Vector}. 665 * 666 * @param html This may be any HTML page or sub-page 667 * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSVEC> 668 * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSVEC> 669 * @param newNodes These are the new {@code HTMLNode's} that are to replace the old ones. 670 * @return The change in the size (size-delta) of the input {@code html} parameter. 671 * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX> 672 */ 673 public static int r(Vector<HTMLNode> html, int sPos, int ePos, Vector<HTMLNode> newNodes) 674 { 675 // The loop variable is needed because its constructor does all of the error checking. 676 // The constructor also checks for the negative-ePos, and changes it if it is negative 677 // 678 // The original version of this method has been deprected, and left as a private method to 679 // this class. It was before noticing the "mirrored" stipulations about Vector-operation 680 // "subList" View the source-code to see the original replace-range method. 681 682 LV l = new LV(html, sPos, ePos); 683 684 List<HTMLNode> list = html.subList(l.start, l.end); 685 686 // The Sun-Oracle Docs say that changes to the list returned by sub-list are mirrored into 687 // changes in the original vector. This is how sub-range operations are done. 688 689 list.clear(); 690 list.addAll(newNodes); 691 692 return newNodes.size() - (ePos - sPos); // ==> (newSize - originalSize) 693 } 694 695 // This was how to do this, until noticing in the Java-Docs that "subList" is "linked" 696 // to the original list! 697 private static void rOLD(Vector<HTMLNode> html, int sPos, int ePos, Vector<HTMLNode> newNodes) 698 { 699 LV l = new LV(html, sPos, ePos); 700 701 // Replacement while-loop. This will replace all nodes that can be replaced in the 702 // "replace range" - without growing or shrinking the size of the underlying vector. 703 // 704 // AFTERWARDS If there are still more nodes to insert, they will be inserted by 705 // growing the vector. 706 // 707 // AND ALSO If there were fewer nodes to insert than the number that need to be 708 // removed the Vector will be shortened. 709 710 int i = 0; 711 int j = sPos; 712 713 while ((j < l.end) && (i < newNodes.size())) 714 { 715 html.setElementAt(newNodes.elementAt(i), j); 716 717 i++; j++; 718 } 719 720 721 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 722 // CASE 1: The number of nodes to remove is precisely equal to the number being inserted. 723 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 724 725 if (l.size() == newNodes.size()) return; 726 727 728 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 729 // CASE 2: The number of nodes being removed is greater than the number being inserted 730 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 731 732 // Use Util.removeRange(...) to remove the remaining nodes that weren't replaced. 733 // More nodes need to be removed than the number of nodes that have already been inserted 734 // (in the previous loop). In this case the original HTML Vector will SHRINK in SIZE. 735 736 if (j < l.end) { Util.Remove.range(html, j, l.end); return; } 737 738 739 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 740 // CASE 3: There are more nodes being added than the number of nodes being removed. 741 // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 742 743 // Use Vector.addAll(...) to add the rest which haven't been inserted yet. 744 // The HTML Vector is going to grow. There were fewer nodes removed than the number of 745 // nodes that need to be inserted. 746 // 747 // NOTE: An "intermediate temp Vector" is used since the "Node Shift" is likely done more 748 // efficiently by 'Vector.addAll', than by calling 'Vector.add' multiple times. 749 750 Vector<HTMLNode> temp = new Vector<>(newNodes.size() - i); 751 752 while (i < newNodes.size()) temp.add(newNodes.elementAt(i++)); 753 754 html.addAll(l.end, temp); 755 } 756 757 /** 758 * Convenience Method. 759 * Invokes: {@link #r(Vector, int, int, HTMLNode)} 760 */ 761 public static int r(Vector<HTMLNode> html, DotPair range, HTMLNode newNode) 762 { return r(html, range.start, range.end + 1, newNode); } 763 764 /** 765 * Replaces the nodes currently within the vectorized HTML parameter {@code 'html'}, in the 766 * sub-range provided by the {@code 'sPos'} and {@code 'ePos'} parameters, <I>with a single 767 * new node provided by {@code 'newNode'}.</I> Essentially, this is a <B>Range Removal</B> 768 * Operation, because a complete sublist is removed, and only a single-node replaces it. 769 * 770 * <BR /><BR />Unless the replacement-range (defined by {@code 'sPos'} and {@code 'ePos'}) has 771 * a size equal to {@code '1'}, this operation will (obviously) shorten the size of the input 772 * HTML {@code Vector} by {@code size - 1} nodes. 773 * 774 * @param html This may be any HTML page or sub-page 775 * 776 * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSVEC> 777 * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSVEC> 778 * 779 * @param newNode This is the new {@code HTMLNode} that is to replace the (entire) list 780 * specified by parameters {@code 'sPos'} and {@code 'ePos'}. 781 * 782 * @return The change in the size (size-delta) of the input {@code html} parameter. 783 * The number returned will always equal {@code 1 - (ePos - sPos)}. This means the return 784 * value for this method will always be negative (i.e. the {@code Vector} shrunk) - unless 785 * {@code sPos} and {@code ePos} had specified a range that was equal to 1. 786 * 787 * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX> 788 */ 789 public static int r(Vector<HTMLNode> html, int sPos, int ePos, HTMLNode newNode) 790 { 791 // This method doesn't have any "for-loops", but the LV class does all the much needed 792 // exception checks, and conversion computations. (ePos < 0 ==> epos = html.size()) 793 794 LV l = new LV(html, sPos, ePos); 795 796 html.setElementAt(newNode, l.start); 797 798 if (l.size() > 1) 799 800 // Util.removeRange(html, l.start + 1, l.end); // OLD-WAY 801 html.subList(l.start + 1, l.end).clear(); // NEW & IMPROVED WAY 802 803 return 1 - (ePos - sPos); // ==> (newSize - originalSize) 804 } 805 806 /** 807 * Replaces the instance of {@code HTMLNode} located at {@code Vector}-index {@code 'pos'} 808 * with the contents of {@code Vector} parameter {@code 'newNodes'}. This removes just 809 * a single instance of {@code HTMLNode}, and replaces it with a list of nodes. 810 * <BR /><BR />Note that this method will, indeed, lengthen the size of the input HTML 811 * {@code Vector} (unless the {@code 'newNodes' Vector} being inserted has only 1 or 0 812 * elements). 813 * 814 * @param html This may be any HTML page or sub-page. 815 * 816 * @param replacePos The position of the {@code HTMLNode} to be removed and replaced with the 817 * list of nodes. 818 * 819 * @param newNodes These are the new {@code HTMLNode's} that are to replace the old instance 820 * of {@code HTMLNode} at position {@code 'pos'}. 821 * 822 * @return The change in the size (size-delta) of the input {@code html} parameter. 823 * The number returned will always equal {@code newNodes.size() - 1} 824 * 825 * @throws ArrayIndexOutOfBoundsException This exception will throw if the specified 826 * {@code 'pos'} parameter is not within the bounds of the {@code Vector}. 827 */ 828 public static int r(Vector<HTMLNode> html, int replacePos, Vector<HTMLNode> newNodes) 829 { 830 if (replacePos < 0) throw new ArrayIndexOutOfBoundsException( 831 "The position passed to this method [" + replacePos + "] is negative." 832 ); 833 834 if (replacePos >= newNodes.size()) throw new ArrayIndexOutOfBoundsException( 835 "The position passed to this method [" + replacePos + "] is greater than or equal " + 836 " to the size of the input HTML Vector parameter, 'html' [" + html.size() + "]" 837 ); 838 839 html.removeElementAt(replacePos); 840 html.addAll(replacePos, newNodes); 841 842 return newNodes.size() - 1; // ==> (newSize - originalSize) 843 } 844}