001package Torello.Java; 002 003import java.util.TreeSet; 004 005/** 006 * Class String-Character provides an exhaustive-combinatoric suite of methods that extend the 007 * basic Java <CODE>String</CODE> methods <CODE>equals, contains, startsWith</CODE> and 008 * <CODE>endsWith</CODE>, using Java {@code char} primitives as the comparator element. 009 * 010 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH> 011 */ 012@Torello.JavaDoc.StaticFunctional 013@Torello.JavaDoc.CSSLinks(FileNames="Strings.css") 014public class StrCh 015{ 016 private StrCh() { } 017 018 019 // ******************************************************************************************** 020 // ******************************************************************************************** 021 // CONTAINS 022 // ******************************************************************************************** 023 // ******************************************************************************************** 024 025 026 /** 027 * <EMBED CLASS=defs DATA-DESC='contains at least one' DATA-CI='is'> 028 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 029 * @param srcStr Any non-null instance of {@code java.lang.String} 030 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 031 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 032 * @see #CONTAINS_NAND_OR(boolean, byte, String, char[]) 033 */ 034 public static boolean containsOR(String srcStr, char... compareChar) 035 { return CONTAINS_NAND_OR(false, OR, srcStr, compareChar); } 036 037 /** 038 * <EMBED CLASS=defs DATA-DESC='contains every one' DATA-CI='is'> 039 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 040 * @param srcStr Any non-null instance of {@code java.lang.String} 041 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 042 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 043 * @see #CONTAINS_XOR_AND(boolean, byte, String, char[]) 044 */ 045 public static boolean containsAND(String srcStr, char... compareChar) 046 { return CONTAINS_XOR_AND(false, AND, srcStr, compareChar); } 047 048 /** 049 * <EMBED CLASS=defs DATA-DESC='contains exactly one' DATA-CI='is'> 050 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 051 * @param srcStr Any non-null instance of {@code java.lang.String} 052 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 053 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 054 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_XOR_NOTE> 055 * @see #CONTAINS_XOR_AND(boolean, byte, String, char[]) 056 */ 057 public static boolean containsXOR(String srcStr, char... compareChar) 058 { return CONTAINS_XOR_AND(false, XOR, srcStr, compareChar); } 059 060 /** 061 * <EMBED CLASS=defs DATA-DESC='does not contain any' DATA-CI='is'> 062 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 063 * @param srcStr Any non-null instance of {@code java.lang.String} 064 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 065 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 066 * @see #CONTAINS_NAND_OR(boolean, byte, String, char[]) 067 */ 068 public static boolean containsNAND(String srcStr, char... compareChar) 069 { return CONTAINS_NAND_OR(false, NAND, srcStr, compareChar); } 070 071 /** 072 * <EMBED CLASS=defs DATA-DESC='contains at least one' DATA-CI='is not'> 073 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 074 * @param srcStr Any non-null instance of {@code java.lang.String} 075 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 076 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 077 * @see #CONTAINS_NAND_OR(boolean, byte, String, char[]) 078 */ 079 public static boolean containsOR_CI(String srcStr, char... compareChar) 080 { return CONTAINS_NAND_OR(true, OR, srcStr, compareChar); } 081 082 /** 083 * <EMBED CLASS=defs DATA-DESC='contains every one' DATA-CI='is not'> 084 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 085 * @param srcStr Any non-null instance of {@code java.lang.String} 086 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 087 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 088 * @see #CONTAINS_XOR_AND(boolean, byte, String, char[]) 089 */ 090 public static boolean containsAND_CI(String srcStr, char... compareChar) 091 { return CONTAINS_XOR_AND(true, AND, srcStr, compareChar); } 092 093 /** 094 * <EMBED CLASS=defs DATA-DESC='contains exactly one' DATA-CI='is not'> 095 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 096 * @param srcStr Any non-null instance of {@code java.lang.String} 097 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 098 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 099 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_XOR_NOTE> 100 * @see #CONTAINS_XOR_AND(boolean, byte, String, char[]) 101 */ 102 public static boolean containsXOR_CI(String srcStr, char... compareChar) 103 { return CONTAINS_XOR_AND(true, XOR, srcStr, compareChar); } 104 105 /** 106 * <EMBED CLASS=defs DATA-DESC='does not contain any' DATA-CI='is not'> 107 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 108 * @param srcStr Any non-null instance of {@code java.lang.String} 109 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 110 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 111 * @see #CONTAINS_NAND_OR(boolean, byte, String, char[]) 112 */ 113 public static boolean containsNAND_CI(String srcStr, char... compareChar) 114 { return CONTAINS_NAND_OR(true, NAND, srcStr, compareChar); } 115 116 117 // ******************************************************************************************** 118 // ******************************************************************************************** 119 // STARTS-WITH, ENDS-WITH 120 // ******************************************************************************************** 121 // ******************************************************************************************** 122 123 124 /** 125 * <EMBED CLASS=defs DATA-DESC='ends with at least one' DATA-CI='is'> 126 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 127 * @param srcStr Any non-null instance of {@code java.lang.String} 128 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 129 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 130 */ 131 public static boolean endsWithOR(String srcStr, char... compareChar) 132 { 133 char c = srcStr.charAt(srcStr.length() - 1); 134 for (char c2 : compareChar) if (c2 == c) return true; 135 return false; 136 } 137 138 /** 139 * <EMBED CLASS=defs DATA-DESC='does not end with any' DATA-CI='is'> 140 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 141 * @param srcStr Any non-null instance of {@code java.lang.String} 142 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 143 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 144 */ 145 public static boolean endsWithNAND(String srcStr, char... compareChar) 146 { 147 char c = srcStr.charAt(srcStr.length() - 1); 148 for (char c2 : compareChar) if (c2 == c) return false; 149 return true; 150 } 151 152 /** 153 * <EMBED CLASS=defs DATA-DESC='starts with at least one' DATA-CI='is'> 154 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 155 * @param srcStr Any non-null instance of {@code java.lang.String} 156 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 157 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 158 */ 159 public static boolean startsWithOR(String srcStr, char... compareChar) 160 { 161 char c = srcStr.charAt(0); 162 for (char c2 : compareChar) if (c2 == c) return true; 163 return false; 164 } 165 166 /** 167 * <EMBED CLASS=defs DATA-DESC='does not start with any' DATA-CI='is'> 168 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 169 * @param srcStr Any non-null instance of {@code java.lang.String} 170 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 171 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 172 */ 173 public static boolean startsWithNAND(String srcStr, char... compareChar) 174 { 175 char c = srcStr.charAt(0); 176 for (char c2 : compareChar) if (c2 == c) return false; 177 return true; 178 } 179 180 181 // ******************************************************************************************** 182 // ******************************************************************************************** 183 // STARTS-WITH, ENDS-WITH - CASE INSENSITIVE 184 // ******************************************************************************************** 185 // ******************************************************************************************** 186 187 188 /** 189 * <EMBED CLASS=defs DATA-DESC='ends with at least one' DATA-CI='is not'> 190 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 191 * @param srcStr Any non-null instance of {@code java.lang.String} 192 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 193 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 194 */ 195 public static boolean endsWithOR_CI(String srcStr, char... compareChar) 196 { 197 char c = Character.toLowerCase(srcStr.charAt(srcStr.length() - 1)); 198 for (char c2 : compareChar) if (Character.toLowerCase(c2) == c) return true; 199 return false; 200 } 201 202 /** 203 * <EMBED CLASS=defs DATA-DESC='does not end with any' DATA-CI='is not'> 204 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 205 * @param srcStr Any non-null instance of {@code java.lang.String} 206 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 207 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 208 */ 209 public static boolean endsWithNAND_CI(String srcStr, char... compareChar) 210 { 211 char c = Character.toLowerCase(srcStr.charAt(srcStr.length() - 1)); 212 for (char c2 : compareChar) if (Character.toLowerCase(c2) == c) return false; 213 return true; 214 } 215 216 /** 217 * <EMBED CLASS=defs DATA-DESC='starts at least one' DATA-CI='is not'> 218 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 219 * @param srcStr Any non-null instance of {@code java.lang.String} 220 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 221 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 222 */ 223 public static boolean startsWithOR_CI(String srcStr, char... compareChar) 224 { 225 char c = Character.toLowerCase(srcStr.charAt(0)); 226 for (char c2 : compareChar) if (Character.toLowerCase(c2) == c) return true; 227 return false; 228 } 229 230 /** 231 * <EMBED CLASS=defs DATA-DESC='does not start with any' DATA-CI='is not'> 232 * <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_DESC> 233 * @param srcStr Any non-null instance of {@code java.lang.String} 234 * @param compareChar The {@code char's} used in the comparison against {@code 'srcStr'} 235 * @return <EMBED CLASS='external-html' DATA-FILE-ID=STR_CH_RET> 236 */ 237 public static boolean startsWithNAND_CI(String srcStr, char ... compareChar) 238 { 239 char c = Character.toLowerCase(srcStr.charAt(0)); 240 for (char c2 : compareChar) if (Character.toLowerCase(c2) == c) return false; 241 return true; 242 } 243 244 245 // ******************************************************************************************** 246 // ******************************************************************************************** 247 // Single char-primitive methods 248 // ******************************************************************************************** 249 // ******************************************************************************************** 250 251 252 /** 253 * Checks whether or not {@code 'srcStr'} <B STYLE='color: red;'>starts-with</B> 254 * {@code 'compareChar'}. 255 * 256 * <BR /><BR />This method <B STYLE='color: red;'>** is not**</B> case-sensitive. 257 * 258 * @param srcStr Any non-null instance of {@code java.lang.String} 259 * @param compareChar The {@code char} used in the comparison against {@code 'srcStr'} 260 * @return {@code TRUE} if {@code 'srcStr'} begins with {@code 'compareChar'}, ignoring case. 261 */ 262 public static boolean startsWithIgnoreCase(String srcStr, char compareChar) 263 { return Character.toLowerCase(srcStr.charAt(0)) == Character.toLowerCase(compareChar); } 264 265 /** 266 * Checks whether or not {@code 'srcStr'} <B STYLE='color: red;'>ends-with</B> 267 * {@code 'compareChar'}. 268 * 269 * <BR /><BR />This method <B STYLE='color: red;'>** is not**</B> case-sensitive. 270 * 271 * @param srcStr Any non-null instance of {@code java.lang.String} 272 * @param compareChar The {@code char} used in the comparison against {@code 'srcStr'} 273 * @return {@code TRUE} if {@code 'srcStr'} ends with {@code 'compareChar'}, ignoring case. 274 */ 275 public static boolean endsWithIgnoreCase(String srcStr, char compareChar) 276 { 277 return Character.toLowerCase(srcStr.charAt(srcStr.length() - 1)) == 278 Character.toLowerCase(compareChar); 279 } 280 281 /** 282 * Checks whether or not {@code 'srcStr'} <B STYLE='color: red;'>contains</B> 283 * {@code 'compareChar'}. 284 * 285 * <BR /><BR />This method <B STYLE='color: red;'>** is not**</B> case-sensitive. 286 * 287 * @param srcStr Any non-null instance of {@code java.lang.String} 288 * @param compareChar The {@code char} used in the comparison against {@code 'srcStr'} 289 * @return {@code TRUE} if {@code 'srcStr'} begins with {@code 'compareChar'}, ignoring case. 290 */ 291 public static boolean containsIgnoreCase(String srcStr, char compareChar) 292 { 293 compareChar = Character.toLowerCase(compareChar); 294 295 int len = srcStr.length(); 296 297 for (int i=0; i < len; i++) 298 if (Character.toLowerCase(srcStr.charAt(i)) == compareChar) 299 return true; 300 301 return false; 302 } 303 304 305 // ******************************************************************************************** 306 // ******************************************************************************************** 307 // PROTECTED CONTAINS HELPER 308 // ******************************************************************************************** 309 // ******************************************************************************************** 310 311 312 /** 313 * Signifies that an {@code 'AND'} operation is required, but only for methods that implement 314 * one of the {@code 'contains'} variants. 315 */ 316 protected static final byte AND = 0; 317 318 /** 319 * Signifies that an {@code 'AND'} operation is required, but only for methods that implement 320 * one of the {@code 'contains'} variants. 321 */ 322 protected static final byte OR = 1; 323 324 /** 325 * Signifies that an {@code 'AND'} operation is required, but only for methods that implement 326 * one of the {@code 'contains'} variants. 327 */ 328 protected static final byte NAND = 2; 329 330 /** 331 * Signifies that an {@code 'AND'} operation is required, but only for methods that implement 332 * one of the {@code 'contains'} variants. 333 */ 334 protected static final byte XOR = 3; 335 336 /** 337 * A protected helper method for <B>{@code NAND}</B> and <B>{@code OR}</B> 'contains' methods. 338 * 339 * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Does not error check the value passed to 340 * {@code 'booleanOperation'}. 341 * 342 * @param ignoreCase Whether the comparisons performed should ignore case. 343 * @param booleanOperation Accepts only {@link StrCh#NAND} and {@link StrCh#OR}. 344 * @param srcStr Any non-null instance of {@code java.lang.String} 345 * @param compareChar The {@code char} used in the comparison against {@code 'srcStr'} 346 */ 347 protected static boolean CONTAINS_NAND_OR 348 (boolean ignoreCase, byte booleanOperation, String srcStr, char[] compareChar) 349 { 350 if (ignoreCase) 351 { 352 char[] temp = new char[compareChar.length]; 353 354 for (int i=0; i < compareChar.length; i++) 355 temp[i] = Character.toLowerCase(compareChar[i]); 356 357 compareChar = temp; 358 } 359 360 int len = srcStr.length(); 361 362 for (int i=0; i < len; i++) 363 { 364 char c = ignoreCase ? Character.toLowerCase(srcStr.charAt(i)) : srcStr.charAt(i); 365 366 for (char c2 : compareChar) 367 368 if (c2 == c) 369 370 switch(booleanOperation) 371 { 372 case NAND: return false; 373 case OR: return true; 374 } 375 } 376 377 switch (booleanOperation) 378 { 379 case NAND: return true; 380 case OR: return false; 381 } 382 383 throw new UnreachableError(); 384 } 385 386 /** 387 * A protected helper method for <B>{@code XOR}</B> and <B>{@code AND}</B> 'contains' methods. 388 * 389 * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Does not error check the value passed to 390 * {@code 'booleanOperation'}. 391 * 392 * @param ignoreCase Whether the comparisons performed should ignore case. 393 * @param booleanOperation Accepts only {@link StrCh#XOR} and {@link StrCh#AND}. 394 * @param srcStr Any non-null instance of {@code java.lang.String} 395 * @param compareChar The {@code char} used in the comparison against {@code 'srcStr'} 396 */ 397 protected static boolean CONTAINS_XOR_AND 398 (boolean ignoreCase, byte booleanOperation, String srcStr, char[] compareChar) 399 { 400 int count = 0; 401 402 // There is a TreeSet so that multiple instances of the same character ARE NOT CHECKED. 403 // Once a character has been found, it is more efficient to stop testing for it at all. 404 405 TreeSet<Character> ts = new TreeSet<>(); 406 407 // When building the TreeSet of characters to check, if 'ignoreCase' has been requested, 408 // then doing the case-conversion before entering the loop is more efficient. 409 410 if (ignoreCase) for (char c : compareChar) ts.add(Character.toLowerCase(c)); 411 else for (char c : compareChar) ts.add(c); 412 413 // Iterate each character in the passed 'srcStr' 414 int len = srcStr.length(); 415 416 for (int i=0; i < len; i++) 417 { 418 char c = ignoreCase ? Character.toLowerCase(srcStr.charAt(i)) : srcStr.charAt(i); 419 420 // Iterate each of the remaining compare-characters in the TreeSet. These characters 421 // are removed when matches are found. 422 423 INNER: 424 for (char c2 : ts) 425 426 if (c2 == c) 427 { 428 ts.remove(c2); 429 430 switch (booleanOperation) 431 { 432 case AND: if (--count == 0) return true; else break INNER; 433 case XOR: if (++count > 1) return false; else break INNER; 434 } 435 } 436 } 437 438 switch (booleanOperation) 439 { 440 // If the count had reached zero, then true would ALREADY have been returned. 441 case AND: return false; 442 443 // Whether or not a match has been found is all that is left to check. 444 case XOR: return count == 1; 445 } 446 447 throw new UnreachableError(); 448 } 449}