001package Torello.Java; 002 003import Torello.Java.Function.IntTFunction; 004import static Torello.Java.C.*; 005 006// Needed for a few JavaDoc Comments 007import Torello.JavaDoc.Field; 008import Torello.JavaDoc.Constructor; 009import Torello.JavaDoc.Method; 010import Torello.JavaDoc.AnnotationElem; 011 012import Torello.JavaDoc.LinkJavaSource; 013import static Torello.JavaDoc.Entity.METHOD; 014 015/** 016 * This class provides several {@code String} printing utilities such as abbreviation and list 017 * printing. 018 */ 019@Torello.JavaDoc.StaticFunctional 020public class StrPrint 021{ 022 private StrPrint() { } 023 024 025 // ******************************************************************************************** 026 // ******************************************************************************************** 027 // HELPER & BASIC 028 // ******************************************************************************************** 029 // ******************************************************************************************** 030 031 032 /** 033 * Converts every line-character ({@code '\n'}) - <I>and any white-space before or after 034 * that character</I>, into the {@code String} - {@code "\\n"} - which is the actual two 035 * character sequence of a back-slash ({@code '\'}), followed by the letter {@code 'n'}. 036 * 037 * <BR /><BR />After new-line characters are replaced, the method will remove any duplicate 038 * spaces that are present in the {@code String}, and reduce them to a single space character 039 * 040 * <BR /><TABLE CLASS=JDBriefTable> 041 * <TR><TH>Input {@code String}</TH><TH>Returned {@code String}</TH></TR> 042 * <TR><TD>"Hello World"</TD><TD>"Hello World"</TD></TR> 043 * <TR><TD>"Hello \t World"</TD><TD>"Hello World"</TD></TR> 044 * <TR><TD>"Hello World\n"</TD><TD>"Hello World\\n"</TD></TR> 045 * <TR><TD>"Hello World \n\t \n"</TD><TD>"Hello World\\n\\n"</TD></TR> 046 * <TR> 047 * <TD>"Hello Today!\nHow Are You?"</TD> 048 * <TD>"Hello Today!\\nHow Are You?"</TD> 049 * </TR> 050 * <TR> 051 * <TD>"Hello,\n Testing 1, 2, 3\n"</TD> 052 * <TD>"Hello,\\nTesting 1, 2, 3\\n"</TD> 053 * </TR> 054 * <TR> 055 * <TD>"Hello,\n Testing 1, 2, 3 \n\t\t\t"</TD> 056 * <TD>"Hello,\\nTesting 1, 2, 3\\n"</TD> 057 * </TR> 058 * <TR><TD>"\n"</TD><TD>"\\n"</TD></TR> 059 * <TR><TD>"\n \t"</TD><TD>"\\n"</TD></TR> 060 * <TR><TD>"\n\t \n\t \n\t "</TD><TD>"\\n\\n\\n"</TD></TR> 061 * </TABLE> 062 * 063 * <BR />This method is used in printing Java Source Code to a terminal - in an abbreviated 064 * way! After this method is finished, java-source-as-text is actually <B><I>still</I></B> 065 * look readable, and can be printed in a table of methods on a page. It is used in the Java 066 * Doc Upgrader tool, and makes printing up <B><I>both</I></B> method-signatures 067 * <B><I>and</I></B> method bodies quite a bit easier. 068 * 069 * <BR /><BR />For a better understanding of the use and application of this function, please 070 * take a look at the <B>{@code 'toString'}</B> methods: <B>{@link Method#toString()}, 071 * {@link Constructor#toString()}, {@link Field#toString()} and 072 * {@link AnnotationElem#toString()}</B> 073 * 074 * <BR /><BR /><B CLASS=JDDescLabel>Regular Expressions:</B> 075 * 076 * <BR />This method uses regular-expressions, rather performing an optimized, in-place, 077 * {@code String} replacement (such as one with a {@code for} or {@code while} loop). This 078 * means that there is a little efficiency sacrificed in the name of brevity. 079 * 080 * <BR /><BR />The replacement used in the {@code String.replaceAll} method was thouroughly 081 * tested. The quadruple-backslash {@code '\n'} <I>is actually necessary!</I> The first 082 * escape used is to communicate with the Java-Compiler, and the second round of escaping is 083 * communicating with the Regular Expression Processor. 084 * 085 * @param s Any {@code java.lang.String}, preferably one with multiple lines of text. 086 * 087 * @return A {@code String}, where each line of text has been "trimmed", and the two 088 * character sequence {@code "\\n"} inserted in-between each line. 089 * 090 * @see #abbrevStartRDSF(String, int, boolean) 091 * @see #abbrevEndRDSF(String, int, boolean) 092 */ 093 public static String newLinesAsText(String s) 094 { 095 return s 096 .replaceAll( 097 // White-Space-Except-Newline, THEN newline, THEN White-SpaceExcept-Newline 098 "[ \t\r\f\b]*\n[ \t\r\f\b]*", 099 100 // Replace Each Occurence of that with: 101 // == COMPILES-TO ==> "\\n" == REG-EX-READS ==> BackSlash and letter 'n' 102 "\\\\n" 103 ) 104 // == COMPILES-TO ==> "\s+" == REG-EX-READS ==> 'spaces' 105 .replaceAll("\\s+", " ") 106 107 // Don't forget about leading and trailing stuff... 108 .trim(); 109 } 110 111 112 // ******************************************************************************************** 113 // ******************************************************************************************** 114 // Abbreviating Text, with "newLinesAsText" - Helper 115 // ******************************************************************************************** 116 // ******************************************************************************************** 117 118 119 /** 120 * Convenience Method. 121 * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B> 122 * <BR />Invokes: {@link StringParse#removeDuplicateSpaces(String)} 123 * <BR />Or Invokes: {@link #newLinesAsText(String)} 124 * <BR />Finally: {@link #abbrevStart(String, boolean, int)} 125 */ 126 public static String abbrevStartRDSF 127 (String s, int maxLength, boolean seeEscapedNewLinesAsText) 128 { 129 // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios 130 // of this conditional-statement, the new-lines have already been removed by the previous 131 // method call. 132 // 133 // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines 134 135 return seeEscapedNewLinesAsText 136 ? abbrevStart(newLinesAsText(s), false, maxLength) 137 : abbrevStart(StringParse.removeDuplicateSpaces(s), false, maxLength); 138 } 139 140 /** 141 * Convenience Method. 142 * <BR /><B STYLE='color: red;'>RDSF: Remove Duplicate Spaces First</B> 143 * <BR />Invokes: {@link StringParse#removeDuplicateSpaces(String)} 144 * <BR />Or Invokes: {@link #newLinesAsText(String)} 145 * <BR />Finally: {@link #abbrevEnd(String, boolean, int)} 146 */ 147 public static String abbrevEndRDSF 148 (String s, int maxLength, boolean seeEscapedNewLinesAsText) 149 { 150 // false is passed to 'abbrevStart' parameter 'escapeNewLines' because in both scenarios 151 // of this conditional-statement, the new-lines have already been removed by the previous 152 // method call. 153 // 154 // both 'removeDuplicateSpaces' and 'newLinesAsText' remove the new-lines 155 156 return seeEscapedNewLinesAsText 157 ? abbrevEnd(newLinesAsText(s), false, maxLength) 158 : abbrevEnd(StringParse.removeDuplicateSpaces(s), false, maxLength); 159 } 160 161 /** 162 * Convenience Method. 163 * <BR />Passes: {@code '0'} to parameter {@code 'abbrevPos'}, forcing the abbreviation to 164 * occur at the <B>start</B> of the {@code String} (if long enough to be abbreviated) 165 * <BR />See Documentation: {@link #abbrev(String, int, boolean, String, int)} 166 */ 167 @LinkJavaSource(handle="Abbrev", name="print1") 168 public static String abbrevStart(String s, boolean escNewLines, int maxLength) 169 { return Abbrev.print1(s, 0, escNewLines, null, maxLength); } 170 171 /** 172 * This will abbreviate any {@code String} using either the ellipsis ({@code '...'}), or some 173 * other use provided abbreviation-{@code String} - <I>as long as the provided {@code String} 174 * is longer than {@code 'maxLength'}.</I> When {@code 's'} is, indeed, longer than 175 * {@code 'maxLength'} the returned-{@code String} will contain the ellipsis abbreviation 176 * beginning at {@code String}-index {@code 'abbrevPos'}. 177 * 178 * <BR /><BR />You have the option of asking that new-line characters ({@code '\n'}) be 179 * escaped into the two-character {@code String: "\\n"}. This optional is provided so that 180 * the output may fit on a single-line, for readability purposes. It will look somewhat like 181 * an escaped {@code JSON} file, which also substitues {@code '\n'} characters for the 182 * 'escaped' version {@code "\\n"}. <I>Note that when this occurs, the replaced-{@code String} 183 * actually only contains two characters, <B>not three,</B> since the first back-slash your are 184 * looking right here is, itself, an escape character!</I> 185 * 186 * @param s This may be any Java (non-null) {@code String} 187 * 188 * @param abbrevPos This parameter is used to indicate where the abbreviation-{@code String} 189 * should occur - <I>if this {@code String 's'} is long enough to be abbreviated.</I> For 190 * instance, if {@code '0'} (zero) were passed to this parameter, and {@code 's'} were longer 191 * than parameter {@code 'maxLength'}, then an ellipsis would be appended to the beginning of 192 * the returned-{@code 'String'}. (Or, if some other {@code 'abbrevStr'} were specified, that 193 * other abbreviation would be appended to the beginning of the returned-{@code String}) 194 * 195 * @param escapeNewLines <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL> 196 * @param abbrevStr <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR> 197 * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL> 198 * 199 * @return If the input {@code String} has a <I>length less-than {@code 'maxLength'}</I>, then 200 * it is returned, unchanged. If the input contained new-line characters, and you have 201 * requested to escape them, that replacement is performed first (which makes the original 202 * {@code String}} longer). Then, if the {@code String} is longer than {@code 'maxLength'}, 203 * it is abbreviated, and either the default ellipsis <I>or the user-provided 204 * {@code 'abbrevStr'}</I> are inserted at location {@code 'abbrevPos'} and returned. 205 * 206 * <BR /><BR />If, after the new-line escape-replacement, the returned-{@code String} would not 207 * be longer than {@code 'maxLength'}, then that escaped-{@code String} is returned, as is 208 * (without any elliptical-characters). 209 * 210 * @throws IllegalArgumentException If the value for {@code 'maxLength'} is negative, or if it 211 * is less than the length of the abbreviation. 212 * 213 * <BR /><BR />Specifically, if {@code 'maxLength'} isn't even long enough to fit the abbreviation 214 * itself, then this exception will throw. 215 * 216 * <BR /><BR />If the value passed to {@code 'abbrevPos'} is negative or longer than the value 217 * passed to {@code 'maxLength'} minus the length of the ellipsis-{@code String}, then this 218 * exception will also throw. 219 */ 220 @LinkJavaSource(handle="Abbrev", name="print1") 221 public static String abbrev( 222 String s, 223 int abbrevPos, 224 boolean escapeNewLines, 225 String abbrevStr, 226 int maxLength 227 ) 228 { return Abbrev.print1(s, abbrevPos, escapeNewLines, abbrevStr, maxLength); } 229 230 /** 231 * Convenience Method. 232 * <BR />Parameter: {@code spaceBeforeAbbrev} set to {@code FALSE} 233 * <BR />Abbreviates: Default ellipsis ({@code '...'}) are placed at the end of the 234 * {@code String} 235 * <BR />See Documentation: {@link #abbrev(String, boolean, boolean, String, int)} 236 */ 237 @LinkJavaSource(handle="Abbrev", name="print2") 238 public static String abbrevEnd(String s, boolean escapeNewLines, int maxLength) 239 { return Abbrev.print2(s, false, escapeNewLines, null, maxLength); } 240 241 /** 242 * This will abbreviate any {@code String} using either the ellipsis ({@code '...'}), or some 243 * other use provided abbreviation-{@code String}, if the provided {@code String} is longer 244 * than {@code 'maxLength'}. If the returned-{@code String} is, indeed, abbreviated then the 245 * elliptical-abbreviation {@code String} will be placed <I>at the end of the 246 * returned-{@code String}</I> 247 * 248 * <BR /><BR />You have the option of asking that new-line characters ({@code '\n'}) be 249 * escaped into the two-character {@code String: "\\n"}. This optional is provided so that 250 * the output may fit on a single-line, for readability purposes. It will look somewhat like 251 * an escaped {@code JSON} file, which also substitues {@code '\n'} characters for the 252 * 'escaped' version {@code "\\n"}. <I>Note that when this occurs, the replaced-{@code String} 253 * actually only contains two characters, <B>not three,</B> since the first back-slash your are 254 * looking right here is, itself, an escape character!</I> 255 * 256 * @param s This may be any Java (non-null) {@code String} 257 * 258 * @param spaceBeforeAbbrev This ensures that for whatever variant of ellipsis being used, the 259 * space-character is inserted directly before appending the ellipsis {@code "..."} or the 260 * user-provided {@code 'abbrevStr'}. 261 * 262 * @param escapeNewLines <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_ENL> 263 * @param abbrevStr <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_STR> 264 * @param maxLength <EMBED CLASS='external-html' DATA-FILE-ID=SP_ABBREV_MXL> 265 * 266 * @return If the input {@code String} has a <I>length less-than {@code 'maxLength'}</I>, then 267 * it is returned, unchanged. If the input contained new-line characters, and you have 268 * requested to escape them, that replacement is performed first (which makes the original 269 * {@code String}} longer). Then, if the {@code String} is longer than {@code 'maxLength'}, 270 * it is abbreviated, and either the default ellipsis <I>or the user-provided 271 * {@code 'abbrevStr'}</I> are are appended to the end and returned. 272 * 273 * @throws IllegalArgumentException If the value for {@code 'maxLength'} is negative, or if it 274 * is less than the length of the abbreviation plus the value of {@code 'spaceBeforeAbbrev'}. 275 * <BR /><BR />Specifically, if {@code 'maxLength'} isn't even long enough to fit the abbreviation 276 * itself, then this exception will throw. 277 */ 278 @LinkJavaSource(handle="Abbrev", name="print2") 279 public static String abbrev( 280 String s, 281 boolean spaceBeforeAbbrev, 282 boolean escapeNewLines, 283 String abbrevStr, 284 int maxLength 285 ) 286 { return Abbrev.print2(s, spaceBeforeAbbrev, escapeNewLines, abbrevStr, maxLength); } 287 288 289 // ******************************************************************************************** 290 // ******************************************************************************************** 291 // Abbreviated List Printing 292 // ******************************************************************************************** 293 // ******************************************************************************************** 294 295 296 /** 297 * Convenience Method. 298 * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'} 299 * <BR />See Documentation: {@link #printListAbbrev(Iterable, IntTFunction, int, int, boolean, 300 * boolean, boolean)} 301 */ 302 @LinkJavaSource(handle="PrintListAbbrev", name="print1") 303 public static <ELEM> String printListAbbrev( 304 Iterable<ELEM> list, int lineWidth, int indentation, boolean seeEscapedNewLinesAsText, 305 boolean printNulls, boolean showLineNumbers 306 ) 307 { 308 return PrintListAbbrev.print1( 309 list, (int i, Object o) -> o.toString(), lineWidth, indentation, 310 seeEscapedNewLinesAsText, printNulls, showLineNumbers 311 ); 312 } 313 314 /** 315 * <EMBED CLASS=defs DATA-LIST_TYPE=Array> 316 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION> 317 * @param list Any iterable-list of Java Object's 318 * @param listItemPrinter <EMBED CLASS='extenal-html' DATA-FILE-ID=SP_PLA_LIST_ITEM_PR> 319 * @param lineWidth <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_LINE_WIDTH> 320 * @param indentation <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_INDENTATION> 321 * @param seeEscapedNewLinesAsText <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SEE_ESC_NL> 322 * @param printNulls <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_PRINT_NULLS> 323 * @param showLineNumbers <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SHOW_LNUMS> 324 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_RETURNS> 325 * @see StringParse#zeroPad10e2(int) 326 * @see #abbrevEndRDSF(String, int, boolean) 327 * @see StringParse#nChars(char, int) 328 * @see StringParse#trimLeft(String) 329 */ 330 @LinkJavaSource(handle="PrintListAbbrev", name="print1") 331 public static <ELEM> String printListAbbrev( 332 Iterable<ELEM> list, 333 IntTFunction<? super ELEM, String> listItemPrinter, 334 int lineWidth, 335 int indentation, 336 boolean seeEscapedNewLinesAsText, 337 boolean printNulls, 338 boolean showLineNumbers 339 ) 340 { 341 return PrintListAbbrev.print1( 342 list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText, 343 printNulls, showLineNumbers 344 ); 345 } 346 347 /** 348 * Convenience Method. 349 * <BR />Passes: {@code Object.toString()} to {@code 'listItemPrinter'} 350 * <BR />See Documentation: {@link #printListAbbrev(Object[], IntTFunction, int, int, boolean, 351 * boolean, boolean)} 352 */ 353 @LinkJavaSource(handle="PrintListAbbrev", name="print2") 354 public static <ELEM> String printListAbbrev( 355 ELEM[] arr, 356 int lineWidth, 357 int indentation, 358 boolean seeEscapedNewLinesAsText, 359 boolean printNulls, 360 boolean showLineNumbers 361 ) 362 { 363 return PrintListAbbrev.print2( 364 arr, (int i, Object o) -> o.toString(), lineWidth, indentation, 365 seeEscapedNewLinesAsText, printNulls, showLineNumbers 366 ); 367 } 368 369 /** 370 * <EMBED CLASS=defs DATA-LIST_TYPE=Array> 371 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_DESCRIPTION> 372 * @param list Any iterable-list of Java Object's 373 * @param listItemPrinter <EMBED CLASS='extenal-html' DATA-FILE-ID=SP_PLA_LIST_ITEM_PR> 374 * @param lineWidth <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_LINE_WIDTH> 375 * @param indentation <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_INDENTATION> 376 * @param seeEscapedNewLinesAsText <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SEE_ESC_NL> 377 * @param printNulls <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_PRINT_NULLS> 378 * @param showLineNumbers <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_SHOW_LNUMS> 379 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_PLA_RETURNS> 380 * @see StringParse#zeroPad10e2(int) 381 * @see #abbrevEndRDSF(String, int, boolean) 382 * @see StringParse#trimLeft(String) 383 */ 384 @LinkJavaSource(handle="PrintListAbbrev", name="print2") 385 public static <ELEM> String printListAbbrev( 386 ELEM[] list, 387 IntTFunction<? super ELEM, String> listItemPrinter, 388 int lineWidth, 389 int indentation, 390 boolean seeEscapedNewLinesAsText, 391 boolean printNulls, 392 boolean showLineNumbers 393 ) 394 { 395 return PrintListAbbrev.print2( 396 list, listItemPrinter, lineWidth, indentation, seeEscapedNewLinesAsText, 397 printNulls, showLineNumbers 398 ); 399 } 400 401 402 // ******************************************************************************************** 403 // ******************************************************************************************** 404 // Line(s) of Text 405 // ******************************************************************************************** 406 // ******************************************************************************************** 407 408 409 /** 410 * This will return the complete text-lines of character data for the line identified by 411 * token-substring position parameters {@code pos} and {@code len}. This method will search, 412 * in the left-direction (decreasing {@code String} index) for the first new-line character 413 * identified. It will also search, starting at position {@code pos + len}, for the first 414 * new-line character in the right-direction (increasing {@code String} index). 415 * 416 * <BR /><BR />If the token-substring identified by {@code s.substring(pos, len)} itself 417 * contains any new-line characters, these will neither affect the prepended, nor the 418 * post-pended search {@code String}. To be precise, any newline characters between 419 * {@code 'pos'} and {@code 'len'} will be irrelevant to the left-wards and right-wards 420 * newlines searches for new-line characters. 421 * 422 * @param s This may be any valid Java {@code String}. It ought to be some variant of a 423 * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both 424 * directions, left and right</I> - from parameter {@code int 'pos'} and parameter 425 * {@code int 'len'} 426 * 427 * @param pos This is a position in the input-parameter {@code String 's'}. The nearest 428 * {@code '\n'} (new-line) character, <I>to the left</I> of this position, will be found and 429 * identified. If the {@code char} at {@code s.charAt(pos)} is, itself, a {@code '\n'} 430 * (newline) character, then no left-direction search will be performed. The left-most 431 * position of the returned substring would then be {@code pos + 1}. 432 * 433 * @param len The search for the 'right-most' {@code '\n'} (newline-character) will begin at 434 * position {@code 'len'}. If the character at {@code s.charAt(pos + len)} is, itself, a 435 * new-line character, then no right-direction search will be performed. The right-most 436 * position of the returned substring would be {@code pos + len - 1}. 437 * 438 * @param unixColorCode If this {@code String} is null, it will be ignored. If this 439 * {@code String} is non-null, it will be inserted before the "Matching {@code String}" 440 * indicated by the index-boundaries {@code pos} <I>TO</I> {@code pos + len}. 441 * 442 * <BR /><BR /><B>NOTE:</B> No Validity Check shall be performed on this {@code String}, and 443 * the user is not obligated to provide a {@link C} valid UNIX Color-Code {@code String}. 444 * Also, a closing {@code C.RESET} is inserted after the terminus of the match. 445 * 446 * @return The {@code String} demarcated by the first new-line character PLUS 1 447 * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1 448 * <I><B>AFTER</I></B> index {@code pos + len}. 449 * 450 * <BR /><BR /><B>NOTE:</B> The above does mean, indeed, that the starting and ending 451 * new-lines <B>WILL NOT</B> be included in the returned {@code String}. 452 * 453 * <BR /><BR /><B>ALSO:</B> Also, if there are no new-line characters before position 454 * {@code 'pos'}, then every character beginning at position zero will be included in the 455 * returned {@code String} result. Also, if there are no new-line characters after position 456 * {@code pos + len} then every character after position {@code pos + len} will be appended to 457 * the returned {@code String} result. 458 * 459 * @throws StringIndexOutOfBoundsException If either {@code 'pos'}, or {@code 'pos + len'} are 460 * not within the bounds of the input {@code String 's'} 461 * 462 * @throws IllegalArgumentException If the value passed to parameter {@code 'len'} is zero or 463 * negative. 464 */ 465 public static String lineOrLines(String s, int pos, int len, String unixColorCode) 466 { 467 if ((pos >= s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException( 468 "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end " + 469 "of String 's', which has length [" + s.length() + "]" 470 ); 471 472 if (len <= 0) throw new IllegalArgumentException 473 ("The value passed to parameter 'len' [" + len + "], may not be negative."); 474 475 if ((pos + len) > s.length()) throw new StringIndexOutOfBoundsException( 476 "The total of parameter 'pos' [" + pos + "], and parameter 'len' [" + len + "], is: " + 477 "[" + (pos + len) + "]. Unfortunately, String parameter 's' only has length " + 478 "[" + s.length() + "]" 479 ); 480 481 int linesStart, linesEnd, temp; 482 483 if (pos == 0) linesStart = 0; 484 else if (s.charAt(pos) == '\n') linesStart = pos + 1; 485 else if ((temp = StrIndexOf.left(s, pos, '\n')) != -1) linesStart = temp + 1; 486 else linesStart = 0; 487 488 if ((pos + len) == s.length()) linesEnd = s.length(); 489 else if (s.charAt(pos + len) == '\n') linesEnd = pos + len; 490 else if ((temp = s.indexOf('\n', pos + len)) != -1) linesEnd = temp; 491 else linesEnd = s.length(); 492 493 /* 494 // VERY USEFUL FOR DEBUGGING. DO NOT DELETE... 495 // NOTE: This method is the one that GREP uses. 496 System.out.println("s.charAt(pos)\t\t= " + "[" + s.charAt(pos) + "]"); 497 System.out.println("s.charAt(pos+len)\t= " + "[" + s.charAt(pos+len) + "]"); 498 System.out.println("s.length()\t\t= " + s.length()); 499 System.out.println("pos\t\t\t= " + pos); 500 System.out.println("pos + len\t\t= " + (pos + len)); 501 System.out.println("linesStart\t\t= " + linesStart); 502 System.out.println("linesEnd\t\t= " + linesEnd); 503 */ 504 505 return (unixColorCode != null) 506 ? s.substring(linesStart, pos) + 507 unixColorCode + s.substring(pos, pos + len) + RESET + 508 s.substring(pos + len, linesEnd) 509 : s.substring(linesStart, linesEnd); 510 511 /* 512 OOPS.... For Posterity, this shall remain, here, but commented 513 s.substring(linesStart, pos) + 514 s.substring(pos, pos + len) + 515 s.substring(pos + len, linesEnd); 516 */ 517 } 518 519 /** 520 * This will return the complete text-line of character data from 'inside' the input-parameter 521 * {@code String s}. The meaning of {@code 'complete text-line'}, in this method, is that any 522 * and all character data between the first {@code '\n'} (new-line character) when scanning 523 * towards <I>the right</I> (increasing {@code String}-index) and the first {@code '\n'} 524 * character when scanning towards <I>the left</I> (decreasing index) constitutes 525 * {@code 'a line'} (of text-data). 526 * 527 * <BR /><BR />This scan shall for the left-most and right-most new-line shall begin at 528 * {@code String}-index parameter {@code pos}. If either the left-direction or 529 * right-direction scan does not find any new-line characters, then start and end indices of 530 * the returned line of text shall be demarcated by input-{@code String} index {@code '0'} and 531 * index {@code String.length()}, <I><B>respectively</B></I> 532 * 533 * @param s This may be any valid Java {@code String}. It ought to be some variant of a 534 * text-file. It will be searched for the nearest {@code '\n'} character - <I>in both 535 * directions, left and right</I> - from parameter {@code int 'pos'}. 536 * 537 * @param pos This is a position in the input-parameter {@code String 's'}. The nearest 538 * new-line character both to the left of this position, and to the right, will be found and 539 * identified. If the character at {@code s.charAt(pos)} is itself a newline {@code '\n'} 540 * character, then <I>an exception shall throw</I>. 541 * 542 * @return The {@code String} identified by the first new-line character PLUS 1 543 * <I><B>BEFORE</I></B> index {@code 'pos'}, and the first new-line character MINUS 1 544 * <I><B>AFTER</I></B> index {@code 'pos + len'}. 545 * 546 * <BR /><BR /><B>NOTE:</B> The above means, that the starting and ending new-lines, 547 * themselves, will not be included in the {@code String} that is returned. 548 * 549 * <BR /><BR /><B>ALSO:</B> Also, if there are no new-line characters before position 550 * {@code 'pos'}, then every character beginning at position zero will be included in the 551 * returned {@code String} result. Also, if there are no new-line characters after position 552 * {@code 'pos + len'} then every character after position {@code 'pos + len'} will be appended 553 * to the returned {@code String} result. 554 * 555 * @throws StringIndexOutOfBoundsException If {@code 'pos'} is not within the bounds of the 556 * input {@code String 's'} 557 * 558 * @throws IllegalArgumentException If the character in {@code String 's'} at position 559 * {@code 'pos'} is a newline {@code '\n'}, itself. 560 */ 561 public static String line(String s, int pos) 562 { 563 if ((pos > s.length()) || (pos < 0)) throw new StringIndexOutOfBoundsException( 564 "The integer passed to parameter 'pos' [" + pos + "], is past the bounds of the end of " + 565 "String 's', which has length [" + s.length() + "]" 566 ); 567 568 if (s.charAt(pos) == '\n') throw new IllegalArgumentException( 569 "The position-index for string-parameter 's' contains, itself, a new line character " + 570 "'\\n.' This is not allowed here." 571 ); 572 573 int lineStart, lineEnd; 574 575 // Prevents StrIndexOf from throwing StringINdexOutOfBounds 576 if (pos == 0) lineStart = 0; 577 578 // Also prevent lineStart equal-to '-1' 579 else if ((lineStart = StrIndexOf.left(s, pos, '\n')) == -1) lineStart = 0; 580 581 // Prevent lineEnd equal to '-1' 582 if ((lineEnd = s.indexOf('\n', pos)) == -1) lineEnd = s.length(); 583 584 // if this is the first line, there was no initial '\n', so don't skip it! 585 return (lineStart == 0) 586 587 // This version returns the String from the position-0 (Pay Attention!) 588 ? s.substring(0, lineEnd) 589 590 // This version simply eliminates the '\n' that is in the directly-preceeding character 591 : s.substring(lineStart + 1, lineEnd); 592 } 593 594 /** 595 * This will retrieve the first {@code 'n'} lines of a {@code String} - where a line is defined 596 * as everything up to and including the next newline {@code '\n'} character. 597 * 598 * @param s Any java {@code String}. 599 * 600 * @param n This is the number of lines of text to retrieve. 601 * 602 * @return a substring of s where the last character in the {@code String} is a {@code '\n'}. 603 * The last character should be the nth {@code '\n'} character found in s. If there is no such 604 * character, then the original {@code String} shall be returned instead. 605 * 606 * @throws NException This exception shall throw if parameter {@code 'n'} is less than 1, or 607 * longer than {@code s.length()}. 608 */ 609 public static String firstNLines(String s, int n) 610 { 611 NException.check(n, s); 612 int pos = StrIndexOf.nth(s, n, '\n'); 613 614 if (pos != -1) return s.substring(0, pos + 1); 615 else return s; 616 } 617 618 /** 619 * This will retrieve the last 'n' lines of a {@code String} - where a line is defined as 620 * everything up to and including the next newline {@code '\n'} character. 621 * 622 * @param s Any java {@code String}. 623 * 624 * @param n This is the number of lines of text to retrieve. 625 * 626 * @return a substring of {@code 's'} where the last character in the {@code String} is a 627 * new-line character {@code '\n'}, and the first character is the character directly before 628 * the nth newline {@code '\n'} found in {@code 's'} - starting the count at the end of the 629 * {@code String}. If there is no such substring, then the original {@code String} shall be 630 * returned. 631 * 632 * @throws NException This exception shall throw if {@code 'n'} is less than 1, or longer 633 * {@code s.length()}. 634 */ 635 public static String lastNLines(String s, int n) 636 { 637 NException.check(n, s); 638 int pos = StrIndexOf.nthFromEnd(s, n, '\n'); 639 640 if (pos != -1) return s.substring(pos + 1); 641 else return s; 642 } 643 644 /** 645 * This is used for "trimming each line" of an input {@code String}. Generally, when dealing 646 * with HTML there may be superfluous white-space that is useful in some places, but not 647 * necessarily when HTML is copied and pasted to other sections of a page (or to another page, 648 * altogether). This will split a {@code String} by new-line characters, and then trim each 649 * line, and afterward rebuild the {@code String} and return it. 650 * 651 * <BR /><BR /><B CLASS=JDDescLabel>CRLF Issues:</B> 652 * 653 * <BR />This will only split the {@code String} using the standard {@code '\n'} character. If 654 * the {@code String} being used uses {@code '\r'} or {@code '\n\r'}, use a different trim 655 * method. 656 * 657 * @param str This may be any {@code String}. It will be split by new-line characters 658 * {@code '\n'} 659 * 660 * @return Returns the rebuilt {@code String}, with each line having a {@code String.trim();} 661 * operation performed. 662 */ 663 public static String trimEachLine(String str) 664 { 665 StringBuilder sb = new StringBuilder(); 666 667 for (String s : str.split("\\n")) 668 669 if ((s = s.trim()).length() == 0) continue; 670 else sb.append(s + '\n'); 671 672 return sb.toString().trim(); 673 } 674 675 /** 676 * Interprets an input {@code String} as one which was read out of a Text-File. Counts the 677 * number of new-line ({@code '\n'}) characters between {@code String} indices {@code '0'} and 678 * {@code 'pos'} 679 * 680 * <BR /><BR />This is intended be the Line-Number where {@code String}-Index parameter 681 * {@code 'pos'} is located inside the {@code 'str'} (presuming {@code 'str'} was retrieved 682 * from a Text-File). 683 * 684 * @param str Any Java {@code String}, preferably one with multiple lines of text. 685 * @param pos Any valid {@code String}-Index that occurs ithin {@code 'str'} 686 * 687 * @return The Line-Number within Text-File parameter {@code 'str'} which contains 688 * {@code String}-Index parameter {@code 'pos'} 689 * 690 * @throws StringIndexOutOfBoundsException If integer-parameter {@code 'pos'} is negative or 691 * past the length of the {@code String}-Parameter {@code 'str'}. 692 * 693 * @see #lineNumberSince(String, int, int, int) 694 */ 695 public static int lineNumber(String str, int pos) 696 { 697 if (pos < 0) throw new StringIndexOutOfBoundsException 698 ("The number provided to index parameter 'pos' : [" + pos + "] is negative."); 699 700 if (pos >= str.length()) throw new StringIndexOutOfBoundsException( 701 "The number provided to index parameter 'pos' : [" + pos + "] is greater than the " + 702 "length of the input String-Parameter 'str' [" + str.length() + "]." 703 ); 704 705 int lineNum = 1; 706 707 for (int i=0; i <= pos; i++) if (str.charAt(i) == '\n') lineNum++; 708 709 return lineNum; 710 } 711 712 /** 713 * This methe may be used, iteratively, inside of a loop for finding the location of any 714 * subsequent {@code String}-Index within a Text-File, based on the information obtained from 715 * a previous Line-Number retrieval. 716 * 717 * <BR /><BR /><B CLASS=JDDescLabel>Use inside For-Loop:</B> 718 * 719 * <BR />This method is designed to be used within a {@code 'for'} or {@code 'while'} loop. 720 * Though it is true that the exception-check which occurs inside this method is superfluous 721 * and redundant, the cost incurred by the two {@code if}-statements is minimal. These checks 722 * are used here, in the code, primarily for readability. 723 * 724 * <BR /><BR />If maximum efficiency is needed, then copy and paste the bottom two lines of 725 * code into your editor, and use that instead, without the exception-checks. 726 * 727 * <BR />In the example below, it should be noted how to use both methods to iterate through 728 * the line numbers in a Text-File, efficiently. 729 * 730 * <DIV CLASS=EXAMPLE>{@code 731 * 732 * int[] posArr = findIndices(myString, "Raindrops on Roses", "Whiskers on Kittens"); 733 * int lineNumber = StrPrint.lineNumber(myString, posArr[0]); 734 * int prevPos = posArr[0]; 735 * 736 * System.out.println("There is a match on line: " + lineNumber); 737 * 738 * for (int i=1; i < posArr.length; i++) 739 * { 740 * lineNumber = StrPrint.lineNumberSince(myString, posArr[i], lineNumber, prevPos); 741 * System.out.println("There is a match on line: " + lineNumber); 742 * 743 * prevPos = posArr[i]; 744 * } 745 * }</DIV> 746 * 747 * @param str Any Java {@code String}. This {@code String} will be interpreted as a Text-File 748 * whose newline characters ({@code '\n'} chars) represent lines of text. 749 * 750 * @param pos Any valid {@code String}-index within {@code 'str'} 751 * 752 * @param prevLineNum This should be the Line-Number that contains the {@code String}-index 753 * {@code 'prevPos'} 754 * 755 * @param prevPos This may be any index contained by {@code String} parameter {@code 'str'}. 756 * It is expected that this parameter be an index that occured on Line-Number 757 * {@code 'prevLineNum'} of the Text-File {@code 'str'} 758 * 759 * @return The Line-Number within Text-File parameter {@code 'str'} that contains 760 * {@code String}-index {@code 'pos'} 761 * 762 * @throws IllegalArgumentException If {@code 'pos'} is less than or equal to 763 * {@code 'prevPos'}, or if {@code 'prevLineNum'} is less than zero. 764 * 765 * @see #lineNumber(String, int) 766 */ 767 public static int lineNumberSince(String str, int pos, int prevLineNum, int prevPos) 768 { 769 if (pos <= prevPos) throw new IllegalArgumentException( 770 "The number provided to index parameter 'pos' : [" + pos + "] is less than or equal " + 771 "to previous-match index-parameter prevPos : [" + prevPos + "]" 772 ); 773 774 if (prevLineNum < 0) throw new IllegalArgumentException( 775 "You have provided a negative number to Line-Number parameter 'prevLineNum' : " + 776 "[" + prevLineNum + "]" 777 ); 778 779 for (int i = (prevPos + 1); i <= pos; i++) if (str.charAt(i) == '\n') prevLineNum++; 780 781 return prevLineNum; 782 } 783 784 785 // ******************************************************************************************** 786 // ******************************************************************************************** 787 // Abbreviation: Line-Length **AND** Number of Lines 788 // ******************************************************************************************** 789 // ******************************************************************************************** 790 791 792 /** 793 * This method allows for printing an abbreviation of a {@code String} such that 794 * <B STYLE='color: red;'><I>BOTH</I></B> the number of lines (height), 795 * <B STYLE='color: red;'><I>AND</I></B> the length of each line of text (width) can both be 796 * abbreviated. There is even a third abbreviation that is possible, and that is where 797 * blank-links can be compacted / flattened first (before the abbreviation process starts). 798 * 799 * <BR /><BR />This method is being used for printing JSON-Response Objects that contain large 800 * HTML-Page {@code String's}. Primarily, if you want to see a message, but do not want 801 * hundreds or even thousands of lines of HTML blasted across your terminal, then this method is 802 * for you! 803 * 804 * <BR /><BR />The package {@code Torello.Browser's} Web-Sockets Communications is making use 805 * of this for printing Chrome-Browser Messages to the terminal. 806 * 807 * @param s Any Java {@code String} 808 * 809 * @param horizAbbrevStr If you have a specific Abbreviation-{@code String} that you would like 810 * to see used in Horizontally-Abbreviated Lines, please pass it here. 811 * 812 * <BR /><BR />Note that this value is directly passed to the {@code 'abbrevStr'} parameters 813 * in the Standard {@code String}-Abbreviation methods. This means that when this parameter is 814 * null, it will be ignored - <I>and if any horizontal (line-length) abbreviations occur, then 815 * the 'Default-Abbreviation {@code String}' {@code "..."} will be used</I>. 816 * 817 * <BR ><BR />Also note that if parameter {@code 'maxLineLength'} is passed null, then lines of 818 * text will not be shortened. In that case, each line of text will retain its exact length 819 * that occured prior to the internal {@code String.split()} invocation. 820 * 821 * @param maxLineLength If you would like to shorten each line of text which is appended to the 822 * returned {@code String}, then pass a positive value to this parameter. 823 * 824 * <BR /><BR />This parameter may be null, and if it is, it will be ignored. In such cases, 825 * individual lines of text will retain their original length. 826 * 827 * @param maxNumLines This is the maximum number of lines of text that will appear in the 828 * returned {@code String}. Note, under normal operation, if this parameter is passed 829 * {@code '3'}, then there will be exactly three lines of text. Furthermore, there will be 830 * exactly {@code '2'} newline {@code '\n'} characters. 831 * 832 * @param compactConsecutiveBlankLines When this parameter is passed {@code TRUE}, any 833 * series of Empty-Lines, or lines only containing White-Space will be compacted to a single 834 * line of text that simply states {@code "[Compacted 10 Blank Lines]"} (or however many 835 * White-Space-Only Lines were actually compacted, if that number isn't {@code '10'}) 836 * 837 * @return The modified {@code String}. 838 * 839 * @throws IllegalArgumentException If parameter {@code 'maxNumLines'} is less than 3. The 840 * returned {@code String} must be long enough to keep the first line, the last line and the 841 * abbreviation note. 842 * 843 * <BR /><BR />This exception will also throw if the internal invocation of the standard 844 * {@code String}-abbreviation method is passed a {@code 'maxLineLength'} that is less than 845 * the minimum line-length requirements for a successful horizontal abbreviation. 846 * 847 * <BR /><BR />Finally, this exception throws if {@code 'maxLineLength'} and 848 * {@code 'maxNumLines'} are both passed null, and {@code 'compactConsecutiveBlankLines'} is 849 * {@code FALSE}. This scenario is considered an error-case because there wouldn't be anything 850 * for the method to do. 851 */ 852 @LinkJavaSource(handle="VertAndHorizAbbrev") 853 public static String widthHeightAbbrev( 854 final String s, 855 final String horizAbbrevStr, 856 final Integer maxLineLength, 857 final Integer maxNumLines, 858 final boolean compactConsecutiveBlankLines 859 ) 860 { 861 return VertAndHorizAbbrev.print 862 (s, horizAbbrevStr, maxLineLength, maxNumLines, compactConsecutiveBlankLines); 863 } 864 865 866 // ******************************************************************************************** 867 // ******************************************************************************************** 868 // Wrap Text to another line 869 // ******************************************************************************************** 870 // ******************************************************************************************** 871 872 873 /** 874 * Convenience Method. 875 * <BR />See Documentation: {@link #wrap(String, int, int)} 876 * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters 877 */ 878 @LinkJavaSource(handle="Wrap") 879 @LinkJavaSource(handle="WrapHelpers") 880 public static String wrap(String s, int lineLen) 881 { return Wrap.wrap(s, lineLen, lineLen); } 882 883 /** 884 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION> 885 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM> 886 * 887 * @param s Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'} 888 * @param firstLineLen <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN> 889 * @param lineLen The maximum allowable Line-Length for all other lines of text. 890 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS> 891 * 892 * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX> 893 * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are 894 * zero or negative. 895 */ 896 @LinkJavaSource(handle="Wrap") 897 @LinkJavaSource(handle="WrapHelpers") 898 public static String wrap(String s, int firstLineLen, int lineLen) 899 { return Wrap.wrap(s, firstLineLen, lineLen); } 900 901 902 /** 903 * Convenience Method. 904 * <BR />See Documentation: {@link #wrapToIndentation(String, int, int)} 905 * <BR />Passes same {@code 'lineLen'} value to <B><I>BOTH</I></B> Line-Length Parameters 906 */ 907 @LinkJavaSource(handle="WrapToIndentation") 908 @LinkJavaSource(handle="WrapHelpers") 909 public static String wrapToIndentation(String s, int lineLen) 910 { return WrapToIndentation.wrap(s, lineLen, lineLen); } 911 912 /** 913 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION> 914 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM> 915 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_TO_INDENT> 916 * 917 * @param s Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'} 918 * @param firstLineLen <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN> 919 * @param lineLen The maximum allowable Line-Length for all other lines of text. 920 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RETURNS> 921 * 922 * <BR /><BR />As already explained, the returned {@code String} shall contain "wrapped" text 923 * which has an amount of indentation (space characters) equal to whatever line is directly 924 * above it. Indented lines, effectively, "inherit" the amount of indentation present in the 925 * line coming directly before them. 926 * 927 * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX> 928 * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are 929 * zero or negative. 930 */ 931 @LinkJavaSource(handle="WrapToIndentation") 932 @LinkJavaSource(handle="WrapHelpers") 933 public static String wrapToIndentation(String s, int firstLineLen, int lineLen) 934 { return WrapToIndentation.wrap(s, firstLineLen, lineLen); } 935 936 /** 937 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_DESCRIPTION> 938 * <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_RIGHT_TRIM> 939 * 940 * @param s Any Java {@code String}; may not contain {@code '\t', '\f' or '\r'} 941 * @param firstLineLen <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_1ST_LN_LEN> 942 * @param lineLen The maximum allowable Line-Length for all other lines of text. 943 * 944 * @param extraSpaces Wrapped text will get an additional amount of indentation, past 945 * any and all inherited indentation 946 * @return Wrapped. 947 * 948 * @throws StringFormatException <EMBED CLASS='external-html' DATA-FILE-ID=SP_WRAP_STR_FMT_EX> 949 * @throws IllegalArgumentException If {@code 'firstLineLen'} or {@code 'lineLen'} are 950 * zero or negative. 951 */ 952 @LinkJavaSource(handle="WrapToIndentationPlus") 953 @LinkJavaSource(handle="WrapHelpers") 954 public static String wrapToIndentationPlus 955 (String s, int firstLineLen, int lineLen, int extraSpaces) 956 { return WrapToIndentationPlus.wrap(s, firstLineLen, lineLen, extraSpaces); } 957}