001package Torello.HTML.NodeSearch;
002
003import java.util.*;
004
005import java.util.function.Predicate;
006
007import Torello.HTML.*;
008
009import Torello.Java.LV;
010
011/**
012 * Abstract parent-class for both of the types of HTML-{@code Iterator's}
013 * 
014 * <EMBED CLASS='external-html' DATA-FILE-ID=AbstractHNLI>
015 * 
016 * @param <E> This must be either {@link TagNode}, {@link TextNode} or {@link CommentNode}.
017 * This is type of the <I><B>query-specifier {@code Predicate}</B></I> being used.
018 * 
019 * @param <F> This is the actual-type that will be iterated.  For instances of {@link HNLI},
020 * both type-parameters {@code 'E'} and {@code 'F'} are identical.  The {@code Predicate's}
021 * will be querrying for a certain type of {@code HTMLNode}, and the {@code Iterator} will be
022 * iterating that type of {@code HTMLNode} too.
023 * 
024 * <BR /><BR />However, for instances of {@link HNLIInclusive}, the query-specifier is 
025 * (automatically-required) to be set to {@code TagNode}, and the {@code Iterator} shall be
026 * returning instances of {@code Vector<HTMLNode>}.  Generic Type-Parameter {@code 'F'} is
027 * automatically set to {@code Vector<HTMLNode>} for instances of {@code HNLIInclusive}.
028 */
029@SuppressWarnings("unchecked")
030@Torello.JavaDoc.JDHeaderBackgroundImg
031public abstract class AbstractHNLI<E extends HTMLNode, F> implements ListIterator<F>
032{
033    // ********************************************************************************************
034    // ********************************************************************************************
035    // FIELDS
036    // ********************************************************************************************
037    // ********************************************************************************************
038
039
040    /** The internal, underlying instance of {@code Vector} that this {@code Iterator} is using. */
041    @SuppressWarnings("rawtypes")
042    protected Vector v;
043
044    /** This {@code Predicate} is the test for identifying {@code Iterator} node-matches */
045    protected Predicate<E> p;
046
047    /** This is the type of {@code HTMLNode} being iterated */
048    protected Class<E> c;
049
050    /**
051     * Internal {@code 'cursor'} field.
052     *
053     * <BR /><BR />It is initialized to -1, which allows the iterator to identify whether it has
054     * been used yet.
055     */
056    protected int cursor = -1;
057
058    /**
059     * This identifies whether one of the {@code Iterator's} html modification methods
060     * ({@code set, remove, add}) have been invoked.  Multiple invocations of these methods
061     * after a single iteration causes a {@code SecondModificationException}
062     *
063     * <BR /><BR />Initializing this to {@code TRUE} prevents the programmer from calling any of
064     * the modification methods without first finding a match.
065     */
066    protected boolean modifiedSince = true;
067
068    /**
069     * The maximum boundary for the {@code Cursor Boundary Window}.
070     *
071     * <BR /><BR />Initialized to {@code '-1'} means the default setting is for the
072     * {@code Iterator} is not to use a {@code Cursor Boundaries}.  One may be assigned
073     * using the {@code restrictCursor(...)} methods.
074     * 
075     * @see #restrictCursor(DotPair)
076     * @see #restrictCursor(int, int)
077     * @see #clearCursorBounds()
078     */
079    protected int maxCursor = -1;
080
081    /**
082     * The minimum boundary for the {@code Cursor Boundary Window}.
083     *
084     * <BR /><BR />Initialized to {@code '-1'} means the default setting is for the
085     * {@code Iterator} is not to use a {@code Cursor Boundaries}.  One may be assigned
086     * using the {@code restrictCursor(...)} methods.
087     * 
088     * @see #restrictCursor(DotPair)
089     * @see #restrictCursor(int, int)
090     * @see #clearCursorBounds()
091     */
092    protected int minCursor = -1;
093
094    /** This is an attempt to monitor outside modifications to an HTML {@code Vector} */
095    protected int expectedSize;
096
097
098    // ********************************************************************************************
099    // ********************************************************************************************
100    // Only Constructor, and 2 Abstract-Methods
101    // ********************************************************************************************
102    // ********************************************************************************************
103
104
105    /**
106     * Constructs an instance of this class using an HTML {@code Vector}, and a 
107     * {@code Predicate} for testing the nodes in that {@code Vector} for matches.
108     * The third parameter {@code 'c'} is only necessary because of Java's type-erasure
109     * problem.
110     * 
111     * @param html Any vectorized HTML page or sub-page.
112     * 
113     * @param p A {@code java.util.function.Predicate} for testing the nodes in the html 
114     * {@code Vector} for matches.
115     * 
116     * @param c The sub-class of {@code HTMLNode} that this {@code HNLI} shall be searching.
117     * The values for parameter {@code 'c'} include: {@code TagNode.class, TextNode}, and
118     * {@code CommentNode.class}
119     */
120    protected AbstractHNLI(Vector<?> html, Predicate<E> p, Class<E> c)
121    {
122        this.expectedSize   = html.size();
123        this.v              = html;
124        this.p              = p;
125        this.c              = c;
126    }
127
128    // Implemented by both sub-classes
129    abstract void RESET_MATCHES();
130
131    // Implemented by both sub-classes
132    abstract int REMOVE();
133
134
135    // ********************************************************************************************
136    // ********************************************************************************************
137    // Stuff
138    // ********************************************************************************************
139    // ********************************************************************************************
140
141
142    protected final void MODIFIED()
143    {
144        modifiedSince   = true;
145        expectedSize    = v.size();
146
147        RESET_MATCHES();
148    }
149
150    /**
151     * This removes the last match that was returned by the {@code Iterator} out of the
152     * underlying {@code Vector}.
153     * 
154     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
155     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
156     * 
157     * @throws ConcurrentModificationException
158     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
159     * 
160     * @see #CHECK_EXCEPTIONS()
161     * @see #MODIFIED()
162     */
163    public void remove()
164    {
165        CHECK_EXCEPTIONS();
166
167        int numRemoved = REMOVE();
168
169        if (maxCursor != -1) maxCursor -= numRemoved;
170
171        MODIFIED();
172    }
173
174
175    // ********************************************************************************************
176    // ********************************************************************************************
177    // Cursor-Methods
178    // ********************************************************************************************
179    // ********************************************************************************************
180
181
182    /**
183     * Convenience Method.
184     * <BR />Invokes: {@link #moveCursor(int)}
185     * <BR />Uses minimum cursor-bound for parameter {@code 'location'}, which would
186     * be zero, unless a cursor-window has been set.
187     */
188    public void moveCursorToStart()
189    { moveCursor((minCursor == -1) ? 0 : minCursor); }
190
191    /**
192     * Convenience Method.
193     * <BR />Invokes: {@link #moveCursor(int)}
194     * <BR />Uses minimum cursor-bound for parameter {@code 'location'}, which would
195     * be {@code v.size() - 1}, unless a cursor-window has been set.
196     */
197    public void moveCursorToEnd()
198    { moveCursor((maxCursor == -1) ? (v.size() - 1) : maxCursor); }
199
200    /**
201     * Sets the internal state of this {@code Iterator's} cursor location.  The next invocation of
202     * {@code next, previous}, etc... will return the nearest match to this {@code Vector}-index.
203     *
204     * <EMBED CLASS='external-html' DATA-FILE-ID=CMERESET>
205     *
206     * @param location Any index into the underlying HTML {@code Vector}
207     *
208     * @throws IndexOutOfBoundsException This shall throw if the value passed to parameter
209     * {@code 'location'} is negative, or past the end of the underlying html {@code Vector}.
210     *
211     * @throws CursorException <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
212     */
213    public void moveCursor(int location)
214    {
215        if (location < 0) throw new IndexOutOfBoundsException
216            ("You have passed a negative value to the cursor location: [" + location + "].");
217
218        if (location > v.size()) throw new IndexOutOfBoundsException(
219            "You have passed a cursor location value [" + location + "] that is larger than the " +
220            "size of the underlying Vector [" + v.size() + "]"
221        );
222
223        CursorException.check(minCursor, maxCursor, location);
224
225        cursor              = location;
226        modifiedSince       = true;
227        expectedSize        = v.size();
228
229        RESET_MATCHES();
230    }
231
232    /**
233     * Convenience Method.
234     * <BR />Accepts: {@code DotPair}
235     * <BR />Invokes: {@link #restrictCursor(int, int)}
236     */
237    public void restrictCursor(DotPair cursorBounds)
238    { restrictCursor(cursorBounds.start, cursorBounds.end); }
239
240    /**
241     * Convenience Method.
242     * <BR />Accepts: {@code LV}
243     * <BR />Invokes: {@link #restrictCursor(int, int)}
244     */
245    public void restrictCursor(LV loopVariable)
246    { restrictCursor(loopVariable.start, loopVariable.end - 1); }
247
248    /**
249     * Restrict the internal {@code cursor} boundaries to a window defined by the parameters
250     * {@code 'maxCursorBounds'}, and {@code 'minCursorBounds'}.  When the {@code cursor}
251     * bounds are restricted, any matches that would be returned by this {@code Iterator} 
252     * which lay outside the boundaries of this window shall be skipped or avoided.
253     * 
254     * <BR /><BR />If the {@code cursor} is currently located outside the boundaries of the
255     * given window, it shall be moved to within it.
256     *
257     * <EMBED CLASS='external-html' DATA-FILE-ID=CMERESET>
258     * 
259     * @param minCursorBounds This shall set a minimum {@code cursor}-index value that restricts
260     * the returned matches generated by this {@code HTML Node List Iterator} to indices less
261     * than {@code minCursorBounds}.  This is an <I><B>inclusive bound</B></I>.  Matches falling
262     * directly on this index may be included in a result set.
263     * 
264     * @param maxCursorBounds This shall set a maximum {@code cursor}-index value that restricts
265     * the returned matches generated by this {@code HTML Node List Iterator} to indices greater
266     * than {@code maxCursorBounds}.  This is <B>*also*</B> an <I><B>inclusive bound</B></I>.
267     * Matches falling directly on this index may be included in a result set.
268     *
269     * @throws IndexOutOfBoundsException If the bounds provided by the {@code 'cursorBounds'}
270     * input parameter extend past the end of the current underlying {@code Vector}, then this
271     * exception shall throw.
272     */
273    public void restrictCursor(int minCursorBounds, int maxCursorBounds)
274    {
275        if (minCursorBounds < 0) throw new IndexOutOfBoundsException
276            ("The value for parameter 'minPos' [" + minCursorBounds + "] cannot be negative");
277
278        if (maxCursorBounds >= v.size()) throw new IndexOutOfBoundsException(
279            "The value for parameter 'maxPos' [" + maxCursorBounds + "] is larger than " +
280            "(or equal to) the size of the underlying Vector [" + v.size() + "]."
281        );
282
283        this.minCursor = minCursorBounds;
284        this.maxCursor = maxCursorBounds;
285
286        if (this.cursor < this.minCursor)
287            { RESET_MATCHES();  this.cursor = this.minCursor; }
288
289        else if (this.cursor > this.maxCursor)
290            { RESET_MATCHES();  this.cursor = this.maxCursor; }
291    }
292
293    /** Eliminates the <B>Cursor Boundary Window</B>, if one had been set. */
294    public void clearCursorBounds()
295    { this.maxCursor = this.minCursor = -1; }
296
297    /**
298     * Retrieves the current location of the cursor.
299     * 
300     * @return The current location of {@code 'this' Iterator's} cursor.  If {@code hasNext()}
301     * or {@code hasPrevious()} has just been called, for example, the cursor will be pointing
302     * to the next (or previous) node-match in the HTML {@code Vector}.
303     */
304    public int cursorLocation()
305    { return cursor; }
306
307
308    // ********************************************************************************************
309    // ********************************************************************************************
310    // ADD
311    // ********************************************************************************************
312    // ********************************************************************************************
313
314
315    /**
316     * Convenience Method.
317     * <BR />Invokes: {@link #addHTMLNode(HTMLNode)}
318     */
319    public void add(E e) { addHTMLNode(e); }
320
321    /**
322     * This provides a way to <B><I>insert</I></B> an HTML node into the HTML-{@code Vector}.  The
323     * node is at the current {@code cursor}-index.  The cursor should be pointing to the location
324     * of the last returned match.
325     *
326     * @param n <EMBED CLASS='external-html' DATA-FILE-ID=HNLIN>
327     *
328     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
329     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
330     * 
331     * @throws ConcurrentModificationException
332     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
333     *
334     * @see #CHECK_EXCEPTIONS()
335     * @see #MODIFIED()
336     */
337    public void addHTMLNode(HTMLNode n)
338    {
339        CHECK_EXCEPTIONS();
340
341        v.add(cursor, n);
342
343        if (maxCursor != -1) maxCursor++;
344
345        cursor++;
346
347        MODIFIED();
348    }
349
350    /**
351     * This provides a way to <B><I>insert</I></B> {@code String}-represented HTML.  The HTML will
352     * placed in the underlying HTML-{@code Vector} directly before the current
353     * {@code cursor}-index.  The {@code cursor} is situated at the location of the last match
354     * returned by the {@code Iterator}.
355     *
356     * <EMBED CLASS='external-html' DATA-FILE-ID=HTMLSTR>
357     * 
358     * @param html This is any valid, parse-able HTML section represented as a
359     * {@code java.lang.String}.  The {@code String} will be converted to vectorized-html before
360     * insertion.
361     * 
362     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
363     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
364     * 
365     * @throws ConcurrentModificationException
366     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
367     * 
368     * @see HTMLPage#getPageTokens(CharSequence, boolean)
369     */
370    public void add(String html) { add(HTMLPage.getPageTokens(html, false)); }
371
372    /**
373     * Adds vectorized-HTML at the current {@code cursor} position.
374     * 
375     * @param html This may be any vectorized HTML page or sub-page.  It will be inserted at the
376     * the location of the last returned match.
377     * 
378     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
379     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
380     * 
381     * @throws ConcurrentModificationException
382     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
383     *
384     * @see #CHECK_EXCEPTIONS()
385     * @see #MODIFIED()
386     */
387    public void add(Vector<HTMLNode> html)
388    {
389        CHECK_EXCEPTIONS();
390
391        v.addAll(cursor, html);
392
393        cursor += html.size();
394
395        if (maxCursor != -1) maxCursor += html.size();
396    
397        MODIFIED();
398    }
399
400
401    // ********************************************************************************************
402    // ********************************************************************************************
403    // SET
404    // ********************************************************************************************
405    // ********************************************************************************************
406
407
408    /**
409     * Convenience Method.
410     * <BR />Invokes: {@link #setHTMLNode(HTMLNode)}
411     */
412    public void set(E e) { setHTMLNode(e); }
413
414    /**
415     * This provides a way to <B><I>replace</I></B> the previous match with an instance of
416     * {@code HTMLNode.}  The match that is replaced is the one that was last returned from a
417     * call to any of: {@code next(), previous(), nextIndex(), previousIndex(),} etc...
418     *
419     * @param n <EMBED CLASS='external-html' DATA-FILE-ID=HNLIN>
420     *
421     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
422     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
423     * 
424     * @throws ConcurrentModificationException
425     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
426     * 
427     * @see #remove()
428     * @see #cursor
429     * @see #expectedSize
430     * @see #maxCursor
431     */
432    public void setHTMLNode(HTMLNode n)
433    {
434        remove();
435
436        v.add(cursor, n);
437
438        cursor++;
439
440        expectedSize++;
441
442        if (maxCursor != -1) maxCursor++;
443    }
444
445    /**
446     * Convenience Method.
447     * <BR />Invokes: (Parses HTML) {@link HTMLPage#getPageTokens(CharSequence, boolean)}
448     * <BR />And-Then: {@link #set(Vector)}
449     * <BR /><BR /><B CLASS=JDDescLabel>Efficiency Warning:</B>
450     * <BR />This method will will parse (and re-parse) the HTML inside parameter {@code 'html'}
451     * everytime this method is invoked!
452     */
453    public void set(String html) { set(HTMLPage.getPageTokens(html, false)); }
454
455    /**
456     * Replaces the last returned match with the contents of parameter {@code 'html'}.
457     * 
458     * @param html This may be any-sized html page, or sub-page.
459     * 
460     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
461     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
462     * 
463     * @see Util.Remove#range(Vector, DotPair)
464     * @see #remove()
465     * @see #cursor
466     * @see #expectedSize
467     * @see #maxCursor
468     */
469    public void set(Vector<HTMLNode> html)
470    {
471        remove();
472
473        v.addAll(cursor, html);
474
475        cursor += html.size();
476
477        expectedSize += html.size();
478
479        if (maxCursor != -1) maxCursor += + html.size();
480    }
481
482
483    // ********************************************************************************************
484    // ********************************************************************************************
485    // INSERT-AT
486    // ********************************************************************************************
487    // ********************************************************************************************
488
489
490    /**
491     * Inserts parameter {@code HTMLNode n} into the underlying vectorized-html at
492     * {@code Vector}-index {@code 'pos'}.
493     *
494     * @param n <EMBED CLASS='external-html' DATA-FILE-ID=HNLIN>
495     *
496     * @param pos This is the location in the underlying {@code Vector} being iterated where the
497     * passed parameter {@code 'n'} shall be inserted.
498     *
499     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
500     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
501     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
502     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
503     * 
504     * @throws ConcurrentModificationException
505     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
506     *
507     * @see #CHECK_EXCEPTIONS(int)
508     * @see #MODIFIED()
509     */
510    public void insertAt(HTMLNode n, int pos)
511    {
512        CHECK_EXCEPTIONS(pos);
513
514        v.add(pos, n);
515
516        if (pos < cursor) cursor++;
517
518        if (maxCursor != -1) maxCursor++;
519
520        MODIFIED();
521    }
522
523    /**
524     * Convenience Method.
525     * <BR />Invokes: (Parses HTML) {@link HTMLPage#getPageTokens(CharSequence, boolean)}
526     * <BR />And-Then: {@link #insertAt(Vector, int)}.
527     * <BR /><BR /><B CLASS=JDDescLabel>Efficiency Warning:</B>
528     * <BR />This method will will parse (and re-parse) the HTML inside parameter {@code 'html'}
529     * everytime this method is invoked!
530     */
531    public void insertAt(String html, int pos)
532    { insertAt(HTMLPage.getPageTokens(html, false), pos); }
533
534    /**
535     * Insert the contents of vectorized-html parameter {@code 'html'} into the underlying html
536     * {@code Vector}, beginning at position {@code 'pos'}.
537     *
538     * @param html This may be any HTML page or sub-page.
539     *
540     * @param pos This is the location in the underlying {@code Vector} being iterated where the
541     * passed parameter {@code 'html'} shall be inserted.
542     *
543     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
544     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
545     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
546     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
547     *
548     * @throws ConcurrentModificationException
549     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
550     * 
551     * @see #CHECK_EXCEPTIONS(int)
552     * @see #MODIFIED()
553     */
554    public void insertAt(Vector<? extends HTMLNode> html, int pos)
555    {
556        CHECK_EXCEPTIONS(pos);
557
558        v.addAll(pos, html);
559
560        if (pos < cursor) cursor += html.size();
561
562        if (maxCursor != -1) maxCursor += html.size();
563
564        MODIFIED();
565    }
566
567
568    // ********************************************************************************************
569    // ********************************************************************************************
570    // REPLACE
571    // ********************************************************************************************
572    // ********************************************************************************************
573
574
575    /**
576     * Convenience Method.
577     * <BR />Invokes: {@link #replaceRange(DotPair, Vector)}
578     * <BR /><BR /><B CLASS=JDDescLabel>Off-By-One Mistake:</B>
579     * <BR />{@code ePos} is decremented by 1, since {@code DotPair.end} is always an
580     * <I>inclusive</I> value, while {@code ePos} is always <I>exclusive</I>.
581     */
582    public void replaceRange(int sPos, int ePos, Vector<HTMLNode> newNodes)
583    { replaceRange(new DotPair(sPos, ePos - 1), newNodes); }
584
585    /**
586     * This replaces a specified sub-range of nodes in the underlying vectorized-html with the
587     * nodes in {@code 'newNodes'}.
588     *
589     * @param range This is the sub-range or "sub-section" of the underlying vectorized-html page
590     * that is going to be replaced by the {@code 'newNodes'}.
591     * 
592     * @param newNodes These are the nodes to be inserted into the location where the old range is.
593     *
594     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
595     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
596     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
597     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
598     *
599     * @throws ConcurrentModificationException
600     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
601     * 
602     * @see Util.Remove#range(Vector, DotPair)
603     * @see DotPair#isInside(int)
604     * @see DotPair#size()
605     * @see #CHECK_EXCEPTIONS(DotPair)
606     * @see #MODIFIED()
607     */
608    public void replaceRange(DotPair range, Vector<HTMLNode> newNodes)
609    {
610        CHECK_EXCEPTIONS(range);
611
612        Util.replaceRange(v, range, newNodes);
613
614        int sizeChange = newNodes.size() - range.size();
615
616        if (range.isInside(cursor))     cursor = range.start;
617        else if(cursor > range.end)     cursor += sizeChange;
618
619        if (maxCursor != -1) maxCursor += sizeChange;
620    
621        MODIFIED();
622    }
623
624
625    // ********************************************************************************************
626    // ********************************************************************************************
627    // REMOVE
628    // ********************************************************************************************
629    // ********************************************************************************************
630
631
632    /**
633     * This removes the node at a specified index in the underlying vectorized-html.
634     *
635     * @param pos This is the index into the underlying vectorized-html page to be removed.
636     *
637     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
638     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
639     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
640     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
641     *
642     * @throws ConcurrentModificationException
643     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
644     * 
645     * @see #CHECK_EXCEPTIONS(int)
646     * @see #MODIFIED()
647     */
648    public void removeElementAt(int pos)
649    {
650        CHECK_EXCEPTIONS(pos);
651
652        v.removeElementAt(pos);
653
654        if (pos < cursor) cursor--;
655
656        if (maxCursor != -1) maxCursor--;           
657
658        MODIFIED();
659    }
660
661    /**
662     * Convenience Method.
663     * <BR />Invokes: {@link #removeRange(int, int)}
664     * <BR /><BR /><B CLASS=JDDescLabel>Off-By-One Mistake:</B>
665     * <BR />{@code ePos} is decremented by 1, since {@code DotPair.end} is always an
666     * <I>inclusive</I> value, while {@code ePos} is always <I>exclusive</I>.
667     */
668    public void removeRange(int sPos, int ePos)
669    { removeRange(new DotPair(sPos, ePos-1)); }
670
671    /**
672     * This removes a specified sub-range of nodes in the underlying vectorized-html.
673     *
674     * @param range This is the sub-range or "sub-section" of the underlying vectorized-html page;
675     *
676     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
677     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
678     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
679     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
680     *
681     * @throws ConcurrentModificationException
682     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
683     * 
684     * @see Util.Remove.range(Vector, DotPair)
685     * @see DotPair#isInside(int)
686     * @see DotPair#size()
687     * @see #CHECK_EXCEPTIONS(DotPair)
688     * @see #MODIFIED()
689     */
690    public void removeRange(DotPair range)
691    {
692        CHECK_EXCEPTIONS(range);
693
694        Util.Remove.range(v, range);
695
696        if (range.isInside(cursor))     cursor = range.start;
697        else if(cursor > range.end)     cursor -= range.size();
698
699        if (maxCursor != -1) maxCursor -= range.size();
700    
701        MODIFIED();
702    }
703
704    /**
705     * This will remove every {@code Vector}-element that is identified by the {@code 'posArr'}
706     * {@code Vector}-position {@code int[]} array.
707     *
708     * @param posArr This is a list of nodes that shall be removed from the underlying
709     * html-{@code Vector}.
710     *
711     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
712     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
713     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
714     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
715     *
716     * @throws ConcurrentModificationException
717     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
718     * 
719     * @see Util.Remove.nodes(boolean, Vector, int[])
720     * @see #CHECK_EXCEPTIONS(int[])
721     * @see #MODIFIED()
722     */
723    public void removeElements(int... posArr)
724    {
725        CHECK_EXCEPTIONS(posArr);
726
727        Util.Remove.nodes(false, v, posArr); // false --> sorts the input int[] array
728
729        int ORIGINAL_CURSOR = cursor;
730
731        for (int pos : posArr)
732
733            if (pos < ORIGINAL_CURSOR) cursor--;
734            else break;
735
736        if (maxCursor != -1) maxCursor -= posArr.length;
737
738        MODIFIED();
739    }
740
741
742    // ********************************************************************************************
743    // ********************************************************************************************
744    // Exception Checking
745    // ********************************************************************************************
746    // ********************************************************************************************
747
748
749    /**
750     * Checks the input position {@code 'pos'} parameter to verify it is not out of the
751     * bounds of the underlying HTML-{@code Vector}, nor out of the bounds of the {@code Cursor
752     * Boundary Window} - <I>if one has been set.</I>
753     *
754     * <BR /><BR />This shall throw exceptions if the passed {@code 'range'} is out of either of
755     * these bounds.
756     * 
757     * <BR /><BR />Upon completion of this test, the rest of the standard Exception check-throws
758     * are performed.
759     *
760     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
761     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
762     * 
763     * @see #CHECK_EXCEPTIONS()
764     */
765    protected void CHECK_EXCEPTIONS(int pos)
766    {
767        IteratorOutOfBoundsException.check(v, pos);
768        CursorException.check(minCursor, maxCursor, pos);
769
770        CHECK_EXCEPTIONS();
771    }
772
773    /**
774     * Checks the input position {@code 'range'} parameter to verify the provided {@code range}
775     * values are not out of the bounds of the underlying HTML-{@code Vector}, nor out of the 
776     * bounds of the {@code Cursor Boundary Window} - <I>if one has been set.</I>
777     *
778     * <BR /><BR />This shall throw exceptions if the passed {@code 'range'} is out of either of
779     * these bounds.
780     * 
781     * <BR /><BR />Upon completion of this test, the rest of the standard Exception check-throws
782     * are performed.
783     *
784     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
785     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
786     * 
787     * @see #CHECK_EXCEPTIONS()
788     */
789    protected void CHECK_EXCEPTIONS(DotPair range)
790    {
791        IteratorOutOfBoundsException.check(v, range);
792        CursorException.check(minCursor, maxCursor, range);
793
794        CHECK_EXCEPTIONS();
795    }
796
797    /**
798     * Checks the input position-array {@code 'posArr'} parameter to verify that none of the
799     * indices listed in this position-array are out of the bounds of the underlying 
800     * HTML-{@code Vector}, nor out of the bounds of the {@code Cursor Boundary Window} - <I>if
801     * one has been set.</I>
802     *
803     * <BR /><BR />This shall throw exceptions if the indices in this input array extend past
804     * either of these boundaries.
805     * 
806     * <BR /><BR />Upon completion of this test, the rest of the standard Exception check-throws
807     * are performed.
808     *
809     * @throws IteratorOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=IOOB_EX>
810     * @throws CursorException              <EMBED CLASS='external-html' DATA-FILE-ID=CURSOR_EX>
811     * 
812     * @see #CHECK_EXCEPTIONS()
813     */
814    protected void CHECK_EXCEPTIONS(int[] posArr)
815    {
816        IteratorOutOfBoundsException.check(v, posArr);
817        CursorException.check(minCursor, maxCursor, posArr);
818
819        CHECK_EXCEPTIONS();
820    }
821
822    /**
823     * Does a check regarding whether any exceptions should throw.
824     * 
825     * @throws IllegalStateException        <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_ILST_EX>
826     * @throws SecondModificationException  <EMBED CLASS='external-html' DATA-FILE-ID=SEC_MOD_EX>
827     * 
828     * @throws ConcurrentModificationException
829     * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX>
830     * 
831     * @see #CHECK_CME()
832     */
833    protected void CHECK_EXCEPTIONS()
834    {
835        CHECK_CME();
836
837        if (cursor == -1) throw new IllegalStateException(
838            "Neither next, nor previous have been called since initializing the iterator with a " +
839            "constructor."
840        );
841
842        if (modifiedSince) throw new SecondModificationException(
843            "One of the remove, add, or set methods - which are this Iterator's Update (Modifier) " +
844            "Operations - has already been called since the prior call to a next, previous, first, " +
845            "or last (Inspection) method.  The Modifier and Inspection methods / API of this " + 
846            "Iterator may only be used ONCE PER CALL (or, rather, on A ONE-TO-ONE BASIS) with " + 
847            "each-other.  In other words, after an invocation of an 'Inspection Method' (such as " +
848            "'next'), only one invocation of a 'Modifier Method' (such as 'set') will be allowed " +
849            "until another inspection is invoked."
850        );
851    }
852
853    /**
854     * Does a check regarding whether an exception should throw.
855     * 
856     * @throws ConcurrentModificationException Checks for, and throws if necessary, Java's
857     * {@code ConcurrentModificationException}.
858     */
859    protected void CHECK_CME()  // Because HNLI<E> is an interface, this is actually 'protected'
860    {
861        if (expectedSize != v.size()) throw new ConcurrentModificationException(
862            "The expected size of the underlying vector was: [" + expectedSize + "], but the " +
863            "encountered size was: [" + v.size() + "].  This implies that the Vector was modified " +
864            "outside of this HNLI Iterator's provided update & modify API.  This is not allowed, " +
865            "unless followed by a call to: first, last, firstIndex, or lastIndex - all of which " +
866            "'reset' the outside-modification monitor-logic (as do the cursor-movement and cursor " +
867            "bounds methods)."
868        );
869    }
870}