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;
011
012/**
013 * Methods for quickly & efficiently replacing the nodes on a Web-Page.
014 * 
015 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=REPLACE_NODES>
016 */
017@Torello.JavaDoc.StaticFunctional
018public class ReplaceNodes
019{
020    private ReplaceNodes() { }
021
022    /**
023     * Allows a user to quickly alter each <B STYLE='color: red;'>row</B> in an HTML
024     * <B STYLE='color: red;'>table</B>, iteratively, in a manner that offers a tremendous
025     * efficiency-improvement over the HTML {@code Iterator's} in the Node Seach Package.
026     * 
027     * <EMBED CLASS='external-html' DATA-FILE-ID=REPL_NODES_FAST DATA-ITEM='table-row'>
028     * 
029     * <BR /><BR /><UL CLASS=JDUL>
030     * <LI> {@code 'page'} is not null.
031     *      </LI>
032     * <LI> {@code 'table'} is non-null, and has {@link DotPair#start} and {@link DotPair#end}
033     *      indices with integer-values smaller than the size of {@code 'page'}
034     *      </LI>
035     * <LI> {@code 'table'} has indices which point to {@code TagNode's} in {@code 'page'} that
036     *      are opening {@code <TABLE>} and closing {@code </TABLE>} tags.
037     *      </LI>
038     * </UL>
039     * 
040     * @param page Any HTML Page or sub-page that has a table.
041     * 
042     * @param table A pointer to the table start and end index bounds.
043     * 
044     * @param tableRowModifier <EMBED CLASS='external-html' DATA-TYPE=Table DATA-ENTITY=Row 
045     *      DATA-FILE-ID=REPL_NODES_ROW_MOD>
046     * 
047     * @return <EMBED CLASS='external-html' DATA-FILE-ID=REPLACEMENT_MRET>
048     * <!-- Bororwed from Replacement.run(...) -->
049     * 
050     * @see TagNodePeekL1Inclusive
051     * @see Replacement#run(Vector, Iterable, boolean)
052     * @see DotPair#exceptionCheck(Vector, String[])
053     * @see SubSection
054     * <!-- In the JD Comments 'external-html/' directory, this is filed under 'r02r03/' -->
055     */
056    public static Vector<HTMLNode> tableTR(
057            Vector<HTMLNode> page, DotPair table,
058            ObjIntConsumer<Vector<HTMLNode>> tableRowModifier
059        )
060    {
061        // Ensure that page.elementAt(table.start) contains a "<TABLE>" element, and that
062        // page.elementAt(table.end) contains a "</TABLE>" element.
063        //
064        // If some other type of TagNode, or a non-TagNode is present, this method throws one of
065        // several exceptions to inform the user about the error.
066
067        table.exceptionCheck(page, "table");
068
069        // Retrieve all "<TR> ... </TR>" elements.  The "L1Inclusive" stipulates that any potential
070        // inner-table rows (if there are any inner-tables), should be ignored.
071
072        Vector<SubSection> rows = TagNodePeekL1Inclusive.all(page, table.start, table.end, "tr");
073
074        // All this does is invoke the user-provided function-pointer on each table-row
075        for (int i=0; i < rows.size(); i++) tableRowModifier.accept(rows.elementAt(i).html, i);
076
077        // Update all Table-Rows.  Remove the old Rows, and insert the new ones.  This replace
078        // operation does this much more efficiently than most replacement-code.
079
080        return Replacement.run(page, rows, false).a;
081    }
082
083    /**
084     * Allows a user to quickly alter each <B STYLE='color: red;'>item</B> in an HTML
085     * <B STYLE='color: red;'>list</B> ({@code <OL>, <UL>} or {@code <MENU>}), iteratively, in a
086     * manner that offers a tremendous efficiency-improvement over the HTML {@code Iterator's} in
087     * the Node Seach Package.
088     * 
089     * <EMBED CLASS='external-html' DATA-FILE-ID=REPL_NODES_FAST DATA-ITEM='list-item'>
090     * 
091     * <BR /><BR /><UL CLASS=JDUL>
092     * <LI> {@code 'page'} is not null.
093     *      </LI>
094     * <LI> {@code 'list'} is non-null, and has {@link DotPair#start} and {@link DotPair#end}
095     *      indices with integer-values smaller than the size of {@code 'page'}
096     *      </LI>
097     * <LI> {@code 'list'} has indices which point to {@code TagNode's} in {@code 'page'} that
098     *      are opening-and-closing {@code <UL>, <OL>} or {@code <MENU>} tags.
099     *      </LI>
100     * </UL>
101     * 
102     * @param page Any HTML Page or sub-page that has an {@code <OL>, <UL>} or {@code <MENU>}.
103     * 
104     * @param list A pointer to the list start and end index bounds.
105     * 
106     * @param listItemModifier <EMBED CLASS='external-html' DATA-TYPE=List DATA-ENTITY=Item 
107     *      DATA-FILE-ID=REPL_NODES_ROW_MOD>
108     * 
109     * @return <EMBED CLASS='external-html' DATA-FILE-ID=REPLACEMENT_MRET> <!-- Bororwed from R01 RET -->
110     * 
111     * @see TagNodePeekL1Inclusive
112     * @see Replacement#run(Vector, Iterable, boolean)
113     * @see DotPair#exceptionCheck(Vector, String[])
114     * @see SubSection
115     * <!-- In the JD Comments 'external-html/' directory, this is filed under 'r02r03/' -->
116     */
117    public static Vector<HTMLNode> listLI(
118            Vector<HTMLNode> page, DotPair list,
119            ObjIntConsumer<Vector<HTMLNode>> listItemModifier
120        )
121    {
122        // Ensure that page.elementAt(list.start) contains an "<OL>", "<UL>", or "<MENU>" element,
123        // and that page.elementAt(list.end) contains an "</OL>", "</UL>" or "</MENU>" element.
124        //
125        // If some other type of TagNode, or a non-TagNode is present, this method throws one of
126        // several exceptions to inform the user about the error.
127
128        list.exceptionCheck(page, "ol", "ul", "menu");
129
130        // Retrieve all "<LI> ... </LI>" elements.  The "L1Inclusive" stipulates that any potential
131        // inner-list items (if there are any inner-lists), should be ignored.
132
133        Vector<SubSection> items = TagNodePeekL1Inclusive.all(page, list.start, list.end, "li");
134
135        // All this does is invoke the user-provided function-pointer on each list-item
136        for (int i=0; i < items.size(); i++) listItemModifier.accept(items.elementAt(i).html, i);
137
138        // Update all items.  Remove the old-Items, and insert the new ones.  This replace
139        // operation does this much more efficiently than most replacement-code.
140
141        return Replacement.run(page, items, false).a;
142    }
143
144    /**
145     * Iterates the integer-pointer values in {@code int[] posArr}, and replaces the nodes inside
146     * {@code 'html'} that have been identified by the {@code 'posArr'} list with a new node
147     * supplied by the {@code interface ReplaceFunction}.  It should be obvious, that lambda
148     * expressions may be used here.
149     * 
150     * <BR /><BR /><B CLASS=JDDescLabel>Returning 'null':</B>
151     * 
152     * <BR />If the Lambda-Target / Functional-Interface {@link ReplaceFunction} (whose method
153     * is named {@link ReplaceFunction#getReplacement(HTMLNode, int, int)}
154     * <B><I STYLE='color: red;'>actually returns <CODE>'null'</CODE></I></B>,
155     * then null will indeed by inserted into the corresponding {@code Vector} position.
156     * 
157     * @param html Any HTML page or section that has been loaded already.
158     * 
159     * @param posArr This is usually generated by one of the node-search {@code 'Find.all(...)'}
160     * methods.  Each and every one of the {@code 'Find.all(...)'} methods in the search package
161     * will return an array of integers.  These integers represent positions/locations in the
162     * passed HTML page {@code Vector}.
163     * 
164     * @param rf This is just a class that implements the {@code interface ReplaceFunction}.  The
165     * interface has a single-method that will receive the position in the {@code Vector}, along
166     * with the {@code HTMLNode} found at that location.  It is expected to produce a new version
167     * of the {@code HTMLNode}.  This new node will be substituted into the page or sup-page.
168     * 
169     * <BR /><BR />This function-pointer may return null, and when it does, <I>the node located at
170     * the current loop-iteration's {@code Vector}-index will not be replaced</I>.
171     * 
172     * @return The number of nodes that were succesfully replaced.  This number will be equal to
173     * {@code posArr.length} minus the number of times {@code rf} returned null;
174     * 
175     * @throws ArrayIndexOutOfBoundsException <B><SPAN STYLE="color: red;">IMPORTANT
176     * NOTE:</B></SPAN> Usually, a position-{@code array} is generated by one of the search-methods
177     * in the NodeSearch package.  If, however, the {@code Vector} has since changed and
178     * {@code posArr} contains stale-data, or if for other reasons there are invalid index-pointers
179     * in {@code posArr}, then an {@code ArrayIndexOutOfBoundsException} will, naturally, be thrown
180     * by java's {@code Vector.setElementAt(...)} method.
181     * 
182     * @see ReplaceFunction
183     */
184    public static int r(Vector<HTMLNode> html, int[] posArr, ReplaceFunction rf)
185    {
186        int counter=0, numReplaced=0;
187        HTMLNode n;
188
189        for (int pos : posArr)
190
191            // pos is the vector-position, counter is the "iteration-count"
192            if ((n = rf.getReplacement(html.elementAt(pos), pos, counter++)) != null)
193            {
194                html.setElementAt(n, pos);
195                numReplaced++;
196            }
197
198        return numReplaced;
199    }
200
201    /**
202     * This will replace <I>each and every node indicated by {@code 'posArr'}</I> with the
203     * exact same replacement node {@code 'n'}.
204     *
205     * @param html Any HTML page or section that has been loaded already.
206     * 
207     * @param posArr This is usually generated by one of the node-search {@code 'Find.all(...)'}
208     * methods.  Each and every one of the {@code 'Find.all(...)'} methods in the search package
209     * will return an array of integers.  These integers represent positions/locations in the
210     * passed HTML page {@code Vector}.
211     *
212     * @param n This may be any non-null {@code HTMLNode}.  This node shall be inserted (and will
213     * replace) each node indicated by the parameter {@code 'posArr'}.
214     * 
215     * @throws ArrayIndexOutOfBoundsException <B><SPAN STYLE="color: red;">IMPORTANT
216     * NOTE:</B></SPAN> Usually, a position-{@code array} is generated by one of the search-methods
217     * in the NodeSearch package.  If, however, the {@code Vector} has since changed and
218     * {@code posArr} contains stale-data, or if for other reasons there are invalid index-pointers
219     * in {@code posArr}, then an {@code ArrayIndexOutOfBoundsException} will, naturally, be thrown
220     * by java's {@code Vector.setElementAt(...)} method.
221     */
222    public static void r(Vector<HTMLNode> html, int[] posArr, HTMLNode n)
223    { 
224        int len= html.size();
225
226        for (int i=0; i < len; i++) html.setElementAt(n, i);
227    }
228
229    /**
230     * Convenience Method.
231     * <BR />Invokes: {@link #r(Vector, int, int, ReplaceFunction)}
232     * <BR />Passes: Entire Range of {@code html} into {@code sPos & ePos}
233     */
234    public static int r(Vector<HTMLNode> html, ReplaceFunction rf)
235    { return r(html, 0, -1, rf); }
236
237    /**
238     * Convenience Method.
239     * <BR />Invokes: {@link #r(Vector, int, int, ReplaceFunction)}
240     * <BR />Passes: {@link DotPair#start} &amp; {@link DotPair#end} into {@code sPos & ePos}
241     */
242    public static int r(Vector<HTMLNode> html, DotPair range, ReplaceFunction rf)
243    { return r(html, range.start, range.end + 1, rf); }
244
245    /**
246     * Iterates <I>the entire html-page, checking every node</I>, replacing them by the
247     * values returned by {@code 'rf'} (the replace function).  The {@link ReplaceFunction}
248     * (parameter {@code 'rf'}) is expected to return values that either:
249     * 
250     * <BR /><BR /><UL CLASS=JDUL>
251     * <LI>provide a replacement {@code HTMLNode} for the indicated position</LI>
252     * <LI>return null as a value - in which case, <I>no substitution will occur</I></LI>
253     * </UL>
254     *
255     * <BR /><BR /><B CLASS=JDDescLabel>Returning 'null':</B>
256     * 
257     * <BR />If the Lambda-Target / Functional-Interface {@link ReplaceFunction} (whose method
258     * is named {@link ReplaceFunction#getReplacement(HTMLNode, int, int)}
259     * <B><I STYLE='color: red;'>returns <CODE>'null'</CODE></I></B>,
260     * then in this particular method (differing from a previous method in this class), the
261     * returned 'null' will be ignored, and no substitution will be performed.
262     * 
263     * @param html Any HTML page or section that has been loaded already.
264     * 
265     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSVEC>
266     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSVEC>
267     * 
268     * @param rf This is just a class that implements the {@code interface ReplaceFunction}.  The
269     * interface has a single-method (@code getReplacement(...)} that will receive the position in
270     * the {@code Vector}, along with the {@code HTMLNode} found at that location.  It is expected
271     * to return a new version of the {@code HTMLNode}.
272     * 
273     * <BR /><BR />This function-pointer may return null, and when it does, <I>the node located at
274     * the current loop-iteration's {@code Vector}-index will not be replaced</I>.
275     * 
276     * @return The number of nodes that were succesfully replaced.  This number will be equal to
277     * {@code html.size()} minus the number of times {@code rf} returned null;
278     * 
279     * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX>
280     * 
281     * @see ReplaceFunction
282     * @see HTMLNode
283     */
284    public static int r(Vector<HTMLNode> html, int sPos, int ePos, ReplaceFunction rf)
285    {
286        LV  l           = new LV(html, sPos, ePos);
287        int numReplaced = 0;
288
289        for (int i=l.start; i < l.end; i++)
290        {
291            // Here the vector-position and iteration-number are the same
292            HTMLNode n = rf.getReplacement(html.elementAt(i), i, i);
293
294            if (n != null)
295            {
296                html.setElementAt(n, i);
297                numReplaced++;
298            }
299        }
300
301        return numReplaced;
302    }
303
304    /**
305     * Iterates the integer-pointer values listed by {@code 'posArr'}, and replaces every position
306     * in {@code 'html'} with an {@code HTMLNode} from the nodes provided by the {@code 'newNodes'}
307     * parameter.
308     * 
309     * @param html Any HTML page or section that has been loaded already.
310     * 
311     * @param posArr This is usually generated by one of the node-search {@code 'Find.all(...)'} 
312     * methods.  Each and every one of the {@code 'Find.all(...)'} methods in the search package 
313     * will return an {@code int[] array}.  These integers represent positions/locations in the
314     * passed HTML page {@code Vector.}
315     * 
316     * @param newNodes This list of new nodes must have a length identical to the
317     * {@code int[] posArr} (pointer-Array) length.
318     * 
319     * @throws ArrayIndexOutOfBoundsException This exception will throw if any of the elements of
320     * {@code 'posArr'} point to a position in the {@code Vector<HTMLNode> v} parameter that are
321     * out of bounds for that {@code Vector}.
322     * 
323     * @throws IllegalArgumentException if the length of the position array (pointer-array) is not
324     * identical to the length of the new-nodes {@code Vector.}
325     */
326    public static void r(Vector<HTMLNode> html, int[] posArr, Vector<HTMLNode> newNodes)
327    {
328        if (posArr.length != newNodes.size()) throw new ArrayIndexOutOfBoundsException(
329            "The pointer array 'posArr', and the replacement-node array 'newNodes' do not have " +
330            "equal lengths!\n" +
331            "posArr.length=" + posArr.length + ", newNodes.size()=" + newNodes.size()
332        );
333
334        int newNodesPos = 0;
335
336        for (int pos : posArr) html.setElementAt(newNodes.elementAt(newNodesPos++), pos);
337    }
338
339    /**
340     * Convenience Method.
341     * <BR />Invokes: {@link #r(Vector, int, int, Vector)}
342     */
343    public static int r(Vector<HTMLNode> html, DotPair range, Vector<HTMLNode> newNodes)
344    { return r(html, range.start, range.end + 1, newNodes); }
345
346    /**
347     * Replaces the nodes currently within the vectorized HTML parameter {@code 'html'}, in the
348     * sub-range provided by the {@code 'sPos'} and {@code 'ePos'} parameters - <I>using the new
349     * nodes provided by {@code Vector}-Parameter {@code 'newNodes'}.</I>  This is, essentially,
350     * a sub-range array-replacement operation.
351     * 
352     * <BR /><BR />Unless exactly the same number of nodes that are in the {@code 'replaceRange'}
353     * are also in {@code 'newNodes'}, this method shall have to shorten or lengthen the size of
354     * the HTML {@code Vector}.
355     * 
356     * @param html This may be any HTML page or sub-page
357     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSVEC>
358     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSVEC>
359     * @param newNodes These are the new {@code HTMLNode's} that are to replace the old ones.
360     * @return The change in the size (size-delta) of the input {@code html} parameter.
361     * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX>
362     */
363    public static int r(Vector<HTMLNode> html, int sPos, int ePos, Vector<HTMLNode> newNodes)
364    {
365        // The loop variable is needed because its constructor does all of the error checking.
366        // The constructor also checks for the negative-ePos, and changes it if it is negative
367        //
368        // The original version of this method has been deprected, and left as a private method to
369        // this class.  It was before noticing the "mirrored" stipulations about Vector-operation
370        // "subList"  View the source-code to see the original replace-range method.
371
372        LV l = new LV(html, sPos, ePos);
373
374        List<HTMLNode> list = html.subList(l.start, l.end);
375
376        // The Sun-Oracle Docs say that changes to the list returned by sub-list are mirrored into
377        // changes in the original vector.  This is how sub-range operations are done.
378
379        list.clear();
380        list.addAll(newNodes);
381
382        return newNodes.size() - (ePos - sPos); // ==> (newSize - originalSize)
383    }
384
385
386    // This was how to do this, until noticing in the Java-Docs that "subList" is "linked"
387    // to the original list!
388
389    private static void rOLD(Vector<HTMLNode> html, int sPos, int ePos, Vector<HTMLNode> newNodes)
390    {
391        LV l = new LV(html, sPos, ePos);
392
393        // Replacement while-loop.  This will replace all nodes that can be replaced in the
394        // "replace range" - without growing or shrinking the size of the underlying vector.
395        //
396        // AFTERWARDS   If there are still more nodes to insert, they will be inserted by
397        //              growing the vector.
398        //
399        // AND ALSO     If there were fewer nodes to insert than the number that need to be
400        //              removed the Vector will be shortened.
401
402        int i = 0;
403        int j = sPos;
404
405        while ((j < l.end) && (i < newNodes.size()))
406        {
407            html.setElementAt(newNodes.elementAt(i), j);
408
409            i++; j++;
410        }
411
412
413        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
414        // CASE 1: The number of nodes to remove is precisely equal to the number being inserted.
415        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
416
417        if (l.size() == newNodes.size()) return;
418
419    
420        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
421        // CASE 2: The number of nodes being removed is greater than the number being inserted
422        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
423
424        // Use Util.removeRange(...) to remove the remaining nodes that weren't replaced.
425        // More nodes need to be removed than the number of nodes that have already been inserted
426        // (in the previous loop).  In this case the original HTML Vector will SHRINK in SIZE.
427
428        if (j < l.end) { Util.Remove.range(html, j, l.end);  return; }
429
430
431        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
432        // CASE 3: There are more nodes being added than the number of nodes being removed.
433        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
434
435        // Use Vector.addAll(...) to add the rest which haven't been inserted yet.
436        // The HTML Vector is going to grow.  There were fewer nodes removed than the number of
437        // nodes that need to be inserted.
438        //
439        // NOTE: An "intermediate temp Vector" is used since the "Node Shift" is likely done more
440        //       efficiently by 'Vector.addAll', than by calling 'Vector.add' multiple times.
441
442        Vector<HTMLNode> temp = new Vector<>(newNodes.size() - i);
443
444        while (i < newNodes.size()) temp.add(newNodes.elementAt(i++));
445
446        html.addAll(l.end, temp);
447    }
448
449    /**
450     * Convenience Method.
451     * Invokes: {@link #r(Vector, int, int, HTMLNode)}
452     */
453    public static int r(Vector<HTMLNode> html, DotPair range, HTMLNode newNode)
454    { return r(html, range.start, range.end + 1, newNode); }
455
456    /**
457     * Replaces the nodes currently within the vectorized HTML parameter {@code 'html'}, in the
458     * sub-range provided by the {@code 'sPos'} and {@code 'ePos'} parameters, <I>with a single
459     * new node provided by {@code 'newNode'}.</I>  Essentially, this is a <B>Range Removal</B>
460     * Operation, because a complete sublist is removed, and only a single-node replaces it.
461     * 
462     * <BR /><BR />Unless the replacement-range (defined by {@code 'sPos'} and {@code 'ePos'}) has
463     * a size equal to {@code '1'}, this operation will (obviously) shorten the size of the input
464     * HTML {@code Vector} by {@code size - 1} nodes.
465     * 
466     * @param html This may be any HTML page or sub-page
467     * 
468     * @param sPos <EMBED CLASS='external-html' DATA-FILE-ID=SPOSVEC>
469     * @param ePos <EMBED CLASS='external-html' DATA-FILE-ID=EPOSVEC>
470     * 
471     * @param newNode This is the new {@code HTMLNode} that is to replace the (entire) list
472     * specified by parameters {@code 'sPos'} and {@code 'ePos'}.
473     * 
474     * @return The change in the size (size-delta) of the input {@code html} parameter.
475     * The number returned will always equal {@code 1 - (ePos - sPos)}.  This means the return
476     * value for this method will always be negative (i.e. the {@code Vector} shrunk) - unless
477     * {@code sPos} and {@code ePos} had specified a range that was equal to 1.
478     * 
479     * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX>
480     */
481    public static int r(Vector<HTMLNode> html, int sPos, int ePos, HTMLNode newNode)
482    {
483        // This method doesn't have any "for-loops", but the LV class does all the much needed
484        // exception checks, and conversion computations. (ePos < 0  ==>  epos = html.size())
485
486        LV l = new LV(html, sPos, ePos);
487
488        html.setElementAt(newNode, l.start);
489
490        if (l.size() > 1)
491
492            // Util.removeRange(html, l.start + 1, l.end);  // OLD-WAY
493            html.subList(l.start + 1, l.end).clear();       // NEW & IMPROVED WAY
494
495        return 1 - (ePos - sPos); // ==> (newSize - originalSize)
496    }
497
498    /**
499     * Replaces the instance of {@code HTMLNode} located at {@code Vector}-index {@code 'pos'}
500     * with the contents of {@code Vector} parameter {@code 'newNodes'}.  This removes just
501     * a single instance of {@code HTMLNode}, and replaces it with a list of nodes.
502     * <BR /><BR />Note that this method will, indeed, lengthen the size of the input HTML
503     * {@code Vector} (unless the {@code 'newNodes' Vector} being inserted has only 1 or 0
504     * elements).
505     *
506     * @param html This may be any HTML page or sub-page.
507     *
508     * @param replacePos The position of the {@code HTMLNode} to be removed and replaced with the
509     * list of nodes.
510     * 
511     * @param newNodes These are the new {@code HTMLNode's} that are to replace the old instance
512     * of {@code HTMLNode} at position {@code 'pos'}.
513     * 
514     * @return The change in the size (size-delta) of the input {@code html} parameter.
515     * The number returned will always equal {@code newNodes.size() - 1}
516     * 
517     * @throws ArrayIndexOutOfBoundsException This exception will throw if the specified 
518     * {@code 'pos'} parameter is not within the bounds of the {@code Vector}.
519     */
520    public static int r(Vector<HTMLNode> html, int replacePos, Vector<HTMLNode> newNodes)
521    {
522        if (replacePos < 0) throw new ArrayIndexOutOfBoundsException(
523            "The position passed to this method [" + replacePos + "] is negative."
524        );
525
526        if (replacePos >= newNodes.size()) throw new ArrayIndexOutOfBoundsException(
527            "The position passed to this method [" + replacePos + "] is greater than or equal " +
528            " to the size of the input HTML Vector parameter, 'html' [" + html.size() + "]"
529        );
530
531        html.removeElementAt(replacePos);
532        html.addAll(replacePos, newNodes);
533
534        return newNodes.size() - 1; // ==> (newSize - originalSize)
535    }
536}