001package Torello.Java; 002 003import java.util.*; 004import java.util.regex.*; 005import java.util.zip.*; 006import java.io.*; 007import java.util.stream.*; 008import java.util.function.*; 009 010import java.text.DecimalFormat; 011import java.net.URL; 012 013import Torello.Java.Function.IntCharFunction; 014import Torello.Java.Function.IntTFunction; 015 016import Torello.Java.ReadOnly.ReadOnlyList; 017import Torello.Java.ReadOnly.ReadOnlyArrayList; 018 019import Torello.Java.Additional.Counter; 020 021/** 022 * A plethora of extensions to Java's {@code String} class. 023 * 024 * <EMBED CLASS='external-html' DATA-FILE-ID=STRING_PARSE> 025 */ 026@Torello.JavaDoc.StaticFunctional 027public class StringParse 028{ 029 private StringParse() { } 030 031 032 // ******************************************************************************************** 033 // ******************************************************************************************** 034 // Constants 035 // ******************************************************************************************** 036 // ******************************************************************************************** 037 038 039 private static final DecimalFormat formatter = new DecimalFormat("#,###"); 040 041 /** 042 * This regular expression simply matches white-space found in a java {@code String}. 043 * @see #removeWhiteSpace(String) 044 */ 045 public static final Pattern WHITE_SPACE_REGEX = Pattern.compile("\\s+"); 046 047 /** 048 * This regular expression simply matches the comma. The only reason for including this here 049 * is because the java {@code class 'Pattern'} contains a method called 050 * {@code Stream<String> 'splitAsStream(CharSequence)'} which is used for the CSV method 051 * further below 052 * 053 * @see StrCSV#CSV(String, boolean, boolean) 054 * @see FileRW#readDoublesFromFile(String, boolean, boolean) 055 * @see FileRW#readLongsFromFile(String, boolean, boolean, int) 056 */ 057 public static final Pattern COMMA_REGEX = Pattern.compile(","); 058 059 /** 060 * This regular expression is used for integer and floating-point numbers that use the 061 * comma ({@code ','}) between the digits that comprise the number. For example, this 062 * Regular Expression would match the {@code String} {@code "900,800,75.00"}. 063 * 064 * @see FileRW#readIntsFromFile(String, boolean, boolean, int) 065 */ 066 public static final Pattern NUMBER_COMMMA_REGEX = Pattern.compile("(\\d),(\\d)"); 067 068 /** 069 * This represents any version of the new-line character. Note that the {@code '\r\n'} version 070 * comes before the single {@code '\r'} version in the regular-expression, to guarantee that 071 * if both are present, they are treated as a single newline. 072 */ 073 public static final Pattern NEWLINEP = Pattern.compile("\\r\\n|\\r|\\n"); 074 075 /** 076 * Predicate for new-line characters 077 * @see #NEWLINEP 078 */ 079 public static final Predicate<String> newLinePred = NEWLINEP.asPredicate(); 080 081 /** The months of the year, as an immutable list of {@code String's}. */ 082 public static final ReadOnlyList<String> months = new ReadOnlyArrayList<>( 083 "January", "February", "March", "April", "May", "June", 084 "July", "August", "September", "October", "November", "December" 085 ); 086 087 private static final Calendar internalCalendar = Calendar.getInstance(); 088 089 /** This is the list of characters that need to be escaped for a regular expression */ 090 public static final String REG_EX_ESCAPE_CHARS = "\\/()[]{}$^+*?-."; 091 092 /** Alpha-Numeric RegEx */ 093 public static final Pattern ALPHA_NUMERIC = Pattern.compile("^[\\d\\w]*$"); 094 095 /** 096 * Alpha-Numeric {@code String} Predicate. 097 * @see #ALPHA_NUMERIC 098 */ 099 public static final Predicate<String> alphaNumPred = ALPHA_NUMERIC.asPredicate(); 100 101 // The minimum value for the byte primitive type, without the minus sign. 102 private static final char[] BYTE_MIN_VALUE_DIGITS_AS_CHARS = { '2', '5', '6' }; 103 104 // The minimum value for the short primitive type, without the minus sign. 105 private static final char[] SHORT_MIN_VALUE_DIGITS_AS_CHARS = { '6', '5', '5', '3', '6' }; 106 107 // The minimum value for the int primitive type, without the minus sign. 108 private static final char[] INT_MIN_VALUE_DIGITS_AS_CHARS = 109 { '2', '1', '4', '7', '4', '8', '3', '6', '4', '8' }; 110 111 // The minimum value for the long primitive type, without the minus sign. 112 private static final char[] LONG_MIN_VALUE_DIGITS_AS_CHARS = 113 { 114 '2', '1', '4', '9', '2', '2', '3', '3', '7', '2', '0', '3', '6', '8', '5', '4', 115 '7', '7', '5', '8', '0', '8' 116 }; 117 118 /** An empty {@code String} array. */ 119 public static final String[] EMPTY_STR_ARRAY = {}; 120 121 122 // ******************************************************************************************** 123 // ******************************************************************************************** 124 // methods 125 // ******************************************************************************************** 126 // ******************************************************************************************** 127 128 129 /** 130 * Makes a {@code long} number like {@code 123456789} into a number-string such as: 131 * {@code "123,456,789"}. Java's {@code package java.text.*} is easy to use, and versatile, but 132 * the commands are not always so easy to remember. 133 * 134 * @param l Any {@code long} integer. Comma's will be inserted for every third power of ten 135 * 136 * @return After calling java's {@code java.text.DecimalFormat} class, a {@code String} 137 * representing this parameter will be returned. 138 */ 139 public static String commas(long l) 140 { return formatter.format(l); } 141 142 /** 143 * Trims any white-space {@code Characters} from the end of a {@code String}. 144 * 145 * <BR /><TABLE CLASS=JDBriefTable> 146 * <TR><TH>Input String:</TH><TH>Output String:</TH></TR> 147 * <TR><TD>{@code "A Quick Brown Fox\n \t"}</TD><TD>{@code "A Quick Brown Fox"}</TD></TR> 148 * <TR><TD>{@code "\tA Lazy Dog."}</TD><TD>{@code "\tA Lazy Dog."}</TD></TR> 149 * <TR><TD>{@code " " (only white-space)}</TD><TD>{@code ""}</TD></TR> 150 * <TR><TD>{@code "" (empty-string)}</TD><TD>{@code ""}</TD></TR> 151 * <TR><TD>{@code null}</TD><TD>throws {@code NullPointerException}</TD></TR> 152 * </TABLE> 153 * 154 * @param s Any Java {@code String} 155 * 156 * @return A copy of the same {@code String} - <I>but all characters that matched Java 157 * method {@code java.lang.Character.isWhitespace(char)}</I> and were at the end of the 158 * {@code String} will not be included in the returned {@code String}. 159 * 160 * <BR /><BR />If the {@code zero-length String} is passed to parameter {@code 's'}, it 161 * shall be returned immediately. 162 * 163 * <BR /><BR />If the resultant-{@code String} has zero-length, it is returned, without 164 * exception. 165 */ 166 public static String trimRight(String s) 167 { 168 if (s.length() == 0) return s; 169 170 int pos = s.length(); 171 172 while ((pos > 0) && Character.isWhitespace(s.charAt(--pos))); 173 174 if (pos == 0) if (Character.isWhitespace(s.charAt(0))) return ""; 175 176 return s.substring(0, pos + 1); 177 } 178 179 /** 180 * Trims any white-space {@code Characters} from the beginning of a {@code String}. 181 * 182 * <TABLE CLASS=JDBriefTable> 183 * <TR><TH>Input String:</TH><TH>Output String:</TH></TR> 184 * <TR><TD>{@code "\t A Quick Brown Fox"}</TD><TD>{@code "A Quick Brown Fox"}</TD></TR> 185 * <TR><TD>{@code "A Lazy Dog. \n\r\t"}</TD><TD>{@code "A Lazy Dog. \n\r\t"}</TD></TR> 186 * <TR><TD>{@code " " (only white-space)}</TD><TD>{@code ""}</TD></TR> 187 * <TR><TD>{@code "" (empty-string)}</TD><TD>{@code ""}</TD></TR> 188 * <TR><TD>{@code null}</TD><TD>throws {@code NullPointerException}</TD></TR> 189 * </TABLE> 190 * 191 * @param s Any Java {@code String} 192 * 193 * @return A copy of the same {@code String} - <I>but all characters that matched Java 194 * method {@code java.lang.Character.isWhitespace(char)}</I> and were at the start of the 195 * {@code String} will not be included in the returned {@code String}. 196 * 197 * <BR /><BR />If the {@code zero-length String} is passed to parameter {@code 's'}, it 198 * shall be returned immediately. 199 * 200 * <BR /><BR />If the resultant-{@code String} has zero-length, it is returned, without 201 * exception. 202 */ 203 public static String trimLeft(String s) 204 { 205 int pos = 0; 206 int len = s.length(); 207 208 if (len == 0) return s; 209 210 while ((pos < len) && Character.isWhitespace(s.charAt(pos++))); 211 212 if (pos == len) if (Character.isWhitespace(s.charAt(len-1))) return ""; 213 214 return s.substring(pos - 1); 215 } 216 217 /** 218 * Primarily for convenience in remembering earlier C-style {@code printf(...)} formatting 219 * commands. 220 * 221 * <BR /><BR />This method will "left pad" an input {@code String} with spaces, if 222 * {@code s.length() < totalStrLength}. If input-parameter {@code 's'} is equal-to or 223 * longer-than the value in {@code 'totalStringLength'}, then the original {@code String} shall 224 * be returned. 225 * 226 * <TABLE CLASS=JDBriefTable> 227 * <TR><TH>Input Parameters</TH><TH>Returned String</TH></TR> 228 * <TR><TD>{@code "Quick Brown Fox"}<BR />{@code 20}</TD> 229 * <TD><PRE>{@code " Quick Brown Fox"}</PRE></TD> 230 * </TR> 231 * <TR><TD>{@code "Hello World"}<BR />{@code 15}</TD> 232 * <TD><PRE>{@code " Hello World"}</PRE></TD> 233 * </TR> 234 * <TR><TD>{@code "Write Once, Run Anywhere"}<BR />{@code 10}</TD> 235 * <TD>{@code "Write Once, Run Anywhere"}</TD> 236 * </TR> 237 * <TR><TD>{@code null}</TD><TD>{@code NullPointerException}</TD></TR> 238 * </TABLE> 239 * 240 * @param s This may be any {@code java.lang.String} 241 * 242 * @param totalStringLength If {@code s.length()} is smaller than {@code 'totalStringLength'}, 243 * then as many space characters ({@code ' '}) as are needed to ensure that the returned 244 * {@code 'String'} has length equal to {@code 'totalStringLength'} will be 245 * <B><I>prepended</B></I> to the input {@code String} parameter {@code 's'}. 246 * 247 * <BR /><BR />If {@code s.length()} is greater than {@code 'totalStringLength'}, then the 248 * original input shall be returned. 249 * 250 * @throws IllegalArgumentException If {@code totalStringLength} is zero or negative. 251 * 252 * @see #rightSpacePad(String, int) 253 */ 254 public static String leftSpacePad(String s, int totalStringLength) 255 { 256 if (totalStringLength <= 0) throw new IllegalArgumentException( 257 "totalString length was '" + totalStringLength + ", " + 258 "however it is expected to be a positive integer." 259 ); 260 261 return (s.length() >= totalStringLength) 262 ? s 263 : String.format("%1$" + totalStringLength + "s", s); 264 } 265 266 /** 267 * Primarily for convenience in remembering earlier C-style {@code printf(...)} formatting 268 * commands. 269 * 270 * <BR /><BR />This method will "right pad" an input {@code String} with spaces, if 271 * {@code s.length() < totalStrLength}. If input-parameter {@code 's'} is equal-to or 272 * longer-than the value in {@code 'totalStringLength'}, then the original {@code String} shall 273 * be returned. 274 * 275 * <TABLE CLASS=JDBriefTable> 276 * <TR><TH>Input Parameters</TH><TH>Returned String</TH></TR> 277 * <TR><TD>{@code "Quick Brown Fox"}<BR />{@code 20}</TD> 278 * <TD><PRE>{@code "Quick Brown Fox "}</PRE></TD> 279 * </TR> 280 * <TR><TD>{@code "Hello World"}<BR />{@code 15}</TD> 281 * <TD><PRE>{@code "Hello World "}</PRE></TD> 282 * </TR> 283 * <TR><TD>{@code "Write Once, Run Anywhere"}<BR />{@code 10}</TD> 284 * <TD>{@code "Write Once, Run Anywhere"}</TD> 285 * </TR> 286 * <TR><TD>{@code null}</TD><TD>{@code NullPointerException}</TD></TR> 287 * </TABLE> 288 * 289 * @param s This may be any {@code java.lang.String} 290 * 291 * @param totalStringLength If {@code s.length()} is smaller than {@code 'totalStringLength'}, 292 * then as many space characters ({@code ' '}) as are needed to ensure that the returned 293 * {@code 'String'} has length equal to {@code 'totalStringLength'} will be 294 * <B><I>postpended</B></I> to the input {@code String} parameter {@code 's'}. 295 * 296 * <BR /><BR />If {@code s.length()} is greater than {@code 'totalStringLength'}, then the 297 * original input shall be returned. 298 * 299 * @throws IllegalArgumentException If {@code totalStringLength} is zero or negative. 300 * 301 * @see #leftSpacePad(String, int) 302 */ 303 public static String rightSpacePad(String s, int totalStringLength) 304 { 305 if (totalStringLength <= 0) throw new IllegalArgumentException( 306 "totalString length was '" + totalStringLength + "', " + 307 "however it is expected to be a positive integer." 308 ); 309 310 return (s.length() >= totalStringLength) 311 ? s 312 : String.format("%1$-" + totalStringLength + "s", s); 313 } 314 315 /** 316 * Runs a Regular-Expression over a {@code String} to retrieve all matches that occur between 317 * input {@code String} parameter {@code 's'} and Regular-Expression {@code 'regEx'}. 318 * 319 * @param s Any Java {@code String} 320 * @param regEx Any Java Regular-Expression 321 * 322 * @param eliminateOverlappingMatches When this parameter is passed {@code 'TRUE'}, successive 323 * matches that have portions which overlap each-other are eliminated. 324 * 325 * @return An array of all {@code MatchResult's} (from package {@code 'java.util.regex.*'}) that 326 * were produced by iterating the {@code Matcher's} {@code 'find()'} method. 327 */ 328 public static MatchResult[] getAllMatches 329 (String s, Pattern regEx, boolean eliminateOverlappingMatches) 330 { 331 Stream.Builder<MatchResult> b = Stream.builder(); 332 Matcher m = regEx.matcher(s); 333 int prevEnd = 0; 334 335 while (m.find()) 336 { 337 MatchResult matchResult = m.toMatchResult(); 338 339 // This skip any / all overlapping matches - if the user has requested it 340 if (eliminateOverlappingMatches) if (matchResult.start() < prevEnd) continue; 341 342 b.accept(matchResult); 343 344 prevEnd = matchResult.end(); 345 } 346 347 // Convert the Java-Stream into a Java-Array and return the result 348 return b.build().toArray(MatchResult[]::new); 349 } 350 351 352 // ******************************************************************************************** 353 // ******************************************************************************************** 354 // Helper set & get for strings 355 // ******************************************************************************************** 356 // ******************************************************************************************** 357 358 359 /** 360 * This sets a character in a {@code String} to a new value, and returns a result 361 * @param str Any java {@code String} 362 * @param i An index into the underlying character array of that {@code String}. 363 * @param c A new character to be placed at the <I>i'th position</I> of this {@code String}. 364 * 365 * @return a new java {@code String}, with the appropriate index into the {@code String} 366 * substituted using character parameter {@code 'c'}. 367 */ 368 public static String setChar(String str, int i, char c) 369 { 370 return ((i + 1) < str.length()) 371 ? (str.substring(0, i) + c + str.substring(i + 1)) 372 : (str.substring(0, i) + c); 373 } 374 375 /** 376 * This removes a character from a {@code String}, and returns a new {@code String} as a 377 * result. 378 * 379 * @param str Any Java-{@code String}. 380 * 381 * @param i This is the index into the underlying java {@code char}-array whose character will 382 * be removed from the return {@code String}. 383 * 384 * @return Since Java {@code String}'s are all immutable, this {@code String} that is returned 385 * is completely new, with the character that was originally at index 'i' removed. 386 */ 387 public static String delChar(String str, int i) 388 { 389 if ((i + 1) < str.length()) 390 return str.substring(0, i) + str.substring(i + 1); 391 else 392 return str.substring(0, i); 393 } 394 395 /** 396 * Returns the same {@code String} is input, but trims all spaces down to a single space. 397 * Each and every <I>lone / independent or contiguous</I> white-space character is reduced 398 * to a single space-character. 399 * 400 * <TABLE CLASS=JDBriefTable> 401 * <TR><TH>Input String</TH><TH>Output String</TH></TR> 402 * <TR><TD><PRE>{@code "This has extra spaces\n"}</PRE></TD> 403 * <TD>{@code "This has extra spaces "}</TD> 404 * </TR> 405 * <TR><TD>{@code "This does not"}</TD> 406 * <TD>{@code "This does not"}</TD> 407 * </TR> 408 * <TR><TD>{@code "\tThis\nhas\ttabs\nand\tnewlines\n"}</TD> 409 * <TD>{@code " This has tabs and newlines "}</TD> 410 * </TR> 411 * </TABLE> 412 * 413 * @param s Any Java {@code String} 414 * 415 * @return A {@code String} where all white-space is compacted to a single space. This is 416 * generally how HTML works, when it is displayed in a browser. 417 */ 418 public static String removeDuplicateSpaces(String s) 419 { return StringParse.WHITE_SPACE_REGEX.matcher(s).replaceAll(" "); } 420 421 /** 422 * This string-modify method simply removes any and all white-space matches found within a 423 * java-{@code String}. 424 * 425 * <TABLE CLASS=JDBriefTable> 426 * <TR><TH>Input String</TH><TH>Output String</TH></TR> 427 * <TR><TD><PRE>{@code "This Has Extra Spaces\n"}</PRE></TD> 428 * <TD>{@code "ThisHasExtraSpaces"}</TD> 429 * </TR> 430 * <TR><TD>{@code "This Does Not"}</TD> 431 * <TD>{@code "ThisDoesNot"}</TD> 432 * </TR> 433 * <TR><TD>{@code "\tThis\nHas\tTabs\nAnd\tNewlines\n"}</TD> 434 * <TD>{@code "ThisHasTabsAndNewlines"}</TD> 435 * </TR> 436 * </TABLE> 437 * 438 * @param s Any {@code String}, but if it has any white-space (space that matches 439 * regular-expression: {@code \w+}) then those character-blocks will be removed 440 * 441 * @return A new {@code String} without any {@code \w} (RegEx for 'whitespace') 442 * 443 * @see #WHITE_SPACE_REGEX 444 */ 445 public static String removeWhiteSpace(String s) 446 { return WHITE_SPACE_REGEX.matcher(s).replaceAll(""); } 447 448 /** 449 * Generates a {@code String} that contains {@code n} copies of character {@code c}. 450 * @return {@code n} copies of {@code c}, as a {@code String}. 451 * @throws IllegalArgumentException If the value passed to parameter {@code 'n'} is negative 452 * @see StrSource#caretBeneath(String, int) 453 */ 454 public static String nChars(char c, int n) 455 { 456 if (n < 0) throw new IllegalArgumentException("Value of parameter 'n' is negative: " + n); 457 458 char[] cArr = new char[n]; 459 Arrays.fill(cArr, c); 460 return new String(cArr); 461 } 462 463 /** 464 * Generates a {@code String} that contains {@code n} copies of {@code s}. 465 * @return {@code n} copies of {@code s} as a {@code String}. 466 * @throws NException if the value provided to parameter {@code 'n'} is negative. 467 */ 468 public static String nStrings(String s, int n) 469 { 470 if (n < 0) throw new NException("A negative value was passed to 'n' [" + n + ']'); 471 472 StringBuilder sb = new StringBuilder(); 473 474 for (int i=0; i < n; i++) sb.append(s); 475 476 return sb.toString(); 477 } 478 479 /** 480 * This method checks whether or not a java-{@code String} has white-space. 481 * 482 * @param s Any Java-{@code String}. If this {@code String} has any white-space, this method 483 * will return {@code TRUE} 484 * 485 * @return {@code TRUE} If there is any white-space in this method, and {@code FALSE} otherwise. 486 * 487 * @see #WHITE_SPACE_REGEX 488 */ 489 public static boolean hasWhiteSpace(String s) 490 { return WHITE_SPACE_REGEX.matcher(s).find(); } 491 492 /** 493 * Counts the number of instances of character input {@code char c} contained by the 494 * input {@code String s} 495 * 496 * @param s Any {@code String} containing any combination of ASCII/UniCode characters 497 * 498 * @param c Any ASCII/UniCode character. 499 * 500 * @return The number of times {@code char c} occurs in {@code String s} 501 */ 502 public static int countCharacters(String s, char c) 503 { 504 int count = 0; 505 int pos = 0; 506 while ((pos = s.indexOf(c, pos + 1)) != -1) count++; 507 return count; 508 } 509 510 511 /** 512 * If the {@code String} passed to this method contains a single-quote on both sides of the 513 * {@code String}, or if it contains a double-quote on both sides of this {@code String}, then 514 * this method shall return a new {@code String} that is shorter in length by 2, and leaves off 515 * the first and last characters of the input parameter {@code String}. 516 * 517 * <BR /><BR /><B>HOPEFULLY,</B> The name of this method explains clearly what this method does 518 * 519 * @param s This may be any java {@code String}. Only {@code String's} whose first and last 520 * characters are not only quotation marks (single or double), but also they are <B>the same, 521 * identical, quotation marks on each side.</B> 522 * 523 * @return A new {@code String} that whose first and last quotation marks are gone - if they 524 * were there when this method began. 525 */ 526 public static String ifQuotesStripQuotes(String s) 527 { 528 if (s == null) return null; 529 if (s.length() < 2) return s; 530 531 int lenM1 = s.length() - 1; // Position of the last character in the String 532 533 if ( ((s.charAt(0) == '\"') && (s.charAt(lenM1) == '\"')) // String has Double-Quotation-Marks 534 || // ** or *** 535 ((s.charAt(0) == '\'') && (s.charAt(lenM1) == '\'')) ) // String has Single-Quotation-Marks 536 return s.substring(1, lenM1); 537 else 538 return s; 539 } 540 541 /** 542 * Counts the number of lines of text inside of a Java {@code String}. 543 * 544 * @param text This may be any text, as a {@code String}. 545 * 546 * @return Returns the number of lines of text. The integer returned shall be precisely 547 * equal to the number of {@code '\n'} characters <B><I>plus one!</I></B> 548 */ 549 public static int numLines(String text) 550 { 551 if (text.length() == 0) return 0; 552 553 int pos = -1; 554 int count = 0; 555 556 do 557 { 558 pos = text.indexOf('\n', pos + 1); 559 count++; 560 } 561 while (pos != -1); 562 563 return count; 564 } 565 566 567 // ******************************************************************************************** 568 // ******************************************************************************************** 569 // Misc Date String Functions 570 // ******************************************************************************************** 571 // ******************************************************************************************** 572 573 574 /** 575 * Converts an integer into a Month. I could just use the class {@code java.util.Calendar}, 576 * but it is so complicated, that using an internal list is easier. 577 * 578 * @param month The month, as a number from {@code '1'} to {@code '12'}. 579 * @return A month as a {@code String} like: {@code "January"} or {@code "August"} 580 * @see #months 581 */ 582 public static String monthStr(int month) { return months.get(month); } 583 584 /** 585 * Generates a "Date String" using the character separator {@code '.'} 586 * @return A {@code String} in the form: {@code YYYY.MM.DD} 587 */ 588 public static String dateStr() { return dateStr('.', false); } 589 590 /** 591 * Generates a "Date String" using the <I>separator</I> parameter as the separator between 592 * numbers 593 * 594 * @param separator Any ASCII or UniCode character. 595 * 596 * @return A {@code String} of the form: {@code YYYYcMMcDD} where {@code 'c'} is the passed 597 * {@code 'separator'} parameter. 598 */ 599 public static String dateStr(char separator) { return dateStr(separator, false); } 600 601 /** 602 * Generates a "Date String" that is consistent with the directory-name file-storage locations 603 * used to store articles from {@code http://Gov.CN}. 604 * 605 * @return The {@code String's} used for the Chinese Government Web-Portal Translation Pages 606 */ 607 public static String dateStrGOVCN() { return dateStr('/', false).replaceFirst("/", "-"); } 608 // "2017-12/05" 609 610 /** 611 * This class is primary included because although Java has a pretty reasonable "general 612 * purpose" calendar class/interface, but a consistent / same {@code String} since is needed 613 * because the primary use here is for building the names of files. 614 * 615 * @param separator Any ASCII or Uni-Code character. 616 * 617 * @param includeMonthName When <I>TRUE</I>, the English-Name of the month ({@code 'January'} 618 * ... {@code 'December'}) will be appended to the month number in the returned {@code String}. 619 * 620 * @return The year, month, and day as a {@code String}. 621 */ 622 public static String dateStr(char separator, boolean includeMonthName) 623 { 624 Calendar c = internalCalendar; 625 String m = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero! 626 String d = zeroPad10e2(c.get(Calendar.DAY_OF_MONTH)); 627 628 if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US); 629 630 if (separator != 0) return c.get(Calendar.YEAR) + "" + separator + m + separator + d; 631 else return c.get(Calendar.YEAR) + "" + m + d; 632 } 633 634 /** 635 * Returns a {@code String} that has the year and the month (but not the day, or other time 636 * components). 637 * 638 * @return Returns the current year and month as a {@code String}. 639 */ 640 public static String ymDateStr() { return ymDateStr('.', false); } 641 642 /** 643 * Returns a {@code String} that has the year and the month (but not the day, or other time 644 * components). 645 * 646 * @param separator The single-character separator used between year, month and day. 647 * 648 * @return The current year and month as a {@code String}. 649 */ 650 public static String ymDateStr(char separator) { return ymDateStr(separator, false); } 651 652 /** 653 * Returns a {@code String} that has the year and the month (but not the day, or other time 654 * components). 655 * 656 * @param separator The single-character separator used between year, month and day. 657 * 658 * @param includeMonthName When this is true, the name of the month, in English, is included 659 * with the return {@code String}. 660 * 661 * @return YYYYseparatorMM(? include-month-name) 662 */ 663 public static String ymDateStr(char separator, boolean includeMonthName) 664 { 665 Calendar c = internalCalendar; 666 String m = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero! 667 668 if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US); 669 670 if (separator != 0) return c.get(Calendar.YEAR) + "" + separator + m; 671 else return c.get(Calendar.YEAR) + "" + m; 672 } 673 674 675 // ******************************************************************************************** 676 // ******************************************************************************************** 677 // Misc Time String Functions 678 // ******************************************************************************************** 679 // ******************************************************************************************** 680 681 682 /** 683 * Returns the current time as a {@code String}. 684 * 685 * @return military time - with AM|PM (redundant) added too. 686 * Includes only Hour and Minute - separated by a colon character {@code ':'} 687 * 688 * @see #timeStr(char) 689 */ 690 public static String timeStr() { return timeStr(':'); } 691 692 /** 693 * Returns the current time as a {@code String}. 694 * 695 * @param separator The character used to separate the minute & hour fields 696 * 697 * @return military time - with AM|PM added redundantly, and a separator of your choosing. 698 */ 699 public static String timeStr(char separator) 700 { 701 Calendar c = internalCalendar; 702 int ht = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12); 703 String h = zeroPad10e2((ht == 0) ? 12 : ht); // 12:00 is represented as "0"... changes this... 704 String m = zeroPad10e2(c.get(Calendar.MINUTE)); 705 String p = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM"; 706 707 if (separator != 0) return h + separator + m + separator + p; 708 else return h + m + p; 709 } 710 711 /** 712 * Returns the current time as a {@code String}. This method uses all time components 713 * available. 714 * 715 * @return military time - with AM|PM added redundantly. 716 */ 717 public static String timeStrComplete() 718 { 719 Calendar c = internalCalendar; 720 int ht = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12); 721 String h = zeroPad10e2((ht == 0) ? 12 : ht); // 12:00 is represented as "0" 722 String m = zeroPad10e2(c.get(Calendar.MINUTE)); 723 String s = zeroPad10e2(c.get(Calendar.SECOND)); 724 String ms = zeroPad(c.get(Calendar.MILLISECOND)); 725 String p = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM"; 726 727 return h + '-' + m + '-' + p + '-' + s + '-' + ms + "ms"; 728 } 729 730 /** 731 * The words "ordinal indicator" are referring to the little character {@code String} that is 732 * often used in English to make a number seem more a part of an english sentence. 733 * 734 * @param i Any positive integer (greater than 0) 735 * 736 * @return This will return the following strings: 737 * 738 * <TABLE CLASS=JDBriefTable> 739 * <TR><TH>Input: </TH><TH>RETURNS:</TH></TR> 740 * <TR><TD>i = 1 </TD><TD>"st" (as in "1st","first") </TD></TR> 741 * <TR><TD>i = 2 </TD><TD>"nd" (as in "2nd", "second") </TD></TR> 742 * <TR><TD>i = 4 </TD><TD>"th" (as in "4th") </TD></TR> 743 * <TR><TD>i = 23 </TD><TD>"rd" (as in "23rd") </TD></TR> 744 * </TABLE> 745 * 746 * @throws IllegalArgumentException If i is negative, or zero 747 */ 748 public static String ordinalIndicator(int i) 749 { 750 if (i < 1) 751 throw new IllegalArgumentException("i: " + i + "\tshould be a natural number > 0."); 752 753 // Returns the last 2 digits of the number, or the number itself if it is less than 100. 754 // Any number greater than 100 - will not have the "text-ending" (1st, 2nd, 3rd..) affected 755 // by the digits after the first two digits. Just analyze the two least-significant digits 756 i = i % 100; 757 758 // All numbers between "4th" and "19th" end with "th" 759 if ((i > 3) && (i < 20)) return "th"; 760 761 // set i to be the least-significant digit of the number - if that number was 1, 2, or 3 762 i = i % 10; 763 764 // Obvious: English Rules. 765 if (i == 1) return "st"; 766 if (i == 2) return "nd"; 767 if (i == 3) return "rd"; 768 769 // Compiler is complaining. This statement should never be executed. 770 return "th"; 771 } 772 773 774 // ******************************************************************************************** 775 // ******************************************************************************************** 776 // Zero Padding stuff 777 // ******************************************************************************************** 778 // ******************************************************************************************** 779 780 781 /** 782 * This just zero-pads integers with "prepended" zero's. java.text has all kinds of extremely 783 * intricate zero-padding and text-formatting classes. However, here, these are generally used 784 * for <B>debug, line-number, or count</B> information that is printed to the UNIX terminal. 785 * When this is the case, a simple and easily remembered <I>'one line method'</I> is a lot more 786 * useful than all of the highly-scalable versions of the text-formatting classes in java.text. 787 * 788 * @param n Any Integer. If {@code 'n'} is negative or greater than 1,000 - then null is 789 * returned. 790 * 791 * @return A zero-padded {@code String} - <B><I>to precisely three orders of 10</I></B>, as in 792 * the example table below: 793 * 794 * <TABLE CLASS=JDBriefTable> 795 * <TR><TH>Input </TH><TH><I>RETURNS:</I></TH></TR> 796 * <TR><TD>n = 9 </TD><TD>"009"</TD></TR> 797 * <TR><TD>n = 99 </TD><TD>"099"</TD></TR> 798 * <TR><TD>n = 999 </TD><TD>"999"</TD></TR> 799 * <TR><TD>n = 9999 </TD><TD>null</TD></TR> 800 * <TR><TD>n = -10 </TD><TD>null</TD></TR> 801 * </TABLE> 802 * 803 * @see #zeroPad10e2(int) 804 * @see #zeroPad10e4(int) 805 */ 806 public static String zeroPad(int n) 807 { 808 if (n < 0) return null; 809 if (n < 10) return "00" + n; 810 if (n < 100) return "0" + n; 811 if (n < 1000) return "" + n; 812 return null; 813 } 814 815 /** 816 * Pads an integer such that it contains enough leading zero's to ensure a String-length of 817 * two. 818 * 819 * @param n Must be an integer between 0 and 99, or else null will be returned 820 * 821 * @return A zero-padded String of the integer, <B><I>to precisely two orders of 822 * 10</I></B><BR />. Null is returned if the number cannot fit within two spaces. Example 823 * table follows: 824 * 825 * <TABLE CLASS=JDBriefTable> 826 * <TR><TH>Input </TH><TH><I>RETURNS:</I></TH></TR> 827 * <TR><TD>n = 9 </TD><TD>"09"</TD></TR> 828 * <TR><TD>n = 99 </TD><TD>"99"</TD></TR> 829 * <TR><TD>n = 999 </TD><TD>null</TD></TR> 830 * <TR><TD>n = -10 </TD><TD>null</TD></TR> 831 * </TABLE> 832 * 833 * @see #zeroPad(int) 834 */ 835 public static String zeroPad10e2(int n) 836 { 837 if (n < 0) return null; 838 if (n < 10) return "0" + n; 839 if (n < 100) return "" + n; 840 return null; 841 } 842 843 /** 844 * Pads an integer such that it contains enough leading zero's to ensure a String-length of 845 * four. 846 * 847 * @param n Must be an integer between 0 and 9999, or else null will be returned 848 * 849 * @return A zero-padded String of the integer, <B><I>to precisely four orders of 10</I></B>. 850 * Null is returned if the number cannot fit within four spaces. Example table follows: 851 * 852 * <TABLE CLASS=JDBriefTable> 853 * <TR><TH>Input </TH><TH><I>RETURNS:</I></TH></TR> 854 * <TR><TD>n = 9 </TD><TD>"0009"</TD></TR> 855 * <TR><TD>n = 99 </TD><TD>"0099"</TD></TR> 856 * <TR><TD>n = 999 </TD><TD>"0999"</TD></TR> 857 * <TR><TD>n = 9999 </TD><TD>"9999" </TD></TR> 858 * <TR><TD>n = 99999 </TD><TD>null</TD></TR> 859 * <TR><TD>n = -10 </TD><TD>null</TD></TR> 860 * </TABLE> 861 * 862 * @see #zeroPad(int) 863 */ 864 public static String zeroPad10e4(int n) 865 { 866 if (n < 0) return null; 867 if (n < 10) return "000" + n; 868 if (n < 100) return "00" + n; 869 if (n < 1000) return "0" + n; 870 if (n < 10000) return "" + n; 871 return null; 872 } 873 874 /** 875 * Pad's an integer with leading zeroes into a {@code String}. The number of zeroes padded is 876 * equal to parameter {@code 'powerOf10'}. If {@code int 'powerOf10'} were equal to zero, then 877 * any integer passed to this function would return a {@code String} that was precisely three 878 * characters long. If the value of parameter {@code int 'n'} were larger than {@code 1,000} 879 * or negative, then null would be returned. 880 * 881 * @param n Must be an integer between {@code '0'} and {@code '9999'} where the number of 882 * {@code '9'} digits is equal to the value of parameter {@code int 'powerOf10'} 883 * 884 * @param powerOf10 This must be a positive integer greater than {@code '1'}. It may not be 885 * larger {@code '11'}. The largest value that any integer in Java may attain is 886 * {@code '2,147,483, 647'} 887 * 888 * @return A zero padded {@code String}. If a negative number is passed to parameter 889 * {@code 'n'}, then 'null' shall be returned. Null shall also be returned if the "Power of 10 890 * Exponent of parameter {@code n}" is greater than the integer-value of parameter 891 * {@code 'powerOf10'} 892 * 893 * <BR /><BR /><B>FOR INSTANCE:</B> a call to: {@code zeroPad(54321, 4);} would return null 894 * since the value of parameter {@code 'n'} has five-decimal-places, but {@code 'powerOf10'} is 895 * only 4! 896 * 897 * @throws IllegalArgumentException if the value parameter {@code 'powerOf10'} is less than 2, 898 * or greater than {@code 11}. 899 */ 900 public static String zeroPad(int n, int powerOf10) 901 { 902 if (n < 0) return null; // Negative Values of 'n' not allowed 903 904 char[] cArr = new char[powerOf10]; // The String's length will be equal to 'powerOf10' 905 String s = "" + n; // (or else 'null' would be returned) 906 int i = powerOf10 - 1; // Internal Loop variable 907 int j = s.length() - 1; // Internal Loop variable 908 909 Arrays.fill(cArr, '0'); // Initially, fill the output char-array with all 910 // zeros 911 912 while ((i >= 0) && (j >= 0)) // Now start filling that char array with the 913 cArr[i--] = s.charAt(j--); // actual number 914 915 if (j >= 0) return null; // if all of parameter 'n' was inserted into the 916 // output (number 'n' didn't fit) then powerOf10 917 // was insufficient, so return null. 918 919 return new String(cArr); 920 } 921 922 923 // ******************************************************************************************** 924 // ******************************************************************************************** 925 // Find / Front Last-Front-Slash 926 // ******************************************************************************************** 927 // ******************************************************************************************** 928 929 930 /** 931 * This function finds the position of the last "front-slash" character {@code '/'} in a 932 * java-{@code String} 933 * 934 * @param urlOrDir This is any java-{@code String}, but preferably one that is a 935 * {@code URL}, or directory. 936 * 937 * @return The {@code String}-index of the last 'front-slash' {@code '/'} position in a 938 * {@code String}, or {@code -1} if there are not front-slashes. 939 */ 940 public static int findLastFrontSlashPos(String urlOrDir) 941 { return urlOrDir.lastIndexOf('/'); } 942 943 /** 944 * This returns the contents of a {@code String}, after the last front-slash found. 945 * 946 * <BR /><BR /><B>NOTE:</B> If not front-slash {@code '/'} character is found, then the 947 * original {@code String} is returned. 948 * 949 * @param urlOrDir This is any java-{@code String}, but preferably one that is a 950 * {@code URL}, or directory. 951 * 952 * @return the portion of the {@code String} after the final front-slash {@code '/'} character. 953 * If there are no front-slash characters found in this {@code String}, then the original 954 * {@code String} shall be returned. 955 */ 956 public static String fromLastFrontSlashPos(String urlOrDir) 957 { 958 int pos = urlOrDir.lastIndexOf('/'); 959 if (pos == -1) return urlOrDir; 960 return urlOrDir.substring(pos + 1); 961 } 962 963 /** 964 * This returns the contents of a {@code String}, before the last front-slash found (including 965 * the front-slash {@code '/'} itself). 966 * 967 * <BR /><BR /><B>NOTE:</B> If no front-slash {@code '/'} character is found, then null is 968 * returned. 969 * 970 * @param urlOrDir This is any java-{@code String}, but preferably one that is a 971 * {@code URL}, or directory. 972 * 973 * @return the portion of the {@code String} <I><B>before and including</B></I> the final 974 * front-slash {@code '/'} character. If there are no front-slash characters found in this 975 * {@code String}, then null. 976 */ 977 public static String beforeLastFrontSlashPos(String urlOrDir) 978 { 979 int pos = urlOrDir.lastIndexOf('/'); 980 if (pos == -1) return null; 981 return urlOrDir.substring(0, pos + 1); 982 } 983 984 985 // ******************************************************************************************** 986 // ******************************************************************************************** 987 // Find / From Last-File-Separator 988 // ******************************************************************************************** 989 // ******************************************************************************************** 990 991 992 /** 993 * This function finds the position of the last {@code 'java.io.File.separator'} character in a 994 * java-{@code String}. In UNIX-based systems, this is a forward-slash {@code '/'} character, 995 * but in Windows-MSDOS, this is a back-slash {@code '\'} character. Identifying which of the 996 * two is used is obtained by "using" Java's {@code File.separator} class and field. 997 * 998 * @param fileOrDir This may be any Java-{@code String}, but preferably one that represents a 999 * file or directory. 1000 * 1001 * @return The {@code String}-index of the last 'file-separator' position in a {@code String}, 1002 * or {@code -1} if there are no such file-separators. 1003 */ 1004 public static int findLastFileSeparatorPos(String fileOrDir) 1005 { return fileOrDir.lastIndexOf(File.separator.charAt(0)); } 1006 1007 /** 1008 * This returns the contents of a {@code String}, after the last 1009 * {@code 'java.io.File.separator'} found. 1010 * 1011 * <BR /><BR /><B>NOTE:</B> If no {@code 'java.io.File.separator'} character is found, then 1012 * the original {@code String} is returned. 1013 * 1014 * @param fileOrDir This is any java-{@code String}, but preferably one that is a filename or 1015 * directory-name 1016 * 1017 * @return the portion of the {@code String} after the final {@code 'java.io.File.separator'} 1018 * character. If there are no such characters found, then the original {@code String} shall 1019 * be returned. 1020 */ 1021 public static String fromLastFileSeparatorPos(String fileOrDir) 1022 { 1023 int pos = fileOrDir.lastIndexOf(File.separator.charAt(0)); 1024 if (pos == -1) return fileOrDir; 1025 return fileOrDir.substring(pos + 1); 1026 } 1027 1028 /** 1029 * This returns the contents of a {@code String}, before the last 1030 * {@code 'java.io.File.separator'} (including the separator itself). 1031 * 1032 * <BR /><BR /><B>NOTE:</B> If no {@code 'java.io.File.separator'} character is found, 1033 * then null is returned. 1034 * 1035 * @param urlOrDir This is any java-{@code String}, but preferably one that is a 1036 * {@code URL}, or directory. 1037 * 1038 * @return the portion of the {@code String} <I><B>before and including</B></I> the final 1039 * {@code 'java.io.File.separator'} character. If there are no such characters found in this 1040 * {@code String}, then null is returned. 1041 */ 1042 public static String beforeLastFileSeparatorPos(String urlOrDir) 1043 { 1044 int pos = urlOrDir.lastIndexOf(File.separator.charAt(0)); 1045 if (pos == -1) return null; 1046 return urlOrDir.substring(0, pos + 1); 1047 } 1048 1049 1050 // ******************************************************************************************** 1051 // ******************************************************************************************** 1052 // Find / From File-Extension 1053 // ******************************************************************************************** 1054 // ******************************************************************************************** 1055 1056 1057 /** 1058 * This method swaps the ending 'File Extension' with another, parameter-provided, 1059 * extension. 1060 * 1061 * @param fileNameOrURLWithExtension Any file-name (or {@code URL}) that has an extension. 1062 * 1063 * @param newExtension The file or {@code URL} extension used as a substitute for the old 1064 * extension. This {@code String} may begin with the dot / period character ({@code '.'}), 1065 * and if it does not, one wil be appended. 1066 * 1067 * @return The new file-name or {@code URL} having the substituted extension. 1068 * 1069 * @throws StringFormatException If the {@code String} passed does not have any 1070 * {@code '.'} (period) characters, then this exception will throw. 1071 * 1072 * <BR /><BR /><B STYLE='color:red'>CAUTION:</B> In lieu of an exhaustive check on whether 1073 * or not the input file-name is a valid name, this method will simply check for the presence 1074 * or absence of a period-character ({@code '.'}). <I>Checking the validity of the input name 1075 * is <B>far</B> beyond the scope of this method.</I> 1076 * 1077 * <BR /><BR /><B>ALSO:</B> This method shall check to ensure that the {@code 'newExtension'} 1078 * parameter does not have length zero. 1079 * 1080 * <BR /><BR />To remove a file-extension, use {@link #removeExtension(String)} 1081 */ 1082 public static String swapExtension(String fileNameOrURLWithExtension, String newExtension) 1083 { 1084 int dotPos = fileNameOrURLWithExtension.lastIndexOf('.'); 1085 1086 if (dotPos == -1) throw new StringFormatException( 1087 "The file-name provided\n[" + fileNameOrURLWithExtension + "]\n" + 1088 "does not have a file-extension" 1089 ); 1090 1091 if (newExtension.length() == 0) throw new StringFormatException( 1092 "The new file-name extension has length 0. " + 1093 " To remove an extension, use 'StringParse.removeFileExtension(fileName)'" 1094 ); 1095 1096 return (newExtension.charAt(0) == '.') 1097 ? fileNameOrURLWithExtension.substring(0, dotPos) + newExtension 1098 : fileNameOrURLWithExtension.substring(0, dotPos) + '.' + newExtension; 1099 } 1100 1101 /** 1102 * This method simply removes all character data after the last identified period character 1103 * ({@code '.'}) found within {@code fileNameOrURL}. 1104 * 1105 * <BR /><BR />If the input-{@code String} does not have a period-character, the original 1106 * {@code String} will be returned, unmodified. 1107 * 1108 * @param fileNameOrURL Any file-name or {@code URL}, as a {@code String}. 1109 * 1110 * @return The modified file-name, or {@code URL}, as a {@code String}. 1111 * 1112 * <BR /><BR /><B STYLE='color:red'>NOTE:</B> No validity checks <I>of any kind</I> are 1113 * performed on {@code 'fileNameOrURL'}. This method merely checks for the presence or 1114 * absence of a {@code '.'} (period-character), and if it finds one, removes everything 1115 * after-and-including the last-period. 1116 */ 1117 public static String removeExtension(String fileNameOrURL) 1118 { 1119 int dotPos = fileNameOrURL.lastIndexOf('.'); 1120 if (dotPos == -1) return fileNameOrURL; 1121 return fileNameOrURL.substring(0, dotPos); 1122 } 1123 1124 /** 1125 * This will return the location within a {@code String} where the last period ({@code '.'}) 1126 * is found. 1127 * 1128 * <BR /><BR /><B>ALSO:</B> No validity checks for valid file-system names are performed. 1129 * Rather, the portion of the input-{@code String} starting at the location of the last period 1130 * is returned, regardless of what the {@code String} contains. 1131 * 1132 * @param file This may be any Java-{@code String}, but preferably one that represents a 1133 * file. 1134 * 1135 * @param includeDot When this parameter is passed {@code TRUE}, the position-index that is 1136 * returned will be the location of the last index where a period ({@code '.'}) is found. 1137 * When {@code FALSE}, the index returned will be the location of that period {@code + 1}. 1138 * 1139 * @return This will return the location of the file-extension. If no period is found, then 1140 * {@code -1} is returned. If the period is the last {@code char} in the {@code String}, 1141 * and parameter {@code 'includeDot'} is {@code FALSE}, then {@code -1} is returned. 1142 */ 1143 public static int findExtension(String file, boolean includeDot) 1144 { 1145 int pos = file.lastIndexOf('.'); 1146 1147 if (pos == -1) return -1; 1148 if (includeDot) return pos; 1149 1150 pos++; 1151 return (pos < file.length()) ? pos : -1; 1152 } 1153 1154 /** 1155 * This returns the contents of a {@code String}, after the last period {@code '.'} in that 1156 * {@code String}. For file-system and web files, this is often referred to as the <B>file 1157 * extension.</B> 1158 * 1159 * <BR /><BR /><B>NOTE:</B> If no period {@code '.'} character is found, then null is returned. 1160 * 1161 * <BR /><BR /><B>ALSO:</B> No validity checks for valid file-system names are performed. 1162 * Rather, the portion of the input-{@code String} starting at the location of the last period 1163 * is returned, regardless of what the {@code String} contains. 1164 * 1165 * @param file This is any java-{@code String}, but preferably one that is a filename. 1166 * 1167 * @param includeDot This determines whether the period {@code '.'} is to be included in the 1168 * returned-{@code String}. 1169 * 1170 * @return the portion of the {@code String} after the final period {@code '.'} character. 1171 * If parameter {@code includeDot} has been passed {@code FALSE}, then the portion of the 1172 * input-{@code String} beginning after the last period is returned. 1173 * 1174 * <BR /><BR />If there are no period characters found in this {@code String}, then null 1175 * is returned. 1176 */ 1177 public static String fromExtension(String file, boolean includeDot) 1178 { 1179 int pos = findExtension(file, includeDot); 1180 if (pos == -1) return null; 1181 return file.substring(pos); 1182 } 1183 1184 /** 1185 * This returns the contents of a {@code String}, before the last period {@code '.'} in that 1186 * {@code String}. For file-system and web files, this is often referred to as the <B>file 1187 * extension.</B> 1188 * 1189 * <BR /><BR /><B>NOTE:</B> If no period {@code '.'} character is found, then the original 1190 * {@code String} is returned. 1191 * 1192 * <BR /><BR /><B>ALSO:</B> No validity checks for valid file-system names are performed. 1193 * Rather, the portion of the input-{@code String} starting at the location of the last period 1194 * is returned, regardless of what the {@code String} contains. 1195 * 1196 * @param file This is any java-{@code String}, but preferably one that is a filename. 1197 * 1198 * @return the portion of the {@code String} before the final period {@code '.'} character. 1199 * 1200 * <BR /><BR />If there are no period characters found in this {@code String}, then the 1201 * original file is returned. 1202 */ 1203 public static String beforeExtension(String file) 1204 { 1205 int pos = file.lastIndexOf('.'); 1206 if (pos == -1) return file; 1207 return file.substring(0, pos); 1208 } 1209 1210 /** 1211 * This function returns the root URL-directory of a {@code String} 1212 * 1213 * <BR /><BR /><B>SPECIFICALLY:</B> it searches for the "last forward slash" in a 1214 * {@code String}, and returns a substring from position 0 to that point. If there aren't any 1215 * forward slashes in this {@code String}, null is returned. The front-slash itself is 1216 * included in the returned {@code String}. 1217 * 1218 * <BR /><BR /><B>NOTE:</B> It is similar to the old MS-DOS call to "DIR PART" 1219 * 1220 * @param url Any {@code String} that is intended to be an "Internet URL" - usually 1221 * http://domain/directory/[file] 1222 * 1223 * @return substring(0, index of last front-slash ({@code '/'}) in {@code String}) 1224 */ 1225 public static String findURLRoot(String url) 1226 { 1227 int pos = findLastFrontSlashPos(url); 1228 1229 if (pos == -1) return null; 1230 else return url.substring(0, pos + 1); 1231 } 1232 1233 /** 1234 * 1235 * @return After breaking the {@code String} by white-space, this returns the first 'chunk' 1236 * before the first whitespace. 1237 */ 1238 public static String firstWord(String s) 1239 { 1240 int pos = s.indexOf(" "); 1241 1242 if (pos == -1) return s; 1243 else return s.substring(0, pos); 1244 } 1245 1246 1247 // ******************************************************************************************** 1248 // ******************************************************************************************** 1249 // Removing parts of a string 1250 // ******************************************************************************************** 1251 // ******************************************************************************************** 1252 1253 1254 /** 1255 * This function will remove any pairs of Brackets within a {@code String}, and returned the 1256 * paired down {@code String} 1257 * 1258 * @param s Any {@code String}, which may or may not contain a "Bracket Pair" 1259 * 1260 * <BR /><BR /><B>For Example:</B> 1261 * 1262 * <BR /><BR /> 1263 * 1264 * <UL CLASS=JDUL> 1265 * <LI>This {@code String} does contain [a pair of brackets] within!</LI> 1266 * <LI>But this {@code String} does not.</LI> 1267 * </UL> 1268 * 1269 * @return The same {@code String}, but with any bracket-pairs removed. 1270 */ 1271 public static String removeBrackets(String s) { return remove_(s, '[', ']'); } 1272 1273 /** 1274 * Functions the same as {@code removeBrackets(String)} - but removes pairs of curly-braces, 1275 * instead<BR /> <B>NOTE:</B>These are { curly braces } that will be removed by this 1276 * {@code String}! 1277 * 1278 * @param s Any valid {@code String} { such as } - <I>(even this {@code String})</I>. 1279 * 1280 * <BR /><BR /><B>For Example:</B> 1281 * 1282 * <BR /><BR /> 1283 * 1284 * <UL CLASS=JDUL> 1285 * <LI>This {@code String} does contain {a pair of curly-braces} within!</LI> 1286 * <LI>But this {@code String} does not.</LI> 1287 * </UL> 1288 * 1289 * @return The same {@code String}, but with any curly-brace-pairs removed. 1290 * 1291 * @see #removeBrackets(String) 1292 */ 1293 public static String removeBraces(String s) { return remove_(s, '{', '}'); } 1294 1295 /** 1296 * Removes Parenthesis, similar to other parenthetical removing functions. 1297 * 1298 * @param s Any (valid) {@code String}. Below are sample inputs: 1299 * 1300 * <BR /><BR /><UL CLASS=JDUL> 1301 * <LI>This {@code String} does contain (a pair of parenthesis) within!</LI> 1302 * <LI>But this {@code String} does not.</LI> 1303 * </UL> 1304 * 1305 * @return The same {@code String}, but with any parenthesis removed. 1306 * 1307 * @see #removeBrackets(String) 1308 */ 1309 public static String removeParens(String s) { return remove_(s, '(', ')'); } 1310 1311 /** 1312 * Removes all parenthetical notations. Calls all <I><B>remove functions</B></I> 1313 * 1314 * @param s Any valid string 1315 * 1316 * @return The same string, but with all parenthesis, curly-brace & bracket pairs removed. 1317 * 1318 * @see #removeParens(String) 1319 * @see #removeBraces(String) 1320 * @see #removeBrackets(String) 1321 */ 1322 public static String removeAllParenthetical(String s) 1323 { return removeParens(removeBraces(removeBrackets(s))); } 1324 1325 private static String remove_(String s, char left, char right) 1326 { 1327 int p = s.indexOf(left); 1328 if (p == -1) return s; 1329 1330 String ret = s.substring(0, p).trim(); 1331 1332 for (++p; (s.charAt(p) != right) && (p < s.length()); p++); 1333 1334 if (p >= (s.length() - 1)) return ret; 1335 1336 ret += " " + s.substring(p + 1).trim(); 1337 1338 if (ret.indexOf(left) != -1) return remove_(ret.trim(), left, right); 1339 else return ret.trim(); 1340 } 1341 1342 1343 1344 // ******************************************************************************************** 1345 // ******************************************************************************************** 1346 // Base-64 Encoded Java Objects 1347 // ******************************************************************************************** 1348 // ******************************************************************************************** 1349 1350 1351 /** 1352 * This will convert any Serializable Java Object into a base-64 String. This {@code String} 1353 * may be saved, transmitted, <I>even e-mailed to another party, if you wish</I> and decoded 1354 * else-where. 1355 * 1356 * <BR /><BR /><B>REQUIREMENTS:</B> 1357 * 1358 * <BR /><BR /><OL CLASS=JDOL> 1359 * <LI> Object must implement the {@code interface java.io.Serializable}</LI> 1360 * 1361 * <LI> Receiving party or storage-device must have access to the {@code .jar file, or .class 1362 * file(s)} needed to instantiate that object! <I>(You must have shared your classes if 1363 * you intend to let other people de-serialize instances of that class)</I> 1364 * </LI> 1365 * </OL> 1366 * 1367 * @param o Any java {@code java.lang.Object}. This object must be Serializable, or else the 1368 * code will generate an exception. 1369 * 1370 * @return A {@code String} version of this object. It will be: 1371 * 1372 * <BR /><BR /><OL CLASS=JDOL> 1373 * <LI> Serialized using the {@code java.io.ObjectOutputStream(...)} <I>object-serialization 1374 * method</I> 1375 * </LI> 1376 * 1377 * <LI> Compressed using the {@code java.io.GZIPOutputStream(...)} <I>stream-compression 1378 * method</I> 1379 * </LI> 1380 * <LI>Encoded to a {@code String}, via Base-64 Encoding 1381 * {@code java.util.Base64.getEncoder()}</LI> 1382 * </OL> 1383 * 1384 * <BR /><B><SPAN STYLE="color: red">NOTE:</B></SPAN> Compression does not always make much 1385 * difference, however often times when doing web-scraping projects, there are large Java 1386 * {@code java.util.Vector<String>} filled with many lines of text, and these lists may be 1387 * instantly and easily saved using object-serialization. Furthermore, in these cases, the 1388 * compression will sometimes reduce file-size by an order of magnitude. 1389 * 1390 * @see #b64StrToObj(String) 1391 */ 1392 public static String objToB64Str(Object o) throws IOException 1393 { 1394 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 1395 GZIPOutputStream gzip = new GZIPOutputStream(bos); 1396 ObjectOutputStream oos = new ObjectOutputStream(gzip); 1397 1398 oos.writeObject(o); oos.flush(); gzip.finish(); oos.close(); bos.close(); 1399 1400 return Base64.getEncoder().encodeToString(bos.toByteArray()); 1401 } 1402 1403 /** 1404 * This converts <B><I>to</B></I> any <I><B>java.io.Serializable</B></I> object 1405 * <I><B>from</B></I> a compressed, serialized, Base-64 Encoded {@code java.lang.String}. This 1406 * method can be thought of as one which converts objects which have been previously encoded as 1407 * a {@code String}, and possibly even transmitted across the internet, back into an Java 1408 * {@code Object}. 1409 * 1410 * <BR /><BR /><B>REQUIREMENTS:</B> The {@code Object} that is to be instantiated must have its 1411 * class files accessible to the class-loader. This is the exact-same requirement expected by 1412 * all Java "de-serializations" routines. 1413 * 1414 * @param str Any previously Base-64 encoded, serialized, compressed {@code java.lang.Object'} 1415 * that has been saved as a {@code String}. That {@code String} should have been generated 1416 * using the {@code Programming.objToB64Str(Object o)} method in this class. 1417 * 1418 * <BR /><BR /><OL CLASS=JDOL> 1419 * <LI>Serialized using the {@code java.io.ObjectOutputStream(...)} <I>object-serialization 1420 * method</I></LI> 1421 * <LI>Compressed using the {@code java.io.GZIPOutputStream(...)} <I>sream-compression 1422 * method</I></LI> 1423 * <LI>Encoded to a {@code String}, via Base-64 Encoding 1424 * {@code java.util.Base64.getEncoder()}</LI> 1425 * </OL> 1426 * 1427 * <BR /><B><SPAN STYLE="color: red">NOTE:</B></SPAN> Compression does not always make much 1428 * difference, however often times when doing web-scraping projects, there are large Java 1429 * {@code java.util.Vector<String>} filled with many lines of text, and these lists may be 1430 * instantly and easily saved using object-serialization. Furthermore, in these cases, the 1431 * compression will sometimes reduce file-size by an order of magnitude. 1432 * 1433 * @return The de-compressed {@code java.lang.Object} converted back from a {@code String}. 1434 * 1435 * @see #objToB64Str(Object) 1436 */ 1437 public static Object b64StrToObj(String str) throws IOException 1438 { 1439 ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(str)); 1440 GZIPInputStream gzip = new GZIPInputStream(bis); 1441 ObjectInputStream ois = new ObjectInputStream(gzip); 1442 Object ret = null; 1443 1444 try 1445 { ret = ois.readObject(); } 1446 catch (ClassNotFoundException e) 1447 { 1448 throw new IOException( 1449 "There were no serialized objects found in your String. See e.getCause();", 1450 e 1451 ); 1452 } 1453 1454 bis.close(); ois.close(); 1455 return ret; 1456 } 1457 1458 /** 1459 * This performs an identical operation as the method: {@code objToB64Str}, however it 1460 * generates an output {@code String} that is "MIME" compatible. All this means is that the 1461 * {@code String} itself - <I>which could conceivable by thousands or even hundreds of 1462 * thousands of characters long</I> - will have {@code new-line characters} inserted such that 1463 * it may be printed on paper or included in a text-file that is (slightly) more 1464 * human-readable. Base64 MIME encoded {@code String's} look like very long paragraphs of 1465 * random-text data, while regular Base64-encodings are a single, very-long, {@code String} 1466 * with no space characters. 1467 * 1468 * @param o Any {@code java.lang.Object}. This object must be Serializable, or else the code 1469 * will generate an exception. 1470 * 1471 * @return A Base-64 MIME Encoded {@code String} version of any serializable 1472 * {@code java.lang.Object}. 1473 * 1474 * @see #objToB64Str(Object) 1475 * @see #b64MimeStrToObj(String) 1476 */ 1477 public static String objToB64MimeStr(Object o) throws IOException 1478 { 1479 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 1480 GZIPOutputStream gzip = new GZIPOutputStream(bos); 1481 ObjectOutputStream oos = new ObjectOutputStream(gzip); 1482 1483 oos.writeObject(o); oos.flush(); gzip.finish(); oos.close(); bos.close(); 1484 1485 return Base64.getMimeEncoder().encodeToString(bos.toByteArray()); 1486 } 1487 1488 /** 1489 * This performs an identical operation as the method: {@code b64StrToObj}, however receives a 1490 * "MIME" compatible encoded {@code String}. All this means is that the {@code String} itself 1491 * - <I>which could conceivable by thousands or even hundreds of thousands of characters 1492 * long</I> - will have {@code new-line characters} inserted such that it may be printed on 1493 * paper or included in a text-file that is (slightly) more human-readable. Base64 MIME 1494 * encoded {@code String's} look like very long paragraphs of random-text data, while regular 1495 * Base64 encodings a single, very-long, {@code String's}. 1496 * 1497 * @return The (de-serialized) java object that was read from the input parameter 1498 * {@code String 'str'} 1499 * 1500 * <BR /><BR /><B>REQUIREMENTS:</B> The object that is to be instantiated must have its class 1501 * files accessible to the class-loader. This is the exact-same requirement expected by all 1502 * Java "de-serializations" routines. 1503 * 1504 * @see #b64StrToObj(String) 1505 * @see #objToB64MimeStr(Object) 1506 */ 1507 public static Object b64MimeStrToObj(String str) throws IOException 1508 { 1509 ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getMimeDecoder().decode(str)); 1510 GZIPInputStream gzip = new GZIPInputStream(bis); 1511 ObjectInputStream ois = new ObjectInputStream(gzip); 1512 Object ret = null; 1513 1514 try 1515 { ret = ois.readObject(); } 1516 catch (ClassNotFoundException e) 1517 { 1518 throw new IOException( 1519 "There were no serialized objects found in your String. See e.getCause();", 1520 e 1521 ); 1522 } 1523 1524 bis.close(); ois.close(); 1525 return ret; 1526 } 1527 1528 1529 // ******************************************************************************************** 1530 // ******************************************************************************************** 1531 // '../' (Parent Directory) 1532 // ******************************************************************************************** 1533 // ******************************************************************************************** 1534 1535 1536 /** 1537 * Computes a "relative {@code URL String}". 1538 * 1539 * @param fileName This is a fileName whose ancestor directory needs to be 1540 * <I>'relative-ised'</I> 1541 * 1542 * @param ancestorDirectory This is an ancestor (container) directory. 1543 * 1544 * @param separator The separator character used to separate file-system directory names. 1545 * 1546 * @return This shall return the "../.." structure needed to insert a relative-{@code URL} or 1547 * link into a web-page. 1548 * 1549 * @throws IllegalArgumentException This exception shall throw if the separator character is 1550 * not one of the standard file & directory separators: forward-slash {@code '/'} or 1551 * back-slash {@code '\'}. 1552 * 1553 * <BR /><BR />This exception also throws if the {@code String} provided to parameter 1554 * {@code 'fileName'} does not begin-with the {@code String} provided to parameter 1555 * {@code 'ancestorDirectory'}. 1556 */ 1557 public static String dotDots(String fileName, String ancestorDirectory, char separator) 1558 { 1559 if ((separator != '/') && (separator != '\\')) throw new IllegalArgumentException( 1560 "The separator character provided to this method must be either a forward-slash '/' " + 1561 "or a back-slash ('\\') character. You have provided: ['" + separator + "']." 1562 ); 1563 1564 if (! fileName.startsWith(ancestorDirectory)) throw new IllegalArgumentException( 1565 "The file-name you have provided [" + fileName + "] is a String that does " + 1566 "start with the ancestorDirectory String [" + ancestorDirectory + "]. " + 1567 "Therefore there is no relative path using the dot-dot construct to the named " + 1568 "ancestor directory fromm the directory where the named file resides." 1569 ); 1570 1571 int levelsDeep = StringParse.countCharacters(fileName, separator) - 1572 StringParse.countCharacters(ancestorDirectory, separator); 1573 1574 String dotDots = ""; 1575 1576 while (levelsDeep-- > 0) dotDots = dotDots + ".." + separator; 1577 1578 return dotDots; 1579 } 1580 1581 /** 1582 * Convenience Method. 1583 * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)} 1584 * <BR />Converts: {@code URL} to {@code String}, eliminates non-essential 1585 * {@code URI}-information (Such as: {@code ASP, JSP, PHP Query-Strings, and others too}) 1586 * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's} 1587 * <BR />Passes: {@code '1'} to parameter {@code 'nLevels'} - only going up on directory 1588 */ 1589 public static String dotDotParentDirectory(URL url) 1590 { 1591 String urlStr = url.getProtocol() + "://" + url.getHost() + url.getPath(); 1592 return dotDotParentDirectory(urlStr, '/', (short) 1); 1593 } 1594 1595 /** 1596 * Convenience Method. 1597 * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)} 1598 * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's} 1599 * <BR />Passes: {@code '1'} to parameter {@code 'nLevels'} - only going up on directory 1600 */ 1601 public static String dotDotParentDirectory(String urlAsStr) 1602 { return dotDotParentDirectory(urlAsStr, '/', (short) 1); } 1603 1604 /** 1605 * Convenience Method. 1606 * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)} 1607 * <BR />Converts: {@code URL} to {@code String}, eliminates non-essential 1608 * {@code URI}-information (Such as: {@code ASP, JSP, PHP Query-Strings, and others too}) 1609 * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's} 1610 */ 1611 public static String dotDotParentDirectory(URL url, short nLevels) 1612 { 1613 String urlStr = url.getProtocol() + "://" + url.getHost() + url.getPath(); 1614 return dotDotParentDirectory(urlStr, '/', nLevels); 1615 } 1616 1617 /** 1618 * Convenience Method. 1619 * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)} 1620 * <BR />Passes: {@code char '/'}, the separator character used in {@code URL's} 1621 */ 1622 public static String dotDotParentDirectory(String urlAsStr, short nLevels) 1623 { return dotDotParentDirectory(urlAsStr, '/', nLevels); } 1624 1625 /** 1626 * Convenience Method. 1627 * <BR />Invokes: {@link #dotDotParentDirectory(String, char, short)}. 1628 * <BR />Passes: {@code '1'} to parameter {@code nLevels} - only going up one directory. 1629 */ 1630 public static String dotDotParentDirectory(String directoryStr, char dirSeparator) 1631 { return dotDotParentDirectory(directoryStr, dirSeparator, (short) 1); } 1632 1633 /** 1634 * This does traverses up a directory-tree structure, and returns a 'parent-level' directory 1635 * that is {@code 'nLevels'} up the tree. 1636 * 1637 * <BR /><BR /><B>NOTE:</B> The character used as the "File Separator" and/or "Directory 1638 * Separator" can be obtained using the field: {@code java.io.File.Separator.charAt(0).} The 1639 * class {@code java.io.File} provides access to the file-separator used by the file-system on 1640 * which the JVM is currently running, although it treats it as a multi-character 1641 * {@code String}. Just use the commonly-used java method {@code 'charAt(0)'} to obtain the 1642 * forward-slash {@code '/'} or backward-slash {@code '\'} character. 1643 * 1644 * <BR /><BR /><B><SPAN STYLE="color: red;">IMPORTANT:</B></SPAN> There is no error-checking 1645 * performed by this method regarding whether the input {@code String} represents a valid file 1646 * or directory. Instead, this method just looks for the <I><B>second from last 1647 * separator-character (usually a {@code '/'} forward-slash char)</B></I> and returns a 1648 * substring that starts at index 0, and continues to that position-plus-1 (in order to include 1649 * that second-to-last separator char). 1650 * 1651 * @param directoryStr This may be any java-{@code String}, although it is expected to be on 1652 * which represents the file & directory structure of file on the file-system. It may also 1653 * be {@code URL} for a web-site 1654 * 1655 * @param separator This is the separator currently used by that file & directory system. 1656 * If trying to find the parent directory of a {@code URL}, this should be the forward-slash 1657 * character {@code '/'}. 1658 * 1659 * @param nLevels This is how many "parent-level directories" (how many levels up the tree) 1660 * need to be computed. This parameter must '1' or greater. If the passed parameter 1661 * {@code 'directoryStr'} does not contain enough directories to traverse up the tree, then 1662 * this method will throw an {@code IllegalArgumentException}. 1663 * 1664 * @return a {@code String} that represents 'nLevels' up the directory tree, either for 1665 * a directory on the local-file system, or on a web-server from a Uniform Resource 1666 * Locator. 1667 * 1668 * @throws IllegalArgumentException If the value of parameter {@code short 'nLevels'} is 1669 * negative, or does not identify a number consistent with the number of directories that are 1670 * contained by the input urlAsStr parameter. 1671 * 1672 * <BR /><BR />This exception shall also throw if the {@code 'separator'} character is not one 1673 * of the standard file & directory separators: forward-slash {@code '/'} or back-slash 1674 * {@code '\'}. 1675 */ 1676 public static String dotDotParentDirectory(String directoryStr, char separator, short nLevels) 1677 { 1678 if (nLevels < 1) throw new IllegalArgumentException( 1679 "The parameter nLevels may not be less than 1, nor negative. You have passed: " + nLevels 1680 ); 1681 1682 if ((separator != '/') && (separator != '\\')) throw new IllegalArgumentException( 1683 "The separator character provided to this method must be either a forward-slash '/' " + 1684 "or a back-slash ('\\') character. You have provided: ['" + separator + "']." 1685 ); 1686 1687 int count = 0; 1688 1689 for (int i=directoryStr.length() - 1; i >= 0; i--) 1690 if (directoryStr.charAt(i) == separator) 1691 if (++count == (nLevels + 1)) 1692 return directoryStr.substring(0, i + 1); 1693 1694 throw new IllegalArgumentException( 1695 "The parameter nLevels was: " + nLevels + ", but unfortunately there only were: " + count + 1696 "'" + separator + "' characters found in the directory-string." 1697 ); 1698 } 1699 1700 1701 // ******************************************************************************************** 1702 // ******************************************************************************************** 1703 // Quick 'isNumber' methods 1704 // ******************************************************************************************** 1705 // ******************************************************************************************** 1706 1707 1708 /** 1709 * Determines, efficiently, whether an input {@code String} is also an integer. 1710 * 1711 * <BR /><BR /><B>NOTE:</B> A leading plus-sign ({@code '+'}) will, in fact, generate a 1712 * {@code FALSE} return-value for this method. 1713 * 1714 * @param s Any java {@code String} 1715 * 1716 * @return {@code TRUE} if the input {@code String} is any integer, and false otherwise. 1717 * 1718 * <BR /><BR /><B>NOTE:</B> This method does not check whether the number, itself, will 1719 * actually fit into a field or variable of type {@code 'int'}. For example, the input 1720 * {@code String '12345678901234567890'} (a very large integer), though an integer from a 1721 * mathematical perspective, is not a valid java {@code 'int'}. In such cases, {@code TRUE} 1722 * is returned, but if Java's {@code Integer.parseInt} method were subsequently used, that 1723 * method would throw an exception. 1724 * 1725 * <BR /><BR /><B>NOTE:</B> The primary purpose of this method is to avoid having to write 1726 * {@code try {} catch (NumberFormatException)} code-blocks. Furthermore, if only a check 1727 * is desired, and the {@code String} does not actually need to be converted to a number, 1728 * this is also more efficient than actually performing the conversion. 1729 * 1730 * @see #isInt(String) 1731 */ 1732 public static boolean isInteger(String s) 1733 { 1734 if (s == null) return false; 1735 1736 int length = s.length(); 1737 1738 if (length == 0) return false; 1739 1740 int i = 0; 1741 1742 if (s.charAt(0) == '-') 1743 { 1744 if (length == 1) return false; 1745 i = 1; 1746 } 1747 1748 while (i < length) 1749 { 1750 char c = s.charAt(i++); 1751 if (c < '0' || c > '9') return false; 1752 } 1753 1754 return true; 1755 } 1756 1757 /** 1758 * Convenience Method. 1759 * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])} 1760 * <BR />Passes: The ASCII characters that comprise {@code Integer.MIN_VALUE} 1761 */ 1762 public static boolean isInt(String s) 1763 { return isOfPrimitiveType(s, INT_MIN_VALUE_DIGITS_AS_CHARS); } 1764 1765 /** 1766 * Convenience Method. 1767 * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])} 1768 * <BR />Passes: The ASCII characters that comprise {@code Long.MIN_VALUE} 1769 */ 1770 public static boolean isLong(String s) 1771 { return isOfPrimitiveType(s, LONG_MIN_VALUE_DIGITS_AS_CHARS); } 1772 1773 /** 1774 * Convenience Method. 1775 * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])} 1776 * <BR />Passes: ASCII characters that comprise {@code Byte.MIN_VALUE} 1777 */ 1778 public static boolean isByte(String s) 1779 { return isOfPrimitiveType(s, BYTE_MIN_VALUE_DIGITS_AS_CHARS); } 1780 1781 /** 1782 * Convenience Method. 1783 * <BR />Invokes: {@link #isOfPrimitiveType(String, char[])} 1784 * <BR />Passes: ASCII characters that comprise {@code Short.MIN_VALUE} 1785 */ 1786 public static boolean isShort(String s) 1787 { return isOfPrimitiveType(s, SHORT_MIN_VALUE_DIGITS_AS_CHARS); } 1788 1789 1790 /** 1791 * Determines whether the input {@code String} is an integer in the range of Java's primitive 1792 * type specified by an input {@code char[]} array parameter. Specifically, if the the input 1793 * {@code String} is both a mathematical integer, and also an integer in the range of 1794 * {@code MIN_VALUE} and {@code MAX_VALUE} for that primitive-type and then (and only then) 1795 * will {@code TRUE} be returned. 1796 * 1797 * <BR /><BR /><B>NOTE:</B> The max and min values in which the range of valid integers 1798 * <B><I>must reside</I></B> (for primitive-type {@code 'int'}, for instance) are as below: 1799 * {@code -2147483648} ... {@code 2147483647}. 1800 * 1801 * <BR /><BR /><B>ALSO:</B> A leading plus-sign ({@code '+'}) will, in fact, generate a 1802 * {@code FALSE} return-value for this method. 1803 * 1804 * @param s Any Java {@code String} 1805 * 1806 * @param minArr The value of a Java Primitive {@code MIN_VALUE}, without the minus-sign, 1807 * represented as a {@code char[]} array. 1808 * 1809 * <TABLE CLASS=JDBriefTable> 1810 * <TR> <TH>Primitive Type</TH> <TH>Integer as ASCII {@code char[]} array</TH></TR> 1811 * <TR> <TD>{@code byte}</TD> <TD>{@code '2', '5', '6'}</TD></TR> 1812 * <TR> <TD>{@code short}</TD> <TD>{@code '6', '5', '5', '3', '6'}</TD></TR> 1813 * 1814 * <TR> <TD>{@code int}</TD> 1815 * <TD>{@code '2', '1', '4', '7,' '4', '8', '3', '6', '4', '8'}</TD> 1816 * </TR> 1817 * 1818 * <TR> <TD>{@code long}</TD> 1819 * <TD>{@code '2', '1', '4', '9', '2', '2', '3', '3', '7', '2', '0', '3', '6', '8', '5', 1820 * '4', '7', '7', '5', '8', '0', '8'}</TD> 1821 * </TR> 1822 * </TABLE> 1823 * 1824 * @return {@code TRUE} If the input {@code String} is both an integer, and also one which 1825 * falls in the range comprised by the specified Java Primitive Type. Return {@code FALSE} 1826 * otherwise. 1827 * 1828 * <BR /><BR /><B>NOTE:</B> The primary purpose of this method is to avoid having to write 1829 * {@code try {} catch (NumberFormatException)} code-blocks. Furthermore, if only a check 1830 * is desired, and the {@code String} does not actually need to be converted to a number, 1831 * this is also more efficient than actually performing the conversion. 1832 * 1833 * @see #isInteger(String) 1834 * @see #isInt(String) 1835 * @see #isByte(String) 1836 * @see #isLong(String) 1837 * @see #isShort(String) 1838 */ 1839 protected static boolean isOfPrimitiveType(String s, char[] minArr) 1840 { 1841 int length = s.length(); 1842 1843 // Zero length string's are not valid integers. 1844 if (length == 0) return false; 1845 1846 // A negative integer may begin with a minus-sign. 1847 boolean negative = s.charAt(0) == '-'; 1848 1849 // **************************************************************************************** 1850 // If the string is too short or too long, this method doesn't need to do any work. 1851 // We either know the answer immediately (too long), or we can call the simpler method 1852 // (in the case that it is too short) 1853 // **************************************************************************************** 1854 1855 // If a string is shorter than (for type 'int', for example): 2147483647 (10 chars) 1856 // then we ought use the simplified method which just checks if the string is an integer. 1857 if (length < minArr.length) return isInteger(s); 1858 1859 // If the string is longer than (for type 'int', for example): -2147483648 (11 chars) 1860 // then it cannot be an integer that fits into primitive 'int', so return false. 1861 if (length > (minArr.length + 1)) return false; 1862 1863 // If the String is *EXACTLY* 11 characters long (for primitive-type 'int', for example), 1864 // but doesn't begin with a negative sign, we also know the answer immediately. 1865 if ((!negative) && (length == (minArr.length + 1))) return false; 1866 1867 // If the String *EXACTLY* the length of MAX_NUUMBER, but it begins with a negative sign, 1868 // we can call the simplified method, instead as well. 1869 if (negative && (length == minArr.length)) return isInteger(s); 1870 1871 // The **REST** of the code is only executed if the numeric part of the String 1872 // (Specifically: leaving out the '-' negative sign, which may or may not be present) 1873 // ... if the numeric part of the String is precisely the length of MAX_VALUE / MAX_NUMBER 1874 // as determined by the length of the array 'minArr'... If the input string is 1875 // **PRECISELY** that length, then the string must be checked in the loop below. 1876 1877 int i = negative ? 1 : 0; 1878 int j = 0; 1879 boolean guaranteedFitIfInteger = false; 1880 char c = 0; 1881 1882 while (i < length) 1883 { 1884 c = s.charAt(i); 1885 1886 if (! guaranteedFitIfInteger) 1887 { 1888 if (c > minArr[j]) return false; 1889 if (c < minArr[j]) guaranteedFitIfInteger = true; 1890 } 1891 1892 if (c < '0') return false; 1893 if (c > '9') return false; 1894 1895 i++; j++; 1896 } 1897 1898 // THE COMMENT BELOW DELINEATES WHAT HAPPENS FOR THE INPUT-CASE OF PRIMITIVE-TYPE 'INT' 1899 // (2147483648)... But it generalizes for byte, short, and long as well. 1900 1901 // This might seem very strange. Since the MIN_VALUE ends with an '8', but the 1902 // MAX_VALUE ends with a '7', and since we are checking each character to see that 1903 // it falls within the array above, **RATHER THAN** just returning TRUE right here, 1904 // we have to catch the **LONE** border/edge case where some joker actually passed the 1905 // String 2147483648 - which must return FALSE, since the last positive integer is 1906 // 2147483647 (see that it has an ending of '7', rather than an '8'). 1907 1908 return guaranteedFitIfInteger || negative || (c != minArr[minArr.length-1]); 1909 } 1910 1911 private static final String Digits = "(\\p{Digit}+)"; 1912 private static final String HexDigits = "(\\p{XDigit}+)"; 1913 1914 // an exponent is 'e' or 'E' followed by an optionally 1915 // signed decimal integer. 1916 1917 private static final String Exp = "[eE][+-]?"+Digits; 1918 1919 /** 1920 * A Predicate which uses a regular-expression for checking whether a {@code String} is a valid 1921 * & parseable {@code double}, which is guaranteed not to throw a 1922 * {@code NumberFormatException} when using the parser {@code Double.parseDouble}. 1923 * 1924 * <BR /><BR /><SPAN CLASS=CopiedJDK>The Following Description is Directly Copied From: 1925 * {@code java.lang.Double.valueOf(String)}, <B>JDK 1.8</B></SPAN> 1926 * 1927 * <EMBED CLASS='external-html' DATA-FILE-ID=STRP_D_VALUEOF> 1928 * 1929 * @see #floatingPointPred 1930 * @see #isDouble(String) 1931 */ 1932 public static final Pattern FLOATING_POINT_REGEX = Pattern.compile( 1933 // NOTE: Digits, HexDigits & Exp defined ABOVE 1934 1935 "[\\x00-\\x20]*"+ // Optional leading "whitespace" 1936 "[+-]?(" + // Optional sign character 1937 "NaN|" + // "NaN" string 1938 "Infinity|" + // "Infinity" string 1939 1940 // A decimal floating-point string representing a finite positive 1941 // number without a leading sign has at most five basic pieces: 1942 // Digits . Digits ExponentPart FloatTypeSuffix 1943 // 1944 // Since this method allows integer-only strings as input 1945 // in addition to strings of floating-point literals, the 1946 // two sub-patterns below are simplifications of the grammar 1947 // productions from section 3.10.2 of 1948 // The Java Language Specification. 1949 1950 // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt 1951 "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ 1952 1953 // . Digits ExponentPart_opt FloatTypeSuffix_opt 1954 "(\\.("+Digits+")("+Exp+")?)|"+ 1955 1956 // Hexadecimal strings 1957 "((" + 1958 1959 // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt 1960 "(0[xX]" + HexDigits + "(\\.)?)|" + 1961 1962 // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt 1963 "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + 1964 1965 ")[pP][+-]?" + Digits + "))" + 1966 "[fFdD]?))" + 1967 "[\\x00-\\x20]*" 1968 // Optional trailing "whitespace"; 1969 ); 1970 1971 /** 1972 * This is the floating-point regular-expression, simply converted to a predicate. 1973 * @see #FLOATING_POINT_REGEX 1974 * @see #isDouble(String) 1975 */ 1976 public static final Predicate<String> floatingPointPred = FLOATING_POINT_REGEX.asPredicate(); 1977 1978 /** 1979 * Tests whether an input-{@code String} can be parsed into a {@code double}, without throwing 1980 * an exception. 1981 * 1982 * @return {@code TRUE} <I>if and only if</I> calling {@code Double.valueOf(s)} (or 1983 * {@code Double.parseDouble(s)}) is guaranteed to produce a result, without throwing a 1984 * {@code NumberFormatException}. 1985 * 1986 * <BR /><BR /><B STYLE='color: red;'>NOTE:</B> Whenever analyzing performance and 1987 * optimizations, it is important to know just "how costly" (as an order of magnitude) a 1988 * certain operation really is. Constructors, for instance, that don't allocated much memory 1989 * can be two orders of magnitude <I>less costly than</I> the JRE's costs for creating the 1990 * {@code StackTrace} object when an exception (such as {@code NumberFormatException}) is 1991 * thrown. 1992 * 1993 * <BR /><BR />Though it costs "extra" to check whether a {@code String} can be parsed by the 1994 * Double-String Parser, if the programmer expects that exceptions will occasionally occur, the 1995 * amount of time saved by checking a {@code String} before parsing it as a Double-String will 1996 * actually save time - <I>even if only 1 in 500 of those {@code String's} are invalid and 1997 * would throw the exception, causing a {@code StackTrace} constructor to be invoked.</I> 1998 * 1999 * @see #FLOATING_POINT_REGEX 2000 * @see #floatingPointPred 2001 */ 2002 public static boolean isDouble(String s) 2003 { return floatingPointPred.test(s); } 2004}