001package Torello.Java; 002 003import Torello.JavaDoc.LinkJavaSource; 004 005import Torello.Java.ReadOnly.ReadOnlyArrayList; 006import Torello.Java.ReadOnly.ReadOnlyList; 007 008import Torello.Java.Function.IntTFunction; 009import static Torello.Java.C.RESET; 010 011import java.util.Calendar; 012import java.util.Locale; 013import java.util.Arrays; 014 015import java.text.DecimalFormat; 016 017/** 018 * This class provides several {@code String} printing utilities such as abbreviation and list 019 * printing. 020 */ 021@Torello.JavaDoc.StaticFunctional 022public class StrPrint 023{ 024 private StrPrint() { } 025 026 027 // ******************************************************************************************** 028 // ******************************************************************************************** 029 // HELPER & BASIC 030 // ******************************************************************************************** 031 // ******************************************************************************************** 032 033 034 /** 035 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_NLAT_DESC> 036 * 037 * @param s Any {@code java.lang.String}, preferably one with multiple lines of text. 038 * 039 * @return A {@code String}, where each line of text has been "trimmed", and the two 040 * character sequence {@code "\\n"} inserted in-between each line. 041 * 042 * @see #abbrevStartRDSF(String, int, boolean) 043 * @see #abbrevEndRDSF(String, int, boolean) 044 */ 045 public static String newLinesAsText(String s) 046 { 047 return s 048 .replaceAll( 049 // White-Space-Except-Newline, THEN newline, THEN White-SpaceExcept-Newline 050 "[ \t\r\f\b]*\n[ \t\r\f\b]*", 051 052 // Replace Each Occurence of that with: 053 // == COMPILES-TO ==> "\\n" == REG-EX-READS ==> BackSlash and letter 'n' 054 "\\\\n" 055 ) 056 // == COMPILES-TO ==> "\s+" == REG-EX-READS ==> 'spaces' 057 .replaceAll("\\s+", " ") 058 059 // Don't forget about leading and trailing stuff... 060 .trim(); 061 } 062 063 064 // ******************************************************************************************** 065 // ******************************************************************************************** 066 // Abbreviating Text, with "newLinesAsText" - Helper 067 // ******************************************************************************************** 068 // ******************************************************************************************** 069 070 071 /** 072 * Convenience Method. 073 * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B> 074 * <BR />Invokes: {@link StringParse#removeDuplicateSpaces(String)} 075 * <BR />Or Invokes: {@link #newLinesAsText(String)} 076 * <BR />Finally: {@link #abbrevStart(String, boolean, int)} 077 */ 078 public static String abbrevStartRDSF 079 (String s, int maxLength, boolean seeEscapedNewLinesAsText) 080 { 081 // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios 082 // of this conditional-statement, the new-lines have already been removed by the previous 083 // method call. 084 // 085 // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines 086 087 return seeEscapedNewLinesAsText 088 ? abbrevStart(newLinesAsText(s), false, maxLength) 089 : abbrevStart(StringParse.removeDuplicateSpaces(s), false, maxLength); 090 } 091 092 /** 093 * Convenience Method. 094 * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B> 095 * <BR />Invokes: {@link StringParse#removeDuplicateSpaces(String)} 096 * <BR />Or Invokes: {@link #newLinesAsText(String)} 097 * <BR />Finally: {@link #abbrevEnd(String, boolean, int)} 098 */ 099 public static String abbrevEndRDSF 100 (String s, int maxLength, boolean seeEscapedNewLinesAsText) 101 { 102 // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios 103 // of this conditional-statement, the new-lines have already been removed by the previous 104 // method call. 105 // 106 // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines 107 108 return seeEscapedNewLinesAsText 109 ? abbrevEnd(newLinesAsText(s), false, maxLength) 110 : abbrevEnd(StringParse.removeDuplicateSpaces(s), false, maxLength); 111 } 112 113 /** 114 * Convenience Method. 115 * <BR />Passes: {@code '0'} to parameter {@code 'abbrevPos'}, forcing the abbreviation to 116 * occur at the <B>start</B> of the {@code String} (if long enough to be abbreviated) 117 * <BR />See Documentation: {@link #abbrev(String, int, boolean, String, int)} 118 */ 119 @LinkJavaSource(handle="Abbrev", name="print1") 120 public static String abbrevStart(String s, boolean escNewLines, int maxLength) 121 { return Abbrev.print1(s, 0, escNewLines, null, maxLength); } 122 123 /** 124 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_1_DESC> 125 * @param s This may be any Java (non-null) {@code String} 126 * 127 * @param abbrevPos This parameter is used to indicate where the abbreviation-{@code String} 128 * should occur - <I>if this {@code String 's'} is long enough to be abbreviated.</I> For 129 * instance, if {@code '0'} (zero) were passed to this parameter, and {@code 's'} were longer 130 * than parameter {@code 'maxLength'}, then an ellipsis would be appended to the beginning of 131 * the returned-{@code 'String'}. (Or, if some other {@code 'abbrevStr'} were specified, that 132 * other abbreviation would be appended to the beginning of the returned-{@code String}) 133 * 134 * @param escapeNewLines <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL> 135 * @param abbrevStr <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR> 136 * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL> 137 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_1_RET> 138 * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_1_IAEX> 139 */ 140 @LinkJavaSource(handle="Abbrev", name="print1") 141 public static String abbrev( 142 String s, 143 int abbrevPos, 144 boolean escapeNewLines, 145 String abbrevStr, 146 int maxLength 147 ) 148 { return Abbrev.print1(s, abbrevPos, escapeNewLines, abbrevStr, maxLength); } 149 150 /** 151 * Convenience Method. 152 * <BR />Parameter: {@code spaceBeforeAbbrev} set to {@code FALSE} 153 * <BR />Abbreviates: Default ellipsis ({@code '...'}) are placed at the end of the 154 * {@code String} 155 * <BR />See Documentation: {@link #abbrev(String, boolean, boolean, String, int)} 156 */ 157 @LinkJavaSource(handle="Abbrev", name="print2") 158 public static String abbrevEnd(String s, boolean escapeNewLines, int maxLength) 159 { return Abbrev.print2(s, false, escapeNewLines, null, maxLength); } 160 161 /** 162 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_2_DESC> 163 * @param s This may be any Java (non-null) {@code String} 164 * 165 * @param spaceBeforeAbbrev This ensures that for whatever variant of ellipsis being used, the 166 * space-character is inserted directly before appending the ellipsis {@code "..."} or the 167 * user-provided {@code 'abbrevStr'}. 168 * 169 * @param escapeNewLines <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL> 170 * @param abbrevStr <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR> 171 * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL> 172 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_2_RET> 173 * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_2_IAEX> 174 */ 175 @LinkJavaSource(handle="Abbrev", name="print2") 176 public static String abbrev( 177 String s, 178 boolean spaceBeforeAbbrev, 179 boolean escapeNewLines, 180 String abbrevStr, 181 int maxLength 182 ) 183 { return Abbrev.print2(s, spaceBeforeAbbrev, escapeNewLines, abbrevStr, maxLength); } 184 185 186 // ******************************************************************************************** 187 // ******************************************************************************************** 188 // Abbreviated List Printing 189 // ******************************************************************************************** 190 // ******************************************************************************************** 191 192 193 /** 194 * Convenience Method. 195 * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'} 196 * <BR />See Documentation: {@link #printListAbbrev(Iterable, IntTFunction, int, int, boolean, 197 * boolean, boolean)} 198 */ 199 @LinkJavaSource(handle="PrintListAbbrev", name="print1") 200 public static <ELEM> String printListAbbrev( 201 Iterable<ELEM> list, int lineWidth, int indentation, boolean seeEscapedNewLinesAsText, 202 boolean printNulls, boolean showLineNumbers 203 ) 204 { 205 return PrintListAbbrev.print1( 206 list, (int i, Object o) -> o.toString(), lineWidth, indentation, 207 seeEscapedNewLinesAsText, printNulls, showLineNumbers 208 ); 209 } 210 211 /** 212 * <EMBED CLASS=defs DATA-LIST_TYPE=Array> 213 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION> 214 * @param list Any iterable-list of Java Object's 215 * 216 * @param listItemPrinter 217 * <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LIST_ITEM_PR> 218 * 219 * @param lineWidth <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LINE_WIDTH> 220 * @param indentation <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_INDENTATION> 221 * @param seeEscapedNewLinesAsText <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SEE_ESC_NL> 222 * @param printNulls <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_PRINT_NULLS> 223 * @param showLineNumbers <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SHOW_LNUMS> 224 * @return <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_RETURNS> 225 * 226 * @see #zeroPad10e2(int) 227 * @see #abbrevEndRDSF(String, int, boolean) 228 * @see StringParse#nChars(char, int) 229 * @see StringParse#trimLeft(String) 230 */ 231 @LinkJavaSource(handle="PrintListAbbrev", name="print1") 232 public static <ELEM> String printListAbbrev( 233 Iterable<ELEM> list, 234 IntTFunction<? super ELEM, String> listItemPrinter, 235 int lineWidth, 236 int indentation, 237 boolean seeEscapedNewLinesAsText, 238 boolean printNulls, 239 boolean showLineNumbers 240 ) 241 { 242 return PrintListAbbrev.print1( 243 list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText, 244 printNulls, showLineNumbers 245 ); 246 } 247 248 /** 249 * Convenience Method. 250 * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'} 251 * <BR />See Documentation: {@link #printListAbbrev(Object[], IntTFunction, int, int, boolean, 252 * boolean, boolean)} 253 */ 254 @LinkJavaSource(handle="PrintListAbbrev", name="print2") 255 public static <ELEM> String printListAbbrev( 256 ELEM[] arr, 257 int lineWidth, 258 int indentation, 259 boolean seeEscapedNewLinesAsText, 260 boolean printNulls, 261 boolean showLineNumbers 262 ) 263 { 264 return PrintListAbbrev.print2( 265 arr, (int i, Object o) -> o.toString(), lineWidth, indentation, 266 seeEscapedNewLinesAsText, printNulls, showLineNumbers 267 ); 268 } 269 270 /** 271 * <EMBED CLASS=defs DATA-LIST_TYPE=Array> 272 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION> 273 * @param list Any iterable-list of Java Object's 274 * 275 * @param listItemPrinter 276 * <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LIST_ITEM_PR> 277 * 278 * @param lineWidth <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_LINE_WIDTH> 279 * @param indentation <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_INDENTATION> 280 * @param seeEscapedNewLinesAsText <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SEE_ESC_NL> 281 * @param printNulls <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_PRINT_NULLS> 282 * @param showLineNumbers <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_SHOW_LNUMS> 283 * @return <EMBED CLASS=external-html DATA-FILE-ID=SP_PLA_RETURNS> 284 * 285 * @see #zeroPad10e2(int) 286 * @see #abbrevEndRDSF(String, int, boolean) 287 * @see StringParse#trimLeft(String) 288 */ 289 @LinkJavaSource(handle="PrintListAbbrev", name="print2") 290 public static <ELEM> String printListAbbrev( 291 ELEM[] list, 292 IntTFunction<? super ELEM, String> listItemPrinter, 293 int lineWidth, 294 int indentation, 295 boolean seeEscapedNewLinesAsText, 296 boolean printNulls, 297 boolean showLineNumbers 298 ) 299 { 300 return PrintListAbbrev.print2( 301 list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText, 302 printNulls, showLineNumbers 303 ); 304 } 305 306 307 // ******************************************************************************************** 308 // ******************************************************************************************** 309 // Line(s) of Text 310 // ******************************************************************************************** 311 // ******************************************************************************************** 312 313 314 /** 315 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINE_OR_DESC> 316 * 317 * @param s This may be any valid Java {@code String}. It ought to be some variant of a 318 * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both 319 * directions, left and right</I> - from parameter {@code int 'pos'} and parameter 320 * {@code int 'len'} 321 * 322 * @param pos This is a position in the input-parameter {@code String 's'}. The nearest 323 * {@code '\n'} (new-line) character, <I>to the left</I> of this position, will be found and 324 * identified. If the {@code char} at {@code s.charAt(pos)} is, itself, a {@code '\n'} 325 * (newline) character, then no left-direction search will be performed. The left-most 326 * position of the returned substring would then be {@code pos + 1}. 327 * 328 * @param len The search for the 'right-most' {@code '\n'} (newline-character) will begin at 329 * position {@code 'len'}. If the character at {@code s.charAt(pos + len)} is, itself, a 330 * new-line character, then no right-direction search will be performed. The right-most 331 * position of the returned substring would be {@code pos + len - 1}. 332 * 333 * @param unixColorCode If this {@code String} is null, it will be ignored. If this 334 * {@code String} is non-null, it will be inserted before the "Matching {@code String}" 335 * indicated by the index-boundaries {@code pos} <I>TO</I> {@code pos + len}. 336 * 337 * <BR /><BR /><DIV CLASS=JDHint> 338 * <B STYLE='color:red;'>Note:</B> No Validity Check shall be performed on this {@code String}, 339 * and the user is not obligated to provide a {@link C} valid UNIX Color-Code {@code String}. 340 * Also, a closing {@code C.RESET} is inserted after the terminus of the match. 341 * </DIV> 342 * 343 * @return The {@code String} demarcated by the first new-line character PLUS 1 344 * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1 345 * <I><B>AFTER</I></B> index {@code pos + len}. 346 * 347 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINES_RET> 348 * 349 * @throws StringIndexOutOfBoundsException If either {@code 'pos'}, or {@code 'pos + len'} are 350 * not within the bounds of the input {@code String 's'} 351 * 352 * @throws IllegalArgumentException If the value passed to parameter {@code 'len'} is zero or 353 * negative. 354 */ 355 public static String lineOrLines(String s, int pos, int len, String unixColorCode) 356 { 357 if ((pos >= s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException( 358 "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end " + 359 "of String 's', which has length [" + s.length() + "]" 360 ); 361 362 if (len <= 0) throw new IllegalArgumentException 363 ("The value passed to parameter 'len' [" + len + "], may not be negative."); 364 365 if ((pos + len) > s.length()) throw new StringIndexOutOfBoundsException( 366 "The total of parameter 'pos' [" + pos + "], and parameter 'len' [" + len + "], is: " + 367 "[" + (pos + len) + "]. Unfortunately, String parameter 's' only has length " + 368 "[" + s.length() + "]" 369 ); 370 371 int linesStart, linesEnd, temp; 372 373 if (pos == 0) linesStart = 0; 374 else if (s.charAt(pos) == '\n') linesStart = pos + 1; 375 else if ((temp = StrIndexOf.left(s, pos, '\n')) != -1) linesStart = temp + 1; 376 else linesStart = 0; 377 378 if ((pos + len) == s.length()) linesEnd = s.length(); 379 else if (s.charAt(pos + len) == '\n') linesEnd = pos + len; 380 else if ((temp = s.indexOf('\n', pos + len)) != -1) linesEnd = temp; 381 else linesEnd = s.length(); 382 383 /* 384 // VERY USEFUL FOR DEBUGGING. DO NOT DELETE... 385 // NOTE: This method is the one that GREP uses. 386 System.out.println("s.charAt(pos)\t\t= " + "[" + s.charAt(pos) + "]"); 387 System.out.println("s.charAt(pos+len)\t= " + "[" + s.charAt(pos+len) + "]"); 388 System.out.println("s.length()\t\t= " + s.length()); 389 System.out.println("pos\t\t\t= " + pos); 390 System.out.println("pos + len\t\t= " + (pos + len)); 391 System.out.println("linesStart\t\t= " + linesStart); 392 System.out.println("linesEnd\t\t= " + linesEnd); 393 */ 394 395 return (unixColorCode != null) 396 ? s.substring(linesStart, pos) + 397 unixColorCode + s.substring(pos, pos + len) + RESET + 398 s.substring(pos + len, linesEnd) 399 : s.substring(linesStart, linesEnd); 400 401 /* 402 OOPS.... For Posterity, this shall remain, here, but commented 403 s.substring(linesStart, pos) + 404 s.substring(pos, pos + len) + 405 s.substring(pos + len, linesEnd); 406 */ 407 } 408 409 /** 410 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINE_DESC> 411 * 412 * @param s This may be any valid Java {@code String}. It ought to be some variant of a 413 * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both 414 * directions, left and right</I> - from parameter {@code int 'pos'}. 415 * 416 * @param pos This is a position in the input-parameter {@code String 's'}. The nearest 417 * new-line character both to the left of this position, and to the right, will be found and 418 * identified. If the character at {@code s.charAt(pos)} is itself a newline {@code '\n'} 419 * character, then <I>an exception shall throw</I>. 420 * 421 * @return The {@code String} identified by the first new-line character PLUS 1 422 * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1 423 * <I><B>AFTER</I></B> index {@code 'pos + len'}. 424 * 425 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINES_RET> 426 * 427 * @throws StringIndexOutOfBoundsException If {@code 'pos'} is not within the bounds of the 428 * input {@code String 's'} 429 * 430 * @throws IllegalArgumentException If the character in {@code String 's'} at position 431 * {@code 'pos'} is a newline {@code '\n'}, itself. 432 */ 433 public static String line(String s, int pos) 434 { 435 if ((pos > s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException( 436 "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end of " + 437 "String 's', which has length [" + s.length() + "]" 438 ); 439 440 if (s.charAt(pos) == '\n') throw new IllegalArgumentException( 441 "The position-index for string-parameter 's' contains, itself, a new line character " + 442 "'\\n.' This is not allowed here." 443 ); 444 445 int lineStart, lineEnd; 446 447 // Prevents StrIndexOf from throwing StringINdexOutOfBounds 448 if (pos == 0) lineStart = 0; 449 450 // Also prevent lineStart equal-to '-1' 451 else if ((lineStart = StrIndexOf.left(s, pos, '\n')) == -1) lineStart = 0; 452 453 // Prevent lineEnd equal to '-1' 454 if ((lineEnd = s.indexOf('\n', pos)) == -1) lineEnd = s.length(); 455 456 // if this is the first line, there was no initial '\n', so don't skip it! 457 return (lineStart == 0) 458 459 // This version returns the String from the position-0 (Pay Attention!) 460 ? s.substring(0, lineEnd) 461 462 // This version simply eliminates the '\n' that is in the directly-preceeding character 463 : s.substring(lineStart + 1, lineEnd); 464 } 465 466 /** 467 * This will retrieve the first {@code 'n'} lines of a {@code String} - where a line is defined 468 * as everything up to and including the next newline {@code '\n'} character. 469 * 470 * @param s Any java {@code String}. 471 * 472 * @param n This is the number of lines of text to retrieve. 473 * 474 * @return a substring of s where the last character in the {@code String} is a {@code '\n'}. 475 * The last character should be the nth {@code '\n'} character found in s. If there is no such 476 * character, then the original {@code String} shall be returned instead. 477 * 478 * @throws NException This exception shall throw if parameter {@code 'n'} is less than 1, or 479 * longer than {@code s.length()}. 480 */ 481 public static String firstNLines(String s, int n) 482 { 483 NException.check(n, s); 484 int pos = StrIndexOf.nth(s, n, '\n'); 485 486 if (pos != -1) return s.substring(0, pos + 1); 487 else return s; 488 } 489 490 /** 491 * This will retrieve the last 'n' lines of a {@code String} - where a line is defined as 492 * everything up to and including the next newline {@code '\n'} character. 493 * 494 * @param s Any java {@code String}. 495 * 496 * @param n This is the number of lines of text to retrieve. 497 * 498 * @return a substring of {@code 's'} where the last character in the {@code String} is a 499 * new-line character {@code '\n'}, and the first character is the character directly before 500 * the nth newline {@code '\n'} found in {@code 's'} - starting the count at the end of the 501 * {@code String}. If there is no such substring, then the original {@code String} shall be 502 * returned. 503 * 504 * @throws NException This exception shall throw if {@code 'n'} is less than 1, or longer 505 * {@code s.length()}. 506 */ 507 public static String lastNLines(String s, int n) 508 { 509 NException.check(n, s); 510 int pos = StrIndexOf.nthFromEnd(s, n, '\n'); 511 512 if (pos != -1) return s.substring(pos + 1); 513 else return s; 514 } 515 516 /** 517 * This is used for "trimming each line" of an input {@code String}. Generally, when dealing 518 * with HTML there may be superfluous white-space that is useful in some places, but not 519 * necessarily when HTML is copied and pasted to other sections of a page (or to another page, 520 * altogether). This will split a {@code String} by new-line characters, and then trim each 521 * line, and afterward rebuild the {@code String} and return it. 522 * 523 * <BR /><BR /><DIV CLASS=JDHint> 524 * <B STYLE='color:red;'>CRLF Issues:</B> This will only split the {@code String} using the 525 * standard {@code '\n'} character. If the {@code String} being used uses {@code '\r'} or 526 * {@code '\n\r'}, use a different trim method. 527 * </DIV> 528 * 529 * @param str This may be any {@code String}. It will be split by new-line characters 530 * {@code '\n'} 531 * 532 * @return Returns the rebuilt {@code String}, with each line having a {@code String.trim();} 533 * operation performed. 534 */ 535 public static String trimEachLine(String str) 536 { 537 StringBuilder sb = new StringBuilder(); 538 539 for (String s : str.split("\\n")) 540 541 if ((s = s.trim()).length() == 0) continue; 542 else sb.append(s + '\n'); 543 544 return sb.toString().trim(); 545 } 546 547 /** 548 * Interprets an input {@code String} as one which was read out of a Text-File. Counts the 549 * number of new-line ({@code '\n'}) characters between {@code String} indices {@code '0'} and 550 * {@code 'pos'} 551 * 552 * <BR /><BR />This is intended be the Line-Number where {@code String}-Index parameter 553 * {@code 'pos'} is located inside the {@code 'str'} (presuming {@code 'str'} was retrieved 554 * from a Text-File). 555 * 556 * @param str Any Java {@code String}, preferably one with multiple lines of text. 557 * @param pos Any valid {@code String}-Index that occurs ithin {@code 'str'} 558 * 559 * @return The Line-Number within Text-File parameter {@code 'str'} which contains 560 * {@code String}-Index parameter {@code 'pos'} 561 * 562 * @throws StringIndexOutOfBoundsException If integer-parameter {@code 'pos'} is negative or 563 * past the length of the {@code String}-Parameter {@code 'str'}. 564 * 565 * @see #lineNumberSince(String, int, int, int) 566 */ 567 public static int lineNumber(String str, int pos) 568 { 569 if (pos < 0) throw new StringIndexOutOfBoundsException 570 ("The number provided to index parameter 'pos' : [" + pos + "] is negative."); 571 572 if (pos >= str.length()) throw new StringIndexOutOfBoundsException( 573 "The number provided to index parameter 'pos' : [" + pos + "] is greater than the " + 574 "length of the input String-Parameter 'str' [" + str.length() + "]." 575 ); 576 577 int lineNum = 1; 578 579 for (int i=0; i <= pos; i++) if (str.charAt(i) == '\n') lineNum++; 580 581 return lineNum; 582 } 583 584 /** 585 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_LINE_SINCE_DESC> 586 * 587 * @param str Any Java {@code String}. This {@code String} will be interpreted as a Text-File 588 * whose newline characters ({@code '\n'} chars) represent lines of text. 589 * 590 * @param pos Any valid {@code String}-index within {@code 'str'} 591 * 592 * @param prevLineNum This should be the Line-Number that contains the {@code String}-index 593 * {@code 'prevPos'} 594 * 595 * @param prevPos This may be any index contained by {@code String} parameter {@code 'str'}. 596 * It is expected that this parameter be an index that occured on Line-Number 597 * {@code 'prevLineNum'} of the Text-File {@code 'str'} 598 * 599 * @return The Line-Number within Text-File parameter {@code 'str'} that contains 600 * {@code String}-index {@code 'pos'} 601 * 602 * @throws IllegalArgumentException If {@code 'pos'} is less than or equal to 603 * {@code 'prevPos'}, or if {@code 'prevLineNum'} is less than zero. 604 * 605 * @see #lineNumber(String, int) 606 */ 607 public static int lineNumberSince(String str, int pos, int prevLineNum, int prevPos) 608 { 609 if (pos <= prevPos) throw new IllegalArgumentException( 610 "The number provided to index parameter 'pos' : [" + pos + "] is less than or equal " + 611 "to previous-match index-parameter prevPos : [" + prevPos + "]" 612 ); 613 614 if (prevLineNum < 0) throw new IllegalArgumentException( 615 "You have provided a negative number to Line-Number parameter 'prevLineNum' : " + 616 "[" + prevLineNum + "]" 617 ); 618 619 for (int i = (prevPos + 1); i <= pos; i++) if (str.charAt(i) == '\n') prevLineNum++; 620 621 return prevLineNum; 622 } 623 624 625 // ******************************************************************************************** 626 // ******************************************************************************************** 627 // Abbreviation: Line-Length **AND** Number of Lines 628 // ******************************************************************************************** 629 // ******************************************************************************************** 630 631 632 /** 633 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_DESCRIPTION> 634 * 635 * @param s Any Java {@code String} 636 * @param horizAbbrevStr <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_H_ABBREV_STR> 637 * @param maxLineLength <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_MAX_LINE_LEN> 638 * @param maxNumLines <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_MAX_NM_LINES> 639 * 640 * @param compactConsecutiveBlankLines When this parameter is passed {@code TRUE}, any 641 * series of Empty-Lines, or lines only containing White-Space will be compacted to a single 642 * line of text that simply states {@code "[Compacted 10 Blank Lines]"} (or however many 643 * White-Space-Only Lines were actually compacted, if that number isn't {@code '10'}) 644 * 645 * @return The modified {@code String}. 646 * @throws IllegalArgumentException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WHA_IAEX> 647 */ 648 @LinkJavaSource(handle="VertAndHorizAbbrev") 649 public static String widthHeightAbbrev( 650 final String s, 651 final String horizAbbrevStr, 652 final Integer maxLineLength, 653 final Integer maxNumLines, 654 final boolean compactConsecutiveBlankLines 655 ) 656 { 657 return VertAndHorizAbbrev.print 658 (s, horizAbbrevStr, maxLineLength, maxNumLines, compactConsecutiveBlankLines); 659 } 660 661 662 // ******************************************************************************************** 663 // ******************************************************************************************** 664 // Wrap Text to another line 665 // ******************************************************************************************** 666 // ******************************************************************************************** 667 668 669 /** 670 * Convenience Method. 671 * <BR />See Documentation: {@link #wrap(String, int, int)} 672 * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters 673 */ 674 @LinkJavaSource(handle="Wrap") 675 @LinkJavaSource(handle="WrapHelpers") 676 public static String wrap(String s, int lineLen) 677 { return Wrap.wrap(s, lineLen, lineLen); } 678 679 /** 680 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION> 681 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM> 682 * 683 * @param s Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'} 684 * @param firstLineLen <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN> 685 * @param lineLen The maximum allowable Line-Length for all other lines of text. 686 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS> 687 * 688 * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX> 689 * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are 690 * zero or negative. 691 */ 692 @LinkJavaSource(handle="Wrap") 693 @LinkJavaSource(handle="WrapHelpers") 694 public static String wrap(String s, int firstLineLen, int lineLen) 695 { return Wrap.wrap(s, firstLineLen, lineLen); } 696 697 698 /** 699 * Convenience Method. 700 * <BR />See Documentation: {@link #wrapToIndentation(String, int, int)} 701 * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters 702 */ 703 @LinkJavaSource(handle="WrapToIndentation") 704 @LinkJavaSource(handle="WrapHelpers") 705 public static String wrapToIndentation(String s, int lineLen) 706 { return WrapToIndentation.wrap(s, lineLen, lineLen); } 707 708 /** 709 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION> 710 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM> 711 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_TO_INDENT> 712 * 713 * @param s Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'} 714 * @param firstLineLen <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN> 715 * @param lineLen The maximum allowable Line-Length for all other lines of text. 716 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS> 717 * 718 * <BR /><BR />As already explained, the returned {@code String} shall contain "wrapped" text 719 * which has an amount of indentation (space characters) equal to whatever line is directly 720 * above it. Indented lines, effectively, "inherit" the amount of indentation present in the 721 * line coming directly before them. 722 * 723 * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX> 724 * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are 725 * zero or negative. 726 */ 727 @LinkJavaSource(handle="WrapToIndentation") 728 @LinkJavaSource(handle="WrapHelpers") 729 public static String wrapToIndentation(String s, int firstLineLen, int lineLen) 730 { return WrapToIndentation.wrap(s, firstLineLen, lineLen); } 731 732 /** 733 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION> 734 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM> 735 * 736 * @param s Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'} 737 * @param firstLineLen <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN> 738 * @param lineLen The maximum allowable Line-Length for all other lines of text. 739 * 740 * @param extraSpaces Wrapped text will get an additional amount of indentation, past 741 * any and all inherited indentation 742 * @return Wrapped. 743 * 744 * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX> 745 * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are 746 * zero or negative. 747 */ 748 @LinkJavaSource(handle="WrapToIndentationPlus") 749 @LinkJavaSource(handle="WrapHelpers") 750 public static String wrapToIndentationPlus 751 (String s, int firstLineLen, int lineLen, int extraSpaces) 752 { return WrapToIndentationPlus.wrap(s, firstLineLen, lineLen, extraSpaces); } 753 754 755 // ******************************************************************************************** 756 // ******************************************************************************************** 757 // Misc Date String Functions 758 // ******************************************************************************************** 759 // ******************************************************************************************** 760 761 762 private static final Calendar internalCalendar = Calendar.getInstance(); 763 764 /** 765 * Generates a "Date String" using the character separator {@code '.'} 766 * @return A {@code String} in the form: {@code YYYY.MM.DD} 767 */ 768 public static String dateStr() { return dateStr('.', false); } 769 770 /** 771 * Generates a "Date String" using the <I>separator</I> parameter as the separator between 772 * numbers 773 * 774 * @param separator Any ASCII or UniCode character. 775 * 776 * @return A {@code String} of the form: {@code YYYYcMMcDD} where {@code 'c'} is the passed 777 * {@code 'separator'} parameter. 778 */ 779 public static String dateStr(char separator) { return dateStr(separator, false); } 780 781 /** 782 * Generates a "Date String" that is consistent with the directory-name file-storage locations 783 * used to store articles from {@code http://Gov.CN}. 784 * 785 * @return The {@code String's} used for the Chinese Government Web-Portal Translation Pages 786 */ 787 public static String dateStrGOVCN() { return dateStr('/', false).replaceFirst("/", "-"); } 788 // "2017-12/05" 789 790 /** 791 * This class is primary included because although Java has a pretty reasonable "general 792 * purpose" calendar class/interface, but a consistent / same {@code String} since is needed 793 * because the primary use here is for building the names of files. 794 * 795 * @param separator Any ASCII or Uni-Code character. 796 * 797 * @param includeMonthName When <I>TRUE</I>, the English-Name of the month ({@code 'January'} 798 * ... {@code 'December'}) will be appended to the month number in the returned {@code String}. 799 * 800 * @return The year, month, and day as a {@code String}. 801 */ 802 public static String dateStr(char separator, boolean includeMonthName) 803 { 804 Calendar c = internalCalendar; 805 String m = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero! 806 String d = zeroPad10e2(c.get(Calendar.DAY_OF_MONTH)); 807 808 if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US); 809 810 return (separator != 0) 811 ? (c.get(Calendar.YEAR) + "" + separator + m + separator + d) 812 : (c.get(Calendar.YEAR) + "" + m + d); 813 } 814 815 816 // ******************************************************************************************** 817 // ******************************************************************************************** 818 // Misc Month-Date String Functions 819 // ******************************************************************************************** 820 // ******************************************************************************************** 821 822 823 /** The months of the year, as an immutable list of {@code String's}. */ 824 public static final ReadOnlyList<String> months = new ReadOnlyArrayList<>( 825 "January", "February", "March", "April", "May", "June", 826 "July", "August", "September", "October", "November", "December" 827 ); 828 829 /** 830 * Converts an integer into a Month. 831 * @param month The month, as a number from {@code '1'} to {@code '12'}. 832 * @return A month as a {@code String} like: {@code "January"} or {@code "August"} 833 * @see #months 834 */ 835 public static String monthStr(int month) { return months.get(month); } 836 837 /** 838 * Returns a {@code String} that has the year and the month (but not the day, or any other 839 * time related components). 840 * 841 * @return Returns the current year and month as a {@code String}. 842 */ 843 public static String ymDateStr() { return ymDateStr('.', false); } 844 845 /** 846 * Returns a {@code String} that has the year and the month (but not the day, or other time 847 * components). 848 * 849 * @param separator The single-character separator used between year, month and day. 850 * @return The current year and month as a {@code String}. 851 */ 852 public static String ymDateStr(char separator) { return ymDateStr(separator, false); } 853 854 /** 855 * Returns a {@code String} that has the year and the month (but not the day, or other time 856 * components). 857 * 858 * @param separator The single-character separator used between year, month and day. 859 * 860 * @param includeMonthName When this is true, the name of the month, in English, is included 861 * with the return {@code String}. 862 * 863 * @return {@code YYYY<separator>MM(? include-month-name)} 864 */ 865 public static String ymDateStr(char separator, boolean includeMonthName) 866 { 867 Calendar c = internalCalendar; 868 String m = zeroPad10e2(c.get(Calendar.MONTH) + 1); // January is month zero! 869 870 if (includeMonthName) m += " - " + c.getDisplayName(Calendar.MONTH, 2, Locale.US); 871 872 return (separator != 0) 873 ? (c.get(Calendar.YEAR) + "" + separator + m) 874 : (c.get(Calendar.YEAR) + "" + m); 875 } 876 877 878 // ******************************************************************************************** 879 // ******************************************************************************************** 880 // Misc Time String Functions 881 // ******************************************************************************************** 882 // ******************************************************************************************** 883 884 885 /** 886 * Returns the current time as a {@code String}. 887 * 888 * @return military time - with AM|PM (redundant) added too. 889 * Includes only Hour and Minute - separated by a colon character {@code ':'} 890 * 891 * @see #timeStr(char) 892 */ 893 public static String timeStr() { return timeStr(':'); } 894 895 /** 896 * Returns the current time as a {@code String}. 897 * @param separator The character used to separate the minute & hour fields 898 * @return military time - with AM|PM added redundantly, and a separator of your choosing. 899 */ 900 public static String timeStr(char separator) 901 { 902 Calendar c = internalCalendar; 903 int ht = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12); 904 String h = zeroPad10e2((ht == 0) ? 12 : ht); // 12:00 is represented as "0"... changes this... 905 String m = zeroPad10e2(c.get(Calendar.MINUTE)); 906 String p = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM"; 907 908 return (separator != 0) 909 ? (h + separator + m + separator + p) 910 : (h + m + p); 911 } 912 913 /** 914 * Returns the current time as a {@code String}. This method uses all time components 915 * available. 916 * 917 * @return military time - with AM|PM added redundantly. 918 */ 919 public static String timeStrComplete() 920 { 921 Calendar c = internalCalendar; 922 int ht = c.get(Calendar.HOUR) + ((c.get(Calendar.AM_PM) == Calendar.AM) ? 0 : 12); 923 String h = zeroPad10e2((ht == 0) ? 12 : ht); // 12:00 is represented as "0" 924 String m = zeroPad10e2(c.get(Calendar.MINUTE)); 925 String s = zeroPad10e2(c.get(Calendar.SECOND)); 926 String ms = zeroPad(c.get(Calendar.MILLISECOND)); 927 String p = (c.get(Calendar.AM_PM) == Calendar.AM) ? "AM" : "PM"; 928 929 return h + '-' + m + '-' + p + '-' + s + '-' + ms + "ms"; 930 } 931 932 933 // ******************************************************************************************** 934 // ******************************************************************************************** 935 // More Numeric Methods 936 // ******************************************************************************************** 937 // ******************************************************************************************** 938 939 940 private static final DecimalFormat formatter = new DecimalFormat("#,###"); 941 942 /** 943 * Makes a {@code long} number like {@code 123456789} into a number-string such as: 944 * {@code "123,456,789"}. Java's {@code package java.text.*} is easy to use, and versatile, but 945 * the commands are not always so easy to remember. 946 * 947 * @param l Any {@code long} integer. Comma's will be inserted for every third power of ten 948 * 949 * @return After calling java's {@code java.text.DecimalFormat} class, a {@code String} 950 * representing this parameter will be returned. 951 */ 952 public static String commas(long l) 953 { return formatter.format(l); } 954 955 /** 956 * The words "ordinal indicator" are referring to the little character {@code String} that is 957 * often used in English to make a number seem more a part of an english sentence. 958 * 959 * @param i Any positive integer (greater than 0) 960 * 961 * @return This will return the following strings: 962 * 963 * <TABLE CLASS=JDBriefTable> 964 * <TR><TH>Input: </TH><TH>RETURNS:</TH></TR> 965 * <TR><TD>i = 1 </TD><TD>"st" (as in "1st","first") </TD></TR> 966 * <TR><TD>i = 2 </TD><TD>"nd" (as in "2nd", "second") </TD></TR> 967 * <TR><TD>i = 4 </TD><TD>"th" (as in "4th") </TD></TR> 968 * <TR><TD>i = 23 </TD><TD>"rd" (as in "23rd") </TD></TR> 969 * </TABLE> 970 * 971 * @throws IllegalArgumentException If i is negative, or zero 972 */ 973 public static String ordinalIndicator(int i) 974 { 975 if (i < 1) throw new IllegalArgumentException 976 ("i: " + i + "\tshould be a natural number > 0."); 977 978 979 // Returns the last 2 digits of the number, or the number itself if it is less than 100. 980 // Any number greater than 100 - will not have the "text-ending" (1st, 2nd, 3rd..) affected 981 // by the digits after the first two digits. Just analyze the two least-significant digits 982 983 i = i % 100; 984 985 // All numbers between "4th" and "19th" end with "th" 986 if ((i > 3) && (i < 20)) return "th"; 987 988 // set i to be the least-significant digit of the number - if that number was 1, 2, or 3 989 i = i % 10; 990 991 // Obvious: English Rules. 992 if (i == 1) return "st"; 993 if (i == 2) return "nd"; 994 if (i == 3) return "rd"; 995 996 // Compiler is complaining. This statement should never be executed. 997 return "th"; 998 } 999 1000 1001 // ******************************************************************************************** 1002 // ******************************************************************************************** 1003 // Zero Padding stuff 1004 // ******************************************************************************************** 1005 // ******************************************************************************************** 1006 1007 1008 /** 1009 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZERO_PAD_DESC> 1010 * @param n Any Integer. If {@code 'n'} is negative or greater than 1,000 - null is returned. 1011 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZERO_PAD_RET> 1012 * @see #zeroPad10e2(int) 1013 * @see #zeroPad10e4(int) 1014 */ 1015 public static String zeroPad(int n) 1016 { 1017 if (n < 0) return null; 1018 if (n < 10) return "00" + n; 1019 if (n < 100) return "0" + n; 1020 if (n < 1000) return "" + n; 1021 return null; 1022 } 1023 1024 /** 1025 * Pads an integer such that it contains enough leading zero's to ensure a 1026 * {@code String}-length of two. 1027 * 1028 * @param n Must be an integer between 0 and 99, or else null will be returned 1029 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_10E2_RET> 1030 * @see #zeroPad(int) 1031 */ 1032 public static String zeroPad10e2(int n) 1033 { 1034 if (n < 0) return null; 1035 if (n < 10) return "0" + n; 1036 if (n < 100) return "" + n; 1037 return null; 1038 } 1039 1040 /** 1041 * Pads an integer such that it contains enough leading zero's to ensure a String-length of 1042 * four. 1043 * 1044 * @param n Must be an integer between 0 and 9999, or else null will be returned 1045 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_10E4_RET> 1046 * @see #zeroPad(int) 1047 */ 1048 public static String zeroPad10e4(int n) 1049 { 1050 if (n < 0) return null; 1051 if (n < 10) return "000" + n; 1052 if (n < 100) return "00" + n; 1053 if (n < 1000) return "0" + n; 1054 if (n < 10000) return "" + n; 1055 return null; 1056 } 1057 1058 /** 1059 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_POW2_DESC> 1060 * 1061 * @param n Must be an integer between {@code '0'} and {@code '9999'} where the number of 1062 * {@code '9'} digits is equal to the value of parameter {@code int 'powerOf10'} 1063 * 1064 * @param powerOf10 This must be a positive integer greater than {@code '1'}. It may not be 1065 * larger {@code '11'}. The largest value that any integer in Java may attain is 1066 * {@code '2,147,483, 647'} 1067 * 1068 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_ZP_POW2_RET> 1069 * 1070 * @throws IllegalArgumentException if the value parameter {@code 'powerOf10'} is less than 2, 1071 * or greater than {@code 11}. 1072 */ 1073 public static String zeroPad(int n, int powerOf10) 1074 { 1075 if (n < 0) return null; // Negative Values of 'n' not allowed 1076 1077 char[] cArr = new char[powerOf10]; // The String's length will be equal to 'powerOf10' 1078 String s = "" + n; // (or else 'null' would be returned) 1079 int i = powerOf10 - 1; // Internal Loop variable 1080 int j = s.length() - 1; // Internal Loop variable 1081 1082 Arrays.fill(cArr, '0'); // Initially, fill the output char-array with all 1083 // zeros 1084 1085 while ((i >= 0) && (j >= 0)) // Now start filling that char array with the 1086 cArr[i--] = s.charAt(j--); // actual number 1087 1088 if (j >= 0) return null; // if all of parameter 'n' was inserted into the 1089 // output (number 'n' didn't fit) then powerOf10 1090 // was insufficient, so return null. 1091 1092 return new String(cArr); 1093 } 1094 1095}