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