001package Torello.Java; 002 003import java.util.*; 004import Torello.HTML.*; 005 006/** 007 * The Loop-Variable End-Points class is used extensively throughout the Java-HTML Library for 008 * throwing properly formatted exception messages <I>vis-a-vis</I> loop variables. 009 * 010 * <EMBED CLASS='external-html' DATA-FILE-ID=LV> 011 */ 012public class LV implements java.io.Serializable, Cloneable 013{ 014 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */ 015 public static final long serialVersionUID = 1; 016 017 /** 018 * This integer represents the starting point of a {@code for-loop}. It is guaranteed to be 019 * consistent with the {@code Vector} that was used with the constructor of this class. 020 */ 021 public final int start; 022 023 /** 024 * This integer represents the ending point of a {@code for-loop}. It is guaranteed to be 025 * consistent with the {@code Vector} that was used with the constructor of this class. 026 */ 027 public final int end; 028 029 030 // ******************************************************************************************** 031 // Standard Java Methods 032 // ******************************************************************************************** 033 034 035 /** 036 * Implements the standard java {@code 'hashCode()'} method. This will provide a hash-code 037 * that is very likely to avoid crashes. 038 * 039 * @return A hash-code that may be used for inserting {@code 'this'} instance into a hashed 040 * table, map or list. 041 */ 042 public int hashCode() 043 { return this.start + (1000 * this.end); } 044 045 /** 046 * Java's {@code toString()} requirement. 047 * @return A string representing 'this' instance of LV / Loop-Variables. 048 */ 049 public String toString() { return "[Loop-Start: " + start + ", Loop-Break: " + end + "]"; } 050 051 /** 052 * Java's {@code public boolean equals(Object o)} requirements. 053 * 054 * @param o This may be any Java Object, but only ones of {@code 'this'} type whose 055 * internal-values are identical with {@code 'this'} instance will make this method return 056 * {@code TRUE}. 057 * 058 * @return {@code TRUE} if (and only if) parameter {@code 'o'} is an {@code instanceof LV} and, 059 * also, has equal {@code 'start'} and {@code 'end'} field values. 060 */ 061 public boolean equals(Object o) 062 { 063 if (o instanceof LV) 064 { 065 LV dp = (LV) o; 066 return (this.start == dp.start) && (this.end == dp.end); 067 } 068 069 else return false; 070 } 071 072 /** 073 * Java's {@code interface Cloneable} requirements. This instantiates a new {@code LV} with 074 * identical {@code 'start', 'end'} fields. 075 * 076 * @return A new {@code LV} instance whose internal fields are identical to this one. 077 */ 078 public LV clone() { return new LV(this.start, this.end); } 079 080 /** 081 * Returns the number of elements that would be iterated, if using {@code 'this'} instance of 082 * {@code LV} as a loop-control variable. 083 * 084 * @return The number of element's that are referenced by {@code 'this'} instance. 085 * 086 * @see #start 087 * @see #end 088 */ 089 public int size() { return end - start; } 090 091 // ******************************************************************************************** 092 // ******************************************************************************************** 093 // Internal Helper Methods 094 // ******************************************************************************************** 095 // ******************************************************************************************** 096 097 098 // Private "Clone Constructor" 099 private LV(int start, int end) { this.start=start; this.end=end; } 100 101 private String NOTEV (int size, int sPos, int ePos) 102 { 103 return 104 "Vector.size(): [" + size + "], " + 105 "Start-Position: [" + sPos + "], " + 106 "End-Position: [" + ePos + ']'; 107 } 108 109 private String NOTESTR(int length, int sPos, int ePos) 110 { 111 return 112 "String.length(): [" + length + "], " + 113 "sPos: [" + sPos + "], " + 114 "ePos: [" + ePos + ']'; 115 } 116 117 private String NOTESTR(int length, int sPos, int ePos, int cmprStrLen) 118 { 119 return 120 "String.length(): [" + length + "], " + 121 "sPos: [" + sPos + "], " + 122 "ePos: [" + ePos + "], " + 123 "cmprStrLen: [" + cmprStrLen + ']'; 124 } 125 126 private String NOTEA(int length, int sPos, int ePos) 127 { 128 return 129 "Array.length: [" + length + "], " + 130 "Start-Position: [" + sPos + "], " + 131 "End-Position: [" + ePos + ']'; 132 } 133 134 135 // ******************************************************************************************** 136 // ******************************************************************************************** 137 // Constructors 138 // ******************************************************************************************** 139 // ******************************************************************************************** 140 141 142 /** 143 * Checks input parameters and either throws {@code IndexOutOfBoundsException} or returns 144 * proper loop-variable starting & ending values. 145 * 146 * <EMBED CLASS='external-html' DATA-FILE-ID=LV_IMPT_NOTE> 147 * 148 * @param html This is any vectorized-html page {@code Vector}. 149 * 150 * @param sPos This is the starting position in the {@code Vector} for the loop-variable 151 * counter being created. This value is <B>inclusive</B>. This means that the first element 152 * searched by {@code 'this'} loop-variable counter instance <I>shall be at the index 153 * {@code 'sPos'}</I>. 154 * 155 * <BR /><BR />If all validity checks are passed, {@code 'this.start'} is assigned the value of 156 * {@code 'sPos'}. 157 * 158 * @param ePos This is the ending position in the {@code Vector} for the loop-variable counter 159 * being created. This value is <B>exclusive</B>. This means that the last element searched 160 * by {@code 'this'} loop-variable counter instance <I>shall be at the index 161 * {@code ePos - 1}</I>. 162 * 163 * <BR /><BR />A negative value may be passed to {@code 'ePos'} - and if so, {@code this.end} 164 * shall be set to {@code html.size()}, otherwise {@code this.end} is assigned the value of 165 * {@code 'ePos'}. 166 * 167 * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX> 168 */ 169 public LV(Vector<? extends HTMLNode> html, int sPos, int ePos) 170 { 171 int size = html.size(); 172 173 if ((size == 0) && (sPos == 0) && (ePos <= 0)) 174 { this.start = this.end = 0; return; } 175 176 if (sPos >= size) throw new IndexOutOfBoundsException( 177 "Starting Vector Position is greater than or equal to the Vector's size:\n" + 178 NOTEV(size, sPos, ePos) 179 ); 180 181 if (sPos < 0) throw new IndexOutOfBoundsException 182 ("Starting Vector Position is negative: " + NOTEV(size, sPos, ePos)); 183 184 if (ePos > size) throw new IndexOutOfBoundsException( 185 "Ending Vector Position is greater than the size of the Vector:\n" + 186 NOTEV(size, sPos, ePos) 187 ); 188 189 if (ePos == 0) throw new IndexOutOfBoundsException 190 ("Ending Vector Position is zero.:\n" + NOTEV(size, sPos, ePos)); 191 192 this.start = sPos; 193 this.end = (ePos <= 0) ? size : ePos; 194 195 if (start > end) throw new IllegalArgumentException( 196 "The starting and ending Vector Positions are not properly chosen:\n" + 197 NOTEV(size, sPos, ePos) 198 ); 199 } 200 201 202 /** 203 * Explaining the issue of type-checking with java-generics, once a certain point has been 204 * reached, is an exercise in futility. The JDK development team did a lot of work on Java 205 * Generics, but didn't not bring them into the "Run-Time" world. As such, there are a few, 206 * details, as we shall call them with names like "CAP#1" that prevent some perfectly 207 * reasonable looking code structures that simply will not compile. 208 * 209 * <BR /><BR />This constructor is identical to the other constructor in this class, but has 210 * had its parameter position inputs reversed in the method signature. Also, <I><B>it accepts 211 * a raw-type {@code Vector} instance.</I></B> This should not present a problem to users at 212 * all, but to the developer of this project / package, it can be disconcerting. In any case, 213 * this constructor checks the input parameters and either throws 214 * {@code IndexOutOfBoundsException} or returns a proper loop-variable starting-ending point 215 * class-object. 216 * 217 * <EMBED CLASS='external-html' DATA-FILE-ID=LV_IMPT_NOTE> 218 * 219 * @param v This may be any {@code raw-type Vector}. 220 * <EMBED CLASS='external-html' DATA-FILE-ID=RAWTYPES> 221 * 222 * @param sPos This is the starting position in the {@code Vector} for the loop-variable 223 * counter being created. This value is <B>inclusive</B>. This means that the first element 224 * searched by {@code 'this'} loop-variable counter instance <I>shall be at the index 225 * {@code 'sPos'}</I>. 226 * 227 * <BR /><BR />If all validity checks are passed, {@code 'this.start'} is assigned the value of 228 * {@code 'sPos'}. 229 * 230 * @param ePos This is the ending position in the {@code Vector} for the loop-variable counter 231 * being created. This value is <B>exclusive</B>. This means that the last element searched 232 * by {@code 'this'} loop-variable counter instance <I>shall be at the index 233 * {@code ePos - 1}</I>. 234 * 235 * <BR /><BR />A negative value may be passed to {@code 'ePos'} - and if so, {@code this.end} 236 * shall be set to {@code html.size()}, otherwise {@code this.end} is assigned the value of 237 * {@code 'ePos'}. 238 * 239 * @throws IndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=VIOOBEX> 240 */ 241 public LV(int sPos, int ePos, Vector<?> v) 242 { 243 int size = v.size(); 244 245 if ((size == 0) && (sPos == 0) && (ePos <= 0)) 246 { this.start = this.end = 0; return; } 247 248 if (sPos >= size) throw new IndexOutOfBoundsException( 249 "Starting Vector Position is greater than or equal to the Vector's size:\n" + 250 NOTEV(size, sPos, ePos) 251 ); 252 253 if (sPos < 0) throw new IndexOutOfBoundsException 254 ("Starting Vector Position is negative: " + NOTEV(size, sPos, ePos)); 255 256 if (ePos > size) throw new IndexOutOfBoundsException( 257 "Ending Vector Position is greater than the size of the Vector:\n" + 258 NOTEV(size, sPos, ePos) 259 ); 260 261 if (ePos == 0) throw new IndexOutOfBoundsException 262 ("Ending Vector Position is zero.:\n" + NOTEV(size, sPos, ePos)); 263 264 this.start = sPos; 265 this.end = (ePos <= 0) ? size : ePos; 266 267 if (start > end) throw new IllegalArgumentException( 268 "The starting and ending Vector Positions are not properly chosen:\n" + 269 NOTEV(size, sPos, ePos) 270 ); 271 } 272 273 /** 274 * Checks input parameters and either throws {@code StringIndexOutOfBoundsException} or returns 275 * proper loop-variable starting & ending values. 276 * 277 * <EMBED CLASS='external-html' DATA-FILE-ID=LV_IMPT_NOTE> 278 * 279 * @param s This may be any {@code String}. 280 * 281 * @param sPos This is the starting position in the {@code String} for the loop-variable 282 * counter being created. This value is <B>inclusive</B>. This means that the first element 283 * searched by {@code 'this'} loop-variable counter instance <I>shall be at the index 284 * {@code 'sPos'}</I>. 285 * 286 * <BR /><BR />If all validity checks are passed, {@code 'this.start'} is assigned the value of 287 * {@code 'sPos'}. 288 * 289 * @param ePos This is the ending position in the {@code String} for the loop-variable counter 290 * being created. This value is <B>exclusive</B>. This means that the last element searched 291 * by {@code 'this'} loop-variable counter instance <I>shall be at the index 292 * {@code ePos - 1}</I>. 293 * 294 * <BR /><BR />A negative value may be passed to {@code 'ePos'} - and if so, {@code this.end} 295 * shall be set to {@code s.size()}, otherwise {@code this.end} is assigned the value of 296 * {@code 'ePos'}. 297 * 298 * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX> 299 */ 300 public LV(String s, int sPos, int ePos) 301 { 302 int length = s.length(); 303 304 if ((length == 0) && (sPos == 0) && (ePos <= 0)) 305 { this.start = this.end = 0; return; } 306 307 if (sPos >= length) throw new StringIndexOutOfBoundsException( 308 "Starting String Position is greater than or equal to the String's length:\n" + 309 NOTESTR(length, sPos, ePos) 310 ); 311 312 if (sPos < 0) throw new StringIndexOutOfBoundsException 313 ("Starting String Position is negative:\n" + NOTESTR(length, sPos, ePos)); 314 315 if (ePos > length) throw new StringIndexOutOfBoundsException( 316 "Ending String Position is greater than the length of the String:\n" + 317 NOTESTR(length, sPos, ePos) 318 ); 319 320 if (ePos == 0) throw new StringIndexOutOfBoundsException 321 ("Ending String Position is zero:\n" + NOTESTR(length, sPos, ePos)); 322 323 this.start = sPos; 324 this.end = (ePos <= 0) ? length : ePos; 325 326 if (start > end) throw new IllegalArgumentException( 327 "The starting and ending String positions are not properly chosen:\n" + 328 NOTESTR(length, sPos, ePos) 329 ); 330 } 331 332 /** 333 * Checks input parameters and either throws {@code StringIndexOutOfBoundsException} or returns 334 * proper loop-variable starting & ending values. In this constructor, the length of a 335 * second, comparing-{@code String}, substring is expected as a parameter. This version of the 336 * {@code LV} constructor is used by {@code class StrIndexOf}. 337 * 338 * <EMBED CLASS='external-html' DATA-FILE-ID=LV_IMPT_NOTE> 339 * 340 * @param s This may be any {@code String}. 341 * 342 * @param sPos This is the starting position in the {@code String} for the loop-variable 343 * counter being created. This value is <B>inclusive</B>. This means that the first element 344 * searched by {@code 'this'} loop-variable counter instance <I>shall be at the index 345 * {@code 'sPos'}</I>. 346 * 347 * <BR /><BR />If all validity checks are passed, {@code 'this.start'} is assigned the value of 348 * {@code 'sPos'}. 349 * 350 * @param ePos This is the ending position in the {@code String} for the loop-variable counter 351 * being created. This value is <B>exclusive</B>. This means that the last element searched 352 * by {@code 'this'} loop-variable counter instance <I>shall be at the index 353 * {@code ePos - 1}</I>. 354 * 355 * <BR /><BR />A negative value may be passed to {@code 'ePos'} - and if so, {@code this.end} 356 * shall be set to {@code s.length() - cmprStrLen + 1}, otherwise {@code this.end} is assigned 357 * the value of {@code 'ePos'}. 358 * 359 * <BR /><BR /><B><SPAN STYLE="color: red;">MEANING:</B></SPAN> Since the {@code String}-Search 360 * and {@code String-Loops} should be as optimized as possible - due to the fact there is a 361 * possibility they could be invoked many, many times - Setting the value of {@code this.end} 362 * to be 'less the value of a compare-{@code String} length' means that many fewer comparison's 363 * need to be performed. The compare-{@code String} cannot possibly fit between {@code ePos} 364 * and 'the end of the source-{@code String}' if {@code ePos} is closer to the end of the 365 * source-{@code String} than the total size of {@code 'cmprStrLen'}. Primarily, if this does 366 * not make sense, this constructor is an optimization on the standard {@code String} loop 367 * variable constructor that allows to shorted {@code this.end} in order to eliminate 368 * extraneous {@code for-loop} comparison's in {@code class StrCmpr}. 369 * 370 * @param cmprStrLen This is just an integer that represents the length of a comparison 371 * {@code String}. When looping through the contents of one {@code String}, and comparing 372 * those contents to another {@code String} - <I><B>the length of that second 373 * {@code String}</I></B> should be subtracted from the value that is stored in the field 374 * {@code public final int end} This is because one {@code String} cannot be a substring of 375 * another with a beginning matching index that does not accommodate a match before the 376 * {@code String}, itself, runs out. 377 * @throws StringIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=SIOOB_EX> 378 */ 379 public LV(String s, int sPos, int ePos, int cmprStrLen) 380 { 381 int length = s.length(); 382 383 if ((length == 0) && (sPos == 0) && (ePos <= 0)) 384 { this.start = this.end = 0; return; } 385 386 if (sPos >= length) throw new StringIndexOutOfBoundsException( 387 "Starting String Position is greater than or equal to the String's length:\n" + 388 NOTESTR(length, sPos, ePos, cmprStrLen) 389 ); 390 391 if (sPos < 0) throw new StringIndexOutOfBoundsException 392 ("Starting String position is negative:\n" + NOTESTR(length, sPos, ePos, cmprStrLen)); 393 394 if (ePos > length) throw new StringIndexOutOfBoundsException( 395 "Ending String Position is greater than the length of the String:\n" + 396 NOTESTR(length, sPos, ePos, cmprStrLen) 397 ); 398 399 if (ePos == 0) throw new StringIndexOutOfBoundsException 400 ("Ending String Position is zero:\n" + NOTESTR(length, sPos, ePos, cmprStrLen)); 401 402 this.start = sPos; 403 int endTEMP = (ePos <= 0) ? length : ePos; 404 405 if (start > endTEMP) throw new IllegalArgumentException( 406 "The starting and ending String positions are not properly chosen:\n" + 407 NOTESTR(length, sPos, ePos, cmprStrLen) 408 ); 409 410 endTEMP = endTEMP - cmprStrLen + 1; 411 this.end = (endTEMP < sPos) ? sPos : endTEMP; 412 } 413 414 415 /** 416 * Checks input parameters and either throws {@code ArrayIndexOutOfBoundsException} or returns 417 * proper loop-variable starting & ending values. 418 * 419 * <EMBED CLASS='external-html' DATA-FILE-ID=LV_IMPT_NOTE> 420 * 421 * @param arr This may be an array of any type {@code Object} 422 * 423 * @param sPos This is the starting position in the {@code 'array'} for the loop-variable 424 * counter being created. This value is <B>inclusive</B>. This means that the first element 425 * searched by {@code 'this'} loop-variable counter instance <I>shall be at the index 426 * {@code 'sPos'}</I>. 427 * 428 * <BR /><BR />If all validity checks are passed, {@code 'this.start'} is assigned the value of 429 * {@code 'sPos'}. 430 * 431 * @param ePos This is the ending position in the {@code 'array'} for the loop-variable counter 432 * being created. This value is <B>exclusive</B>. This means that the last element searched 433 * by {@code 'this'} loop-variable counter instance <I>shall be at the index 434 * {@code ePos - 1}</I>. 435 * 436 * <BR /><BR />A negative value may be passed to {@code 'ePos'} - and if so, {@code this.end} 437 * shall be set to {@code array.length}, otherwise {@code this.end} is assigned the value of 438 * {@code 'ePos'}. 439 * 440 * @throws ArrayIndexOutOfBoundsException <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX> 441 */ 442 public <T> LV(T[] arr, int sPos, int ePos) 443 { 444 int length = arr.length; 445 446 if ((length == 0) && (sPos == 0) && (ePos <= 0)) 447 { this.start = this.end = 0; return; } 448 449 if (sPos >= length) throw new ArrayIndexOutOfBoundsException( 450 "Starting Array Position is greater than or equal to the Array's length:\n" + 451 NOTEA(length, sPos, ePos) 452 ); 453 454 if (sPos < 0) throw new ArrayIndexOutOfBoundsException 455 ("Starting Array Position is negative: " + NOTEA(length, sPos, ePos)); 456 457 if (ePos > length) throw new IndexOutOfBoundsException( 458 "Ending Array Position is greater than the length of the Array:\n" + 459 NOTEA(length, sPos, ePos) 460 ); 461 462 if (ePos == 0) throw new ArrayIndexOutOfBoundsException 463 ("Ending Array Position is zero.:\n" + NOTEA(length, sPos, ePos)); 464 465 this.start = sPos; 466 this.end = (ePos <= 0) ? length : ePos; 467 468 if (start > end) throw new IllegalArgumentException( 469 "The starting and ending Array Positions are not properly chosen:\n" + 470 NOTEA(length, sPos, ePos) 471 ); 472 } 473 474 /** 475 * Checks input parameters and either throws {@code ArrayIndexOutOfBoundsException} or returns 476 * proper loop-variable starting & ending values. 477 * 478 * <EMBED CLASS='external-html' DATA-FILE-ID=LV_IMPT_NOTE> 479 * 480 * <BR /><BR />In this particular method, when {@code 'ePos'} is passed a Negative-Value, the 481 * length of the input-array parameter {@code 'primitiveArray'} (the value assigned to 482 * {@link #end}) is computed using a heuristic from {@code java.lang.reflect}. 483 * 484 * @param primitiveArray This may be an array of any <B>primitive</B> type. {@code int[], 485 * float[], boolean[]}, etc... 486 * 487 * @param sPos This is the starting position in the {@code 'array'} for the loop-variable 488 * counter being created. This value is <B>inclusive</B>. This means that the first element 489 * searched by {@code 'this'} loop-variable counter instance <I>shall be at the index 490 * {@code 'sPos'}</I>. 491 * 492 * <BR /><BR />If all validity checks are passed, {@code 'this.start'} is assigned the value of 493 * {@code 'sPos'}. 494 * 495 * @param ePos This is the ending position in the {@code 'array'} for the loop-variable counter 496 * being created. This value is <B>exclusive</B>. This means that the last element searched 497 * by {@code 'this'} loop-variable counter instance <I>shall be at the index 498 * {@code ePos - 1}</I>. 499 * 500 * <BR /><BR />A negative value may be passed to {@code 'ePos'} - and if so, {@code this.end} 501 * shall be set to {@code primitiveArray.length}, otherwise {@code this.end} is assigned the 502 * value of {@code 'ePos'}. 503 * 504 * @throws ArrayIndexOutOfBoundsException 505 * <EMBED CLASS='external-html' DATA-FILE-ID=AIOOB_EX> 506 * 507 * @throws ArrayExpectedError This error is thrown if the reference passed to parameter 508 * {@code 'primitiveArray'} is not actually a reference to a {@code byte[], short[], int[]} 509 * etc... primitive array. An error is used because the whole purpose of the class {@code LV} 510 * is to help reduce programming errors with automatic for-loop bounds checking. If, in the 511 * course of exception checking, another exception is thrown it signals a more fundamental 512 * mistake has been made. 513 */ 514 public LV(int sPos, int ePos, Object primitiveArray) 515 { 516 if (! primitiveArray.getClass().isArray()) throw new ArrayExpectedError( 517 "The Object passed to 'primitiveArray' is not an actually an array, but " + 518 "rather an instance of [" + primitiveArray.getClass().getName() + ']' 519 ); 520 521 int length = java.lang.reflect.Array.getLength(primitiveArray); 522 523 if ((length == 0) && (sPos == 0) && (ePos <= 0)) 524 { this.start = this.end = 0; return; } 525 526 if (sPos >= length) throw new ArrayIndexOutOfBoundsException( 527 "Starting Array Position is greater than or equal to the Array's length:\n" + 528 NOTEA(length, sPos, ePos) 529 ); 530 531 if (sPos < 0) throw new ArrayIndexOutOfBoundsException 532 ("Starting Array Position is negative: " + NOTEA(length, sPos, ePos)); 533 534 if (ePos > length) throw new ArrayIndexOutOfBoundsException( 535 "Ending Array Position is greater than the length of the Array:\n" + 536 NOTEA(length, sPos, ePos) 537 ); 538 539 if (ePos == 0) throw new ArrayIndexOutOfBoundsException 540 ("Ending Array Position is zero.:\n" + NOTEA(length, sPos, ePos)); 541 542 this.start = sPos; 543 this.end = (ePos <= 0) ? length : ePos; 544 545 if (start > end) throw new IllegalArgumentException( 546 "The starting and ending Array Positions are not properly chosen:\n" + 547 NOTEA(length, sPos, ePos) 548 ); 549 } 550}