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