001package Torello.Java; 002 003import Torello.HTML.NodeSearch.TCCompareStrException; 004import Torello.HTML.NodeSearch.TextComparitor; 005 006import java.util.*; 007import java.util.regex.*; 008import java.io.IOException; 009 010import java.util.function.Predicate; 011import java.util.stream.Stream; 012import java.io.Serializable; 013 014/** 015 * A simple functional-interface that provides many 'builder' methods for creating standard 016 * Java <CODE>Predicate's</CODE> that operate on an Object's <CODE>'toString'</CODE> method. 017 * 018 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER> 019 */ 020@FunctionalInterface 021public interface StrFilter extends Serializable, Predicate<Object> 022{ 023 /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDFI> */ 024 public static final long serialVersionUID = 1; 025 026 027 // ******************************************************************************************** 028 // ******************************************************************************************** 029 // Functional-Interface Method 030 // ******************************************************************************************** 031 // ******************************************************************************************** 032 033 034 /** 035 * <EMBED CLASS='external-html' DATA-FILE-ID=FUNC_INTER_METH> 036 * 037 * <BR /><BR />This method will receive a {@code java.lang.Object}. This {@code Object} must 038 * have implemented its {@code toString()} method The purpose of this method is to provide an 039 * easy means to filter certain {@code String's} (automatically). 040 * 041 * <BR /><BR /><B CLASS=JDDescLabel>Standard Filter Behavior:</B> 042 * 043 * <BR />This method should return {@code FALSE} if the passed {@code String} <I><B>should be 044 * skipped</B></I>. A return value of {@code TRUE} implies that the {@code String} is not to 045 * be ignored or passed over, but rather 'kept.' 046 * 047 * <BR /><BR />This behavior is consisten with the Java Stream's method 048 * {@code Stream.filter(Predicate)}. 049 * 050 * @param o This is the {@code Object} that will be tested. If it passes the test, then this 051 * method must return {@code TRUE} which would imply that the object shall not be filtered. 052 * If the object fails the test, this method should return {@code FALSE}, and the object shall 053 * not be included in the result set as it has failed the test. 054 * 055 * @return When implementing this method, returning {@code TRUE} must mean that the 056 * {@code Object} has passed the filter's test-requirements (and will subsequently be retained 057 * by whatever function is carrying out the filter operation). 058 */ 059 public boolean test(Object o); 060 061 /** 062 * Java is not perfect. This method is fine: since {@code String} inherits {@code Object}, 063 * and the {@code interface Predicate} does not have any write-dependencies. 064 * 065 * <BR /><BR />Erasure is like this with {@code interface Predicate} - because that's how 066 * it works with all of the generics. A {@code Vector<Object>} cannot be passed to a 067 * {@code Vector<String>} parameter (because a person might need to use the {@code String's}). 068 * But a {@code Predicate<Object>} <B><I>can not</I></B> have problems being passed to a 069 * {@code Predicate<String>}... 070 * 071 * @return {@code 'this' StrFilter} cast into a {@code Predicate<String>} 072 */ 073 @SuppressWarnings({"rawtypes", "unchecked"}) 074 public default Predicate<String> castToStrPred() 075 { 076 Predicate p = this; 077 return (Predicate<String>) p; 078 } 079 080 081 // ******************************************************************************************** 082 // ******************************************************************************************** 083 // strList: KEEP 084 // ******************************************************************************************** 085 // ******************************************************************************************** 086 087 088 /** 089 * Convenience Method. 090 * <BR />Invokes: {@link #strListKEEP(Iterator, boolean)} 091 */ 092 public static StrFilter strListKEEP(Iterable<String> strList, boolean ignoreCase) 093 { return strListKEEP(strList.iterator(), ignoreCase); } 094 095 /** 096 * Convenience Method. 097 * <BR />Invokes: {@link #strListKEEP(Iterator, boolean)} 098 * <BR />Converts: {@code String[]} VarArgs to {@code Iterator<String>} 099 */ 100 public static StrFilter strListKEEP(boolean ignoreCase, String... strList) 101 { return strListKEEP(Arrays.asList(strList).iterator(), ignoreCase); } 102 103 /** 104 * Convenience Method. 105 * <BR />Invokes: {@link #strListKEEP(Iterator, boolean)} 106 * <BR />Loads: {@code 'strListFileName'} from disk to an {@code Iterator<String>} 107 */ 108 public static StrFilter strListKEEP(String strListFileName, boolean ignoreCase) 109 throws IOException 110 { return strListKEEP(FileRW.loadFileToVector(strListFileName, false).iterator(), ignoreCase); } 111 112 /** 113 * Convenience Method. 114 * <BR />Invokes: {@link #strListKEEP(Iterator, boolean)} 115 * <BR />Loads: {@code 'strListFileName'} from disk to an {@code Iterator<String>} 116 * <BR />Catches Exception, and Fails Fast - (See {@link LFEC}) 117 */ 118 public static StrFilter strListKEEP_NOIOE(String strListFileName, boolean ignoreCase) 119 { return strListKEEP(LFEC.loadFileToVector(strListFileName, false).iterator(), ignoreCase); } 120 121 /** 122 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE2> 123 * 124 * <BR /><BR />Here, an input-object's {@code toString()} result is compared against the 125 * {@code String's} contained in {@code 'strList'} - which would also be called an 126 * input-{@code String} 'white-list.' 127 * 128 * @param strList A list of {@code String's} to be used as a 'comparison set' against an 129 * input-{@code String} in a {@code Predicate}-test. 130 * 131 * @param ignoreCase When this is {@code TRUE} all equality-comparisons performed in the 132 * {@code String's} tested will ignore case-sensitivity. 133 * 134 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 135 * according to each of the {@code String's} in the input-parameter {@code 'strList'} 136 */ 137 public static StrFilter strListKEEP(Iterator<String> strList, boolean ignoreCase) 138 { 139 // Build a TreeSet<String>, this will be used, internally, in the instance of 'StrFilter' 140 // (a Predicate<Object>) that is returned by this factory-builder method. 141 // 142 // Experienced Java users: TreeSet is a very convenient and easy-to-use "sorted list" 143 // implementation that stores strings, in sorted order, in a binary-tree. This makes 144 // looking them up as efficient as possible. 145 146 final TreeSet<String> ts = new TreeSet<>(); 147 148 // When building this sorted-list (TreeSet), if we are ignoring case (and-only-if), then 149 // we must convert the strings contained by the input parameter string-list 'strList' to 150 // lower-case first. 151 152 if (ignoreCase) while (strList.hasNext()) ts.add(strList.next().toLowerCase()); 153 else while (strList.hasNext()) ts.add(strList.next()); 154 155 // The returned Predicate<Object> must call '.toString()' on it's object-input before 156 // checking the "Sorted-List" (TreeSet<String>) to see if it contains the string. If a 157 // case-insensitive predicate was requested, then we must build a Predicate that invokes 158 // BOTH 'toString()' AND THEN invokes 'toLowerCase()' on the input Object. 159 160 if (ignoreCase) return (Object o) -> ts.contains(o.toString().toLowerCase()); 161 else return (Object o) -> ts.contains(o.toString()); 162 } 163 164 165 // ******************************************************************************************** 166 // ******************************************************************************************** 167 // strList REJECT 168 // ******************************************************************************************** 169 // ******************************************************************************************** 170 171 172 /** 173 * Convenience Method. 174 * <BR />Invokes: {@link #strListREJECT(Iterator, boolean)} 175 */ 176 public static StrFilter strListREJECT(Iterable<String> strList, boolean ignoreCase) 177 { return strListREJECT(strList.iterator(), ignoreCase); } 178 179 /** 180 * Convenience Method. 181 * <BR />Invokes: {@link #strListREJECT(Iterator, boolean)} 182 * <BR />Converts: {@code String[]} VarArgs to {@code Iterator<String>} 183 */ 184 public static StrFilter strListREJECT(boolean ignoreCase, String... strList) 185 { return strListREJECT(Arrays.asList(strList).iterator(), ignoreCase); } 186 187 /** 188 * Convenience Method. 189 * <BR />Invokes: {@link #strListREJECT(Iterator, boolean)} 190 * <BR />Loads: {@code 'strListFileName'} from disk to an {@code Iterator<String>} 191 */ 192 public static StrFilter strListREJECT(String strListFileName, boolean ignoreCase) throws IOException 193 { return strListREJECT(FileRW.loadFileToVector(strListFileName, false).iterator(), ignoreCase); } 194 195 /** 196 * Convenience Method. 197 * <BR />Invokes: {@link #strListREJECT(Iterator, boolean)} 198 * <BR />Loads: {@code 'strListFileName'} from disk to an {@code Iterator<String>} 199 * <BR />Catches Exception, and Fails Fast - (See {@link LFEC}) 200 */ 201 public static StrFilter strListREJECT_NOIOE(String strListFileName, boolean ignoreCase) 202 { return strListREJECT(LFEC.loadFileToVector(strListFileName, false).iterator(), ignoreCase); } 203 204 /** 205 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE2> 206 * 207 * <BR /><BR />Here, an input-object's {@code toString()} result is compared against the 208 * {@code String's} contained in {@code 'strList'} - which would also be called an 209 * input-{@code String} 'black-list.' 210 * 211 * @param strList A list of {@code String's} to be used as a 'comparison set' against an 212 * input-{@code String} in a {@code Predicate}-test. 213 * 214 * @param ignoreCase When this is {@code TRUE} all equality-comparisons performed in the 215 * {@code String's} tested will ignore case-sensitivity. 216 * 217 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 218 * according to each of the {@code String's} in the input-parameter {@code 'strList'}. 219 */ 220 public static StrFilter strListREJECT(Iterator<String> strList, boolean ignoreCase) 221 { 222 // Build a TreeSet<String>, this will be used, internally, in the instance of 'StrFilter' 223 // (a Predicate<Object>) that is returned by this factory-builder method. 224 // 225 // Experienced Java users: TreeSet is a very convenient and easy-to-use "sorted list" 226 // implementation that stores strings, in sorted order, in a binary-tree. This makes 227 // looking them up as efficient as possible. 228 229 final TreeSet<String> ts = new TreeSet<>(); 230 231 // When building this sorted-list (TreeSet), if we are ignoring case (and-only-if), then 232 // we must convert the strings contained by the input parameter string-list 'strList' to 233 // lower-case first. 234 235 if (ignoreCase) while (strList.hasNext()) ts.add(strList.next().toLowerCase()); 236 else while (strList.hasNext()) ts.add(strList.next()); 237 238 // The returned Predicate<Object> must call '.toString()' on it's object-input before 239 // checking the "Sorted-List" (TreeSet<String>) to see if it contains the string. If a 240 // case-insensitive predicate was requested, then we must build a Predicate that invokes 241 // BOTH 'toString()' AND THEN invokes 'toLowerCase()' on the input Object. 242 243 if (ignoreCase) return (Object o) -> ! ts.contains(o.toString().toLowerCase()); 244 else return (Object o) -> ! ts.contains(o.toString()); 245 } 246 247 248 // ******************************************************************************************** 249 // ******************************************************************************************** 250 // strList Regular-Expressions: Filter Factory / Filter-Generator static-methods 251 // ******************************************************************************************** 252 // ******************************************************************************************** 253 254 255 /** 256 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE2> 257 * 258 * <BR /><BR />Here, an input-object's {@code toString()} result is checked against Java's 259 * Regular-Expression matcher. The {@code Predicate} generated will choose to <B>KEEP</B> 260 * input-{@code Object's} by returning {@code TRUE} when the Regular-Expression matches the 261 * {@code Object.toString()} results. 262 * 263 * @param regEx A regular-expression used to compare against an input-{@code Object} (using 264 * {@code Object.toString()} in a {@code Predicate}-test. 265 * 266 * @param regExMustMatchEntireString When this parameter is passed {@code TRUE}, then the 267 * regular-expression {@code Pattern} must match the <B>entire input {@code Object.toString()} 268 * </B>. When this parameter is passed {@code FALSE}, it only need match some portion or 269 * part of the input-parameter {@code String} in the {@code Predicate-test} that is being 270 * generated. 271 * 272 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 273 * according to the input-parameter {@code 'regEx'} 274 */ 275 public static StrFilter regExKEEP(Pattern regEx, boolean regExMustMatchEntireString) 276 { 277 // FAIL-FAST: Perform this test BEFORE building the regex, to save the programmer 278 // from later on (when using this factory generated predicate) having a 279 // NullPointerException that is more difficult to isolate. 280 281 if (regEx == null) throw new NullPointerException( 282 "The Regular-Expression provided to the 'regEx' parameter of this " + 283 "static-factory-builder method, 'regExKEEP,' was null." 284 ); 285 286 if (regExMustMatchEntireString) 287 { 288 // the java.util.regex.Pattern.asPredicate() method produces a predicate that must 289 // match an entire input string. This would be identical (if it were possible) to 290 // insert a '^' at the beginning of the regex, and an '$' at the end. 291 292 final Predicate<String> p = regEx.asPredicate(); 293 294 return (Object o) -> p.test(o.toString()); 295 } 296 297 else 298 return (Object o) -> regEx.matcher(o.toString()).find(); 299 } 300 301 /** 302 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE2> 303 * 304 * <BR /><BR />Here, an input-object's {@code toString()} result is checked against Java's 305 * Regular-Expression matcher. The {@code Predicate} generated will choose to <B>REJECT</B> 306 * input-{@code Object's} by returning {@code FALSE} when the Regular-Expression matches the 307 * {@code Object.toString()} results. 308 * 309 * @param regEx A regular-expression used to compare against an input-{@code Object} (using 310 * {@code Object.toString()} in a {@code Predicate}-test. 311 * 312 * @param regExMustMatchEntireString When this parameter is passed {@code TRUE}, then the 313 * regular-expression {@code Pattern} must match the <B>entire input {@code Object.toString()} 314 * </B>. When this parameter is passed {@code FALSE}, it only need match some portion or 315 * part of the input-parameter {@code String} in the {@code Predicate-test} that is being 316 * generated. 317 * 318 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 319 * according to the input-parameter {@code 'regEx'} 320 */ 321 public static StrFilter regExREJECT(Pattern regEx, boolean regExMustMatchEntireString) 322 { 323 // FAIL-FAST: Perform this test BEFORE building the regex, to save the programmer 324 // from later on (when using this factory generated predicate) having a 325 // NullPointerException that is more difficult to isolate. 326 327 if (regEx == null) throw new NullPointerException( 328 "The Regular-Expression provided to the 'regEx' parameter of this " + 329 "static-factory-builder method, 'regExREJECT,' was null." 330 ); 331 332 if (regExMustMatchEntireString) 333 { 334 // the java.util.regex.Pattern.asPredicate() method produces a predicate that must 335 // match an entire input string. This would be identical (if it were possible) to 336 // insert a '^' at the beginning of the regex, and an '$' at the end. 337 338 final Predicate<String> p = regEx.asPredicate().negate(); 339 return (Object o) -> ! p.test(o.toString()); 340 } 341 else 342 return (Object o) -> ! regEx.matcher(o.toString()).find(); 343 } 344 345 346 // ******************************************************************************************** 347 // ******************************************************************************************** 348 // strList Regular-Expressions: Filter Factory / Filter-Generator static-methods 349 // ******************************************************************************************** 350 // ******************************************************************************************** 351 352 353 /** 354 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE> 355 * 356 * The {@code Predicate} that is created by this factory-method will have a {@code test()} 357 * method that returns {@code TRUE} if an input object's {@code Object.toString()} output 358 * matches <I>each-and-every-one</I> of the provided regular-expressions. 359 * 360 * @param regExs <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_REGEXS> 361 * 362 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 363 * according to the input-parameter {@code 'regExs'} 364 */ 365 public static StrFilter regExsAND(Pattern... regExs) 366 { 367 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 368 // the lambda-predicate is invoked. 369 370 for (Pattern regEx : regExs) 371 372 if (regEx == null) throw new NullPointerException( 373 "One or more of the elements passed to static-factory method 'regexsAND' are " + 374 "null." 375 ); 376 377 // This over-comes a minor "possible complication" - without likely causing much 378 // inefficiency. If a RegEx[] were passed (array, not a '...' list) and that array were 379 // changed after building this Predicate, the Predicate's behavior would fail. 380 // Avoid this (unlikely, but possible) problem by copying the array, and returning a 381 // predicate that stores a different array pointer (because it is to a different array) 382 // inside the Predicate's body. 383 384 final Pattern[] pArr = regExs.clone(); 385 386 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 387 // the lambda-predicate is invoked. 388 389 if (pArr.length == 0) throw new IllegalArgumentException( 390 "This static-factory method 'regExsAND' has been invoked with zero-arguments to the " + 391 "'regExs' var-args parameter." 392 ); 393 394 return (Object o) -> 395 { 396 String s=o.toString(); 397 398 // Cycle the Regular-Expressions. "AND" means if even one fails, return FALSE 399 // immediately. 400 401 for (Pattern p : pArr) if (! p.matcher(s).find()) return false; 402 403 // None of them failed, return TRUE. 404 return true; 405 }; 406 } 407 408 /** 409 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE> 410 * 411 * The {@code Predicate} that is created by this factory-method will have a {@code test()} 412 * method that returns {@code TRUE} if an input object's {@code Object.toString()} output 413 * matches <I>any-one-of</I> the provided regular-expressions. 414 * 415 * @param regExs <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_REGEXS> 416 * 417 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 418 * according to the input-parameter {@code 'regExs'} 419 */ 420 public static StrFilter regExsOR(Pattern... regExs) 421 { 422 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once the 423 // lambda-predicate is invoked. 424 425 for (Pattern regEx : regExs) 426 427 if (regEx == null) throw new NullPointerException( 428 "One or more of the elements passed to static-factory method 'regExsOR' are " + 429 "null." 430 ); 431 432 // This over-comes a minor "possible complication" - without likely causing much 433 // inefficiency. If a RegEx[] were passed (array, not a '...' list) and that array were 434 // changed after building this Predicate, the Predicate's behavior would fail. 435 // Avoid this (unlikely, but possible) problem by copying the array, and returning a 436 // predicate that stores a different array pointer (because it is to a different array) 437 // inside the Predicate's body. 438 439 final Pattern[] pArr = regExs.clone(); 440 441 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 442 // the lambda-predicate is invoked. 443 444 if (pArr.length == 0) throw new IllegalArgumentException( 445 "This static-factory method 'regExsOR' has been invoked with zero-arguments to the" + 446 "'regExs' var-args parameter." 447 ); 448 449 return (Object o) -> 450 { 451 String s = o.toString(); 452 453 // Cycle the Regular-Expressions. "OR" means if even one succeeds, return TRUE 454 // immediately. 455 456 for (Pattern p : pArr) if (p.matcher(s).find()) return true; 457 458 // None succeeded, so return FALSE. 459 return false; 460 }; 461 } 462 463 /** 464 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE> 465 * 466 * The {@code Predicate} that is created by this factory-method will have a {@code test()} 467 * method that returns {@code TRUE} if an input object's {@code Object.toString()} output 468 * <I>does-not-match-any</I> of the provided regular-expressions. 469 * 470 * @param regExs <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_REGEXS> 471 * 472 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 473 * according to the input-parameter {@code 'regExs'}. 474 */ 475 public static StrFilter regExsNAND(Pattern... regExs) 476 { 477 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once the 478 // lambda-predicate is invoked. 479 480 for (Pattern regEx : regExs) 481 482 if (regEx == null) throw new NullPointerException( 483 "One or more of the elements passed to static-factory method 'regExsNAND' are " + 484 "null." 485 ); 486 487 // This over-comes a minor "possible complication" - without likely causing much 488 // inefficiency. If a RegEx[] were passed (array, not a '...' list) and that array were 489 // changed after building this Predicate, the Predicate's behavior would fail. 490 // Avoid this (unlikely, but possible) problem by copying the array, and returning a 491 // predicate that stores a different array pointer (because it is to a different array) 492 // inside the Predicate's body. 493 494 final Pattern[] pArr = regExs.clone(); 495 496 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 497 // the lambda-predicate is invoked. 498 499 if (pArr.length == 0) throw new IllegalArgumentException( 500 "This static-factory method 'regExsNAND' has been invoked with zero-arguments to " + 501 "the 'regExs' var-args parameter." 502 ); 503 504 return (Object o) -> 505 { 506 String s = o.toString(); 507 508 // Cycle the Regular-Expressions. "NAND" means if even one succeeds, return FALSE 509 // immediately. 510 511 for (Pattern p : pArr) if (p.matcher(s).find()) return false; 512 513 // All regex's failed, so return TRUE 514 return true; 515 }; 516 } 517 518 /** 519 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE> 520 * 521 * The {@code Predicate} that is created by this factory-method will have a {@code test()} 522 * method that returns {@code TRUE} if an input object's {@code Object.toString()} output 523 * matches <I>precisely-one-of</I> the provided regular-expressions. 524 * 525 * @param regExs <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_REGEXS> 526 * 527 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 528 * according to the input-parameter {@code 'regExs'} 529 */ 530 public static StrFilter regExsXOR(Pattern... regExs) 531 { 532 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once the 533 // lambda-predicate is invoked. 534 535 for (Pattern regEx : regExs) 536 537 if (regEx == null) throw new NullPointerException( 538 "One or more of the elements passed to static-factory method 'regExsXOR' are " + 539 "null." 540 ); 541 542 // This over-comes a minor "possible complication" - without likely causing much 543 // inefficiency. If a RegEx[] were passed (array, not a '...' list) and that array were 544 // changed after building this Predicate, the Predicate's behavior would fail. 545 // Avoid this (unlikely, but possible) problem by copying the array, and returning a 546 // predicate that stores a different array pointer (because it is to a different array) 547 // inside the Predicate's body. 548 549 final Pattern[] pArr = regExs.clone(); 550 551 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 552 // the lambda-predicate is invoked. 553 554 if (pArr.length == 0) throw new IllegalArgumentException( 555 "This static-factory method 'regExsXOR' has been invoked with zero-arguments to the " + 556 "'regExs' var-args parameter." 557 ); 558 559 return (Object o) -> 560 { 561 String s = o.toString(); 562 int count = 0; 563 564 // Cycle the Regular-Expressions. Because this is "XOR" - we must keep a count. 565 // If that count is > 1, we have to return FALSE immediately. 566 567 for (Pattern p : pArr) if (p.matcher(s).find()) { if (++count > 1) return false; } 568 569 // If there was precisely one match, return TRUE, otherwise return FALSE. 570 return count == 1; 571 }; 572 } 573 574 575 // ******************************************************************************************** 576 // ******************************************************************************************** 577 // Other static-factory methods 578 // ******************************************************************************************** 579 // ******************************************************************************************** 580 581 582 /** 583 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE2> 584 * 585 * Here, the class {@code TextComparitor} is reused from the {@code 'NodeSearch'} package. 586 * Please review how each of the pre-defined, {@code static}-instances of class 587 * {@code TextComparitor} operate when presented with {@code String}-input. 588 * 589 * <DIV CLASS="EXAMPLE">{@code 590 * StrFilter filter = StrFilter.comparitor 591 * (TextComparitor.DOES_NOT_START_WITH, "Today in Washington,"); 592 * 593 * // The above fiter would REJECT any input Object whose 'toString()' method returned a 594 * // String that began with the words "Today in Washington," 595 * 596 * StrFilter filter2 = StrFilter.comparitor 597 * (TextComparitor.CONTAINS_CASE_INSENSITIVE, "Highway 61 Revisited"); 598 * 599 * // This filter would KEEP / RETAIN any input Object whose 'toString()' method returned a 600 * // String that contained the words "Highway 61 Revisited". This comparison to be performed 601 * // would be case-insensitive. 602 * }</DIV> 603 * 604 * @param tc This is an instance of the {@code class TextComparitor}, which is defined in the 605 * NodeSearch package. 606 * 607 * @param compareStrs These must be the comparison-{@code String's} used by class 608 * {@code 'TextComparitor'} for performing the comparisons. 609 * 610 * @return A new {@code StrFilter} that can be used to test and filter {@code String's}, 611 * according to the input-parameters {@code 'tc'} and {@code 'compareStrs'} 612 * 613 * @see TextComparitor 614 */ 615 public static StrFilter comparitor(TextComparitor tc, java.lang.String... compareStrs) 616 { 617 // FAIL-FAST: Check that the user-provided-parameters would not cause exceptions once 618 // the lambda-predicate is invoked. 619 620 if (tc == null) throw new NullPointerException 621 ("A null TextComparitor has been passed to static-factory method 'comparitorKeep'"); 622 623 TCCompareStrException.check(compareStrs); 624 625 // Mostly, this is over-board, but it doesn't slow anything down much, and it does force 626 // users to think about Object-References and Parameter-Argument Marshaling. 627 628 final String[] cmpStrs = compareStrs.clone(); 629 630 // Builds (and returns) a Predicate<Object> that re-uses the TextComparitor's 631 // 'test(String, String...)' method. 632 633 return (Object o) -> tc.test(o.toString(), cmpStrs); 634 } 635 636 /** 637 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE2> 638 * 639 * Here, a single {@code String} is used as input. The {@code Predicate} that is created by 640 * this factory-method will have a {@code test()} method that returns {@code TRUE} only if the 641 * an input object's {@code 'toString()'} output <I>equals</I> the the input-parameter 642 * {@code String 's'}. 643 * 644 * @param s This is a {@code String} which will be used to test for equality in the generated 645 * {@code Predicate.test(Object)} 646 * 647 * @param ignoreCase When this is {@code TRUE}, the equality-test will ignore case when 648 * performing its comparisons. 649 * 650 * @return A new {@code StrFilter} that can be used to test and filter {@code Object's} via the 651 * {@code Object.toString()} method, and the input-parameter {@code 's'} 652 */ 653 public static StrFilter isEqualKEEP(String s, boolean ignoreCase) 654 { 655 if (s == null) throw new NullPointerException( 656 "A null String has been passed to parameter 's' of static-factory method " + 657 "'isEqualKEEP'." 658 ); 659 660 // Builds & returns a predicate based on the whether or not 'ignoreCase' is true or false. 661 return ignoreCase 662 ? (Object o) -> o.toString().equalsIgnoreCase(s) 663 : (Object o) -> o.toString().equals(s); 664 } 665 666 /** 667 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_FILTER_NOTE2> 668 * 669 * Here, a single {@code String} is used as input. The {@code Predicate} that is created by 670 * this factory-method will have a {@code test()} method that returns {@code TRUE} only if an 671 * input {@code Object's} {@code Object.toString()} output <I>does-not-equal</I> the the 672 * input-parameter {@code String 's'}. 673 * 674 * @param s This is a {@code String} which will be used to test for equality in the generated 675 * {@code Predicate.test(Object)}. 676 * 677 * @param ignoreCase When this is {@code TRUE}, the equality-test will ignore case when 678 * performing its comparisons. 679 * 680 * @return A new {@code StrFilter} that can be used to test and filter {@code Object's} via the 681 * {@code Object.toString()} method, and the input-parameter {@code 's'} 682 */ 683 public static StrFilter isEqualREJECT(String s, boolean ignoreCase) 684 { 685 if (s == null) throw new NullPointerException( 686 "A null String has been passed to parameter 's' of static-factory method " + 687 "'isEqualREJECT'." 688 ); 689 690 // Builds & returns a predicate based on the whether or not 'ignoreCase' is true or false. 691 return ignoreCase 692 ? (Object o) -> ! o.toString().equalsIgnoreCase(s) 693 : (Object o) -> ! o.toString().equals(s); 694 } 695}