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