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 * A Java Generic {@code Iterator}-Class that for iterating {@link TagNode}, {@link TextNode} and 013 * {@code CommentNode} instances which match user-provided search-criteria. 014 * 015 * <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_EXTENDS_LITER> 016 * <EMBED CLASS='external-html' DATA-FILE-ID=HNLI_EASY_TO_USE> 017 * 018 * @param <E> The type of {@code HTMLNode} being iterated. 019 */ 020@SuppressWarnings("unchecked") 021@Torello.JavaDoc.JDHeaderBackgroundImg 022public class HNLI<E extends HTMLNode> extends AbstractHNLI<E, E> 023{ 024 // ******************************************************************************************** 025 // ******************************************************************************************** 026 // Private Fields 027 // ******************************************************************************************** 028 // ******************************************************************************************** 029 030 031 // -1 means a "Right-Direction Match" has not been found/identified yet. 032 private int hasNextVectorPos = -1; 033 034 // -1 means a "Left-Direction Match" has not been found/identified yet. 035 private int hasPrevVectorPos = -1; 036 037 038 // ******************************************************************************************** 039 // ******************************************************************************************** 040 // Only Constructor **AND** Package-Private Abstract-Method Implementations 041 // ******************************************************************************************** 042 // ******************************************************************************************** 043 044 045 /** 046 * This will produce an {@code Iterator<E>}. The last parameter to this constructor 047 * {@code Class<E> c} is required since, as per Java's Erasure "Feature," there is no way 048 * to identify what the Variable-Type Parameter {@code 'E'} evaluates at Run-Time. 049 * 050 * @param html This may be any HTML {@code Vector} or sub-section. 051 * 052 * @param p This is a {@code java.util.function.Predicate} that identifies when the 053 * {@code Iterator} should consider an instance of {@code 'E'} to be a "Match." 054 * 055 * @param c This parameter should just be the value of a call to {@code 'E'.getClass()} where 056 * {@code 'E'} is the instance of the variable-type parameter {@code 'E'} used in this method. 057 */ 058 HNLI (Vector<? extends HTMLNode> html, Predicate<E> p, Class<E> c) 059 { super(html, p, c); } 060 061 void RESET_MATCHES() { hasNextVectorPos = hasPrevVectorPos = -1; } 062 063 int REMOVE() { v.remove(cursor); return 1; } 064 065 066 // ******************************************************************************************** 067 // ******************************************************************************************** 068 // "Previous" - Retrieval Operations 069 // ******************************************************************************************** 070 // ******************************************************************************************** 071 072 073 /** 074 * Use this method to find out whether the underlying {@code Vector} and current {@code cursor} 075 * position would retrieve another match if {@code 'previous()'} or {@code 'previousIndex()'} 076 * were called. 077 * 078 * @return This shall return {@code TRUE} if calling the {@code previous()}, or 079 * {@code previousIndex()} methods would return another node-match. This method shall return 080 * {@code FALSE} if calling {@code previous} would generate / throw a 081 * {@code 'NoSuchElementException'} - <I>because there are no more matches in the underlying 082 * {@code Vector}, given the current {@code cursor} position.</I> 083 * 084 * @throws ConcurrentModificationException 085 * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX> 086 */ 087 public boolean hasPrevious() 088 { 089 CHECK_CME(); 090 091 if (hasPrevVectorPos != -1) return true; 092 093 Object o; // Temp Object 094 095 int LOOP_BOUNDARY = (minCursor == -1) ? 0 : minCursor; 096 097 if (cursor == -1) cursor = LOOP_BOUNDARY; // will return false 098 099 // System.out.println("Loop Boundary: " + LOOP_BOUNDARY + ", cursor: " + cursor); 100 101 while (--cursor >= LOOP_BOUNDARY) 102 103 if (c.isInstance(o = v.elementAt(cursor)) && p.test(c.cast(o))) 104 { 105 hasPrevVectorPos = cursor; 106 return true; 107 } 108 109 return false; 110 } 111 112 /** 113 * Returns the nearest node-match in the underlying {@code Vector}, given the current 114 * {@code cursor} position - <I>when searching in the left-direction, or in the direction of 115 * decreasing {@code Vector}-indices.</I> 116 * 117 * @return This shall return the node-match that is directly previous to the current 118 * {@code cursor} position. 119 * 120 * @throws ConcurrentModificationException 121 * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX> 122 * 123 * @throws NoSuchElementException If there aren't any more matches available, this exception 124 * shall throw. Avoid having to catch this exception by always calling method 125 * {@code 'hasPrevious'}, and only invoking {@code 'previous'} if that method returned 126 * {@code TRUE}. 127 */ 128 public E previous() 129 { return (E) v.elementAt(previousIndex()); } 130 131 /** 132 * Returns the nearest node-match, <I><B>as an integer {@code Vector}-index</I></B>, in the 133 * underlying {@code Vector}, given the current {@code cursor} position - <I>when searching in 134 * the left-direction, or in the direction of decreasing {@code Vector}-indices.</I> 135 * 136 * @return This shall return the node-match that is directly previous to the current 137 * {@code cursor} position. 138 * 139 * @throws ConcurrentModificationException 140 * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX> 141 * 142 * @throws NoSuchElementException If there aren't any more matches available, this exception 143 * shall throw. Avoid having to catch this exception by always calling method 144 * {@code 'hasPrevious()'}, and only invoking {@code 'previousIndex()'} if that method 145 * returned <B>TRUE.</B> 146 */ 147 public int previousIndex() 148 { 149 CHECK_CME(); 150 151 int temp = hasPrevVectorPos; 152 153 hasPrevVectorPos = hasNextVectorPos = -1; 154 modifiedSince = false; 155 156 if (temp != -1) return temp; 157 158 Object o; // Temp Object 159 160 int LOOP_BOUNDARY = (minCursor == -1) ? 0 : minCursor; 161 162 if (cursor == -1) cursor = LOOP_BOUNDARY; // will throw exception 163 164 while (--cursor >= LOOP_BOUNDARY) 165 166 if (c.isInstance(o = v.elementAt(cursor)) && p.test(c.cast(o))) 167 return cursor; 168 169 throw new NoSuchElementException("There are no more 'previous' elements available."); 170 } 171 172 173 // ******************************************************************************************** 174 // ******************************************************************************************** 175 // "Next" - Retrieval Operations 176 // ******************************************************************************************** 177 // ******************************************************************************************** 178 179 180 /** 181 * Use this method to find out whether the underlying {@code Vector} and current {@code cursor} 182 * position would retrieve another match if {@code 'next()'} or {@code 'nextIndex()'} were 183 * called. 184 * 185 * @return This shall return {@code TRUE} if calling the {@code next()}, or {@code nextIndex()} 186 * methods would return another node-match. This method shall return {@code FALSE} if calling 187 * {@code next()} would generate / throw a {@code 'NoSuchElementException'} - <I>because there 188 * are no more matches in the underlying {@code Vector}, given the current {@code cursor} 189 * position.</I> 190 * 191 * @throws ConcurrentModificationException 192 * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX> 193 */ 194 public boolean hasNext() 195 { 196 CHECK_CME(); 197 198 if (hasNextVectorPos != -1) return true; 199 200 Object o; // Temp Object 201 202 int LOOP_BOUNDARY = (maxCursor == -1) ? (v.size() - 1) : maxCursor; 203 204 if (cursor == -1) cursor = (minCursor == -1) ? -1 : (minCursor-1); 205 206 // System.out.println("Loop Boundary: " + LOOP_BOUNDARY + ", cursor: " + cursor); 207 208 while (++cursor <= LOOP_BOUNDARY) 209 210 if (c.isInstance(o = v.elementAt(cursor)) && p.test(c.cast(o))) 211 { hasNextVectorPos=cursor; return true; } 212 213 return false; 214 } 215 216 /** 217 * Returns the nearest node-match in the underlying {@code Vector}, given the current 218 * {@code cursor} position - <I>when searching in the right-direction, or in the direction of 219 * increasing {@code Vector}-indices.</I> 220 * 221 * @return This shall return the node-match that is directly next to the current 222 * {@code cursor} position. 223 * 224 * @throws ConcurrentModificationException 225 * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX> 226 * 227 * @throws NoSuchElementException If there aren't any more matches available, this exception 228 * shall throw. Avoid having to catch this exception by always calling method 229 * {@code 'hasNext()'}, and only invoking {@code 'next()'} if that method returned {@code TRUE}. 230 */ 231 public E next() 232 { return (E) v.elementAt(nextIndex()); } 233 234 /** 235 * Returns the nearest node-match, <I><B>as an integer {@code Vector}-index</I></B>, in the 236 * underlying {@code Vector}, given the current {@code cursor} position - <I>when searching in 237 * the right-direction, or in the direction of increasing {@code Vector}-indices.</I> 238 * 239 * @return This shall return the node-match that is directly next to the current {@code cursor} 240 * position. 241 * 242 * @throws ConcurrentModificationException 243 * <EMBED CLASS='external-html' DATA-FILE-ID=CONC_MOD_EX> 244 * 245 * @throws NoSuchElementException If there aren't any more matches available, this exception 246 * shall throw. Avoid having to catch this exception by always calling method 247 * {@code 'hasNext()'}, and only invoking {@code 'nextIndex()'} if that method returned 248 * <B>TRUE.</B> 249 */ 250 public int nextIndex() 251 { 252 CHECK_CME(); 253 254 int temp = hasNextVectorPos; 255 256 hasPrevVectorPos = hasNextVectorPos = -1; 257 modifiedSince = false; 258 259 if (temp != -1) return temp; 260 261 Object o; // Temp Object 262 263 int LOOP_BOUNDARY = (maxCursor == -1) ? (v.size() - 1) : maxCursor; 264 265 if (cursor == -1) cursor = (minCursor == -1) ? -1 : (minCursor-1); 266 267 while (++cursor <= LOOP_BOUNDARY) 268 269 if (c.isInstance(o = v.elementAt(cursor)) && p.test(c.cast(o))) 270 return cursor; 271 272 throw new NoSuchElementException("There are no more 'next' elements available."); 273 } 274 275 276 // ******************************************************************************************** 277 // ******************************************************************************************** 278 // "First" and "Last" - Retrieval Operations 279 // ******************************************************************************************** 280 // ******************************************************************************************** 281 282 283 /** 284 * Convenience Method. 285 * <BR />Invokes: {@link #firstIndex()} 286 * <BR />Retrieves: {@code 'E'} node-instance from the {@code Vector} 287 */ 288 public E first() 289 { return (E) v.elementAt(firstIndex()); } 290 291 /** 292 * This method will "reset the internal {@code cursor}" to the beginning, and return the index 293 * of the first node-match (rather than the node itself). 294 * 295 * <EMBED CLASS='external-html' DATA-FILE-ID=CMERESET> 296 * 297 * @return The index of the very-first node-match in this list. As with other 298 * {@code Iterator}-retrieval methods, if the underlying {@code Vector} has been changed using 299 * calls to: {@code set, remove,} or {@code add}, then this method will return the 300 * first-integer index of the node-match for the modified-{@code Vector}. 301 */ 302 public int firstIndex() 303 { 304 cursor = (minCursor == -1) ? 0 : minCursor; 305 hasNextVectorPos = hasPrevVectorPos = -1; 306 expectedSize = v.size(); 307 308 // NOTE: A call to first, last, firstIndex, or lastIndex 309 // "resets" the CME Monitor-Logic ==> expectedSize = v.size(); 310 311 return nextIndex(); 312 } 313 314 /** 315 * Convenience Method. 316 * <BR />Invokes: {@link #lastIndex()} 317 * <BR />Retrieves: {@code 'E'} node-instance from the {@code Vector} 318 */ 319 public E last() 320 { return (E) v.elementAt(lastIndex()); } 321 322 /** 323 * This method will "advance the internal {@code cursor}" to the end of the {@code Vector}, and 324 * return the index of the last node-match (rather than the node itself). 325 * 326 * <EMBED CLASS='external-html' DATA-FILE-ID=CMERESET> 327 * 328 * @return The index of the very-last node-match in this list. As with other 329 * {@code Iterator}-retrieval methods, if the underlying {@code Vector} has been changed using 330 * calls to: {@code set, remove,} or {@code add}, then this method will return the 331 * last-integer index of the node-match for the modified-{@code Vector}. 332 */ 333 public int lastIndex() 334 { 335 cursor = (maxCursor == -1) ? (v.size() - 1) : maxCursor; 336 hasNextVectorPos = hasPrevVectorPos = -1; 337 expectedSize = v.size(); 338 339 // NOTE: A call to first, last, firstIndex, or lastIndex 340 // "resets" the CME Monitor-Logic ==> expectedSize = v.size(); 341 342 return previousIndex(); 343 } 344}