001package Torello.Java; 002 003import java.util.regex.*; 004 005import java.util.stream.IntStream; 006 007import Torello.Browser.Debugger.scriptParsed; 008import Torello.Java.Function.IntCharFunction; 009 010import Torello.JavaDoc.StaticFunctional; 011import Torello.JavaDoc.Excuse; 012import Torello.JavaDoc.LinkJavaSource; 013 014/** 015 * A class for indenting, unindenting and trimming textual-strings. 016 * 017 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=STRINDENT> 018 */ 019@StaticFunctional 020public class StrIndent 021{ 022 private StrIndent() { } 023 024 // This matches lines of text that contain only blank / white-space characters 025 private static final Pattern EMPTY_LINE = Pattern.compile("^[ \t]+\n", Pattern.MULTILINE); 026 027 /** 028 * Replaces sub-strings that contain a newline character followed by only white-space with 029 * only the new-line character itself. 030 * 031 * @param s Any Java {@code String}, but preferrably one which contains newline characters 032 * ({@code '\n'}), and white-space characters immediately followng the newline, but not other 033 * ASCII nor UNICODE. 034 * 035 * @return The same String with each instance of {@code ^[ \t]+\n} replaced by {@code '\n'}. 036 */ 037 public static String trimWhiteSpaceOnlyLines(String s) 038 { 039 Matcher m = EMPTY_LINE.matcher(s); 040 return m.replaceAll("\n"); 041 } 042 043 /** 044 * Will iterate through <I>each line of text</I> within the input {@code String}-parameter 045 * {@code 's'}, and right-trim the lines. "Right Trim" means to remove all white-space 046 * characters that occur <I><B>after</I></B> the last non-white-space character on the line. 047 * (Does not remove the new-line character ({@code '\n'}) itself). 048 * 049 * <BR /><BR /><B>NOTE:</B> Any line of text which contains only white-space characters is 050 * reduced to a single new-line character. 051 * 052 * @param s Any Java {@code String}, preferrably one with several new-line characters. 053 * 054 * @return The same text, but only after having any 'trailing white-space' characters removed 055 * from each line of text. 056 */ 057 @LinkJavaSource(handle="RightTrimAll") 058 public static String rightTrimAll(String s) 059 { return RightTrimAll.run(s); } 060 061 /** 062 * Will iterate through <I>each line of text</I> within the input {@code String}-parameter 063 * {@code 's'}, and left-trim the lines. "Left Trim" means to remove all white-space 064 * characters that occur <I><B>before</I></B> the last non-white-space character on the line. 065 * (Does not remove the new-line character ({@code '\n'}) itself). 066 * 067 * <BR /><BR /><B>NOTE:</B> Any line of text which contains only white-space characters is 068 * reduced to a single new-line character. 069 * 070 * @param s Any Java {@code String}, preferrably one with several new-line characters. 071 * 072 * @return The same text, but only after having any 'leading white-space' characters removed 073 * from each line of text. 074 */ 075 @LinkJavaSource(handle="LeftTrimAll") 076 public static String leftTrimAll(String s) 077 { return LeftTrimAll.run(s); } 078 079 /** 080 * This method expects to receive a method body, constructor body, or other callable body as a 081 * {@code String}; it will remove the beginning and ending braces <CODE>('{'</CODE> & 082 * <CODE>'}')</CODE>, and beginning & ending empty lines. This is to prepare the 083 * method for code hiliting, used internally by the package {@code Torello.JavaDoc}. 084 * 085 * @param callableAsStr This should be a method body. Make sure <B>**NOT TO INCLUDE**</B> the 086 * method signature at the beginning of the method. The first non-white-space character should 087 * be the open braces character <CODE>('{')</CODE>, and the last non-white-space should be 088 * the closing braces character <CODE>('&#125')</CODE>. 089 * 090 * @return A method body {@code String} that can be hilited using the code-hiliting mechanism 091 * of the "JavaDoc Package." 092 * 093 * @throws CallableBodyException If the input parameter {@code String} does not begin and end 094 * with the curly-braces. 095 */ 096 @LinkJavaSource(handle="ChompCallableBraces") 097 public static String chompCallableBraces(String callableAsStr) 098 { return ChompCallableBraces.run(callableAsStr); } 099 100 /** 101 * Accepts a method body as a {@code String} and left-shifts or right-shifts each line 102 * of text (each {@code 'Line of Code' - LOC}) so that the indentation is consistent with 103 * the requested-indentation input-parameter. 104 * 105 * <BR /><BR /><B>NOTE:</B> The lines of code contained by input-parameter {@code 'codeAsStr'} 106 * must contain leading white-space <I><B STYLE='color: red;'>without any tab ({@code '\t'}) 107 * characters.</B></I> The reasoning here is that tabs can be interpreted in many different 108 * ways, and therefore it is required to replace them before invoking this method. This method 109 * merely adds or removes leading {@code ASCII 0x20} (space-bar characters) from the beginning 110 * of each line of text. A leading tab-character {@code ASCII 0x09} will generate an exception 111 * throw. 112 * 113 * @param codeAsStr A method body. It is expected to be the internal part of a chunk of 114 * source code. 115 * 116 * @param requestedIndent The requested amount of indentation for the method-body. The 117 * line of code that contains the shortest amount of indentation (white-space) will be 118 * calculated, and then all LOC's shall be left-shifted (or right-shifted) according to that 119 * LOC which contained the least amount of leading white-space. 120 * 121 * @return An updated method-body as a {@code String}. 122 * 123 * @throws StringFormatException This exception shall throw whenever a line of text contained 124 * by the input {@code String} has a {@code '\t'} (tab-character) before the first 125 * non-white-space character on that line of text. Code that contains tab-characters is 126 * invariably "auto-indented" by the Code-Editor when loaded into the GUI, and the amount of 127 * indentation applied for each tab-character is usually configurable. Because there are many 128 * variants of how tab's {@code '\t'} gets interpreted by the editor, it is required to replace 129 * these characters first before invoking this method. 130 */ 131 @LinkJavaSource(handle="SetCodeIndent") 132 public static String setCodeIndent(String codeAsStr, int requestedIndent) 133 { return SetCodeIndent.run(codeAsStr, requestedIndent); } 134 135 /** 136 * Convenience Method. 137 * <BR />See Documentation: {@link #setCodeIndent(String, int)} 138 * <BR />Converts: All {@code '\t'} to the specified number of spaces in parameter 139 * {@code SPACES}. 140 * <BR /><B STYLE='color: red'>NOTE:</B> Exception-Checking is <B STYLE='color: red'>NOT</B> 141 * done on input 142 */ 143 @LinkJavaSource(handle="SetCodeIndent") 144 public static String setCodeIndent_WithTabsPolicyAbsolute 145 (String codeAsStr, int requestedIndent, String SPACES) 146 { return SetCodeIndent.run(codeAsStr.replace("\t", SPACES), requestedIndent); } 147 148 /** 149 * Adjusts code-indentation using a relative-sized tab-policy. This method performs the 150 * equivalent of shifting the entire text-block, proportionately, to the left or right. 151 * 152 * <BR /><BR />To do this, first, the number of spaces that preceed the 153 * <B STYLE='color: red;'>least-indented</B> line is computed, and afterwards, every line in 154 * the text is shifted by an identical number of space-characters. The number of spaces that 155 * are either added or removed from each line is dependent on whether the requested 156 * indentation (parameter {@code 'requestedIndent'}) is greater-than or less-than the computed 157 * least-indented line. 158 * 159 * <EMBED CLASS='external-html' DATA-FILE-ID=SI_REL_TABS> 160 * 161 * @param codeAsStr Any Java source-code block, as a {@code java.lang.String} 162 * 163 * @param requestedIndent The number of spaces that the code should have as indentation. 164 * Note, that in the JavaDoc Upgrader code, this number is always {@code '1'}. 165 * 166 * @param spacesPerTab If tabs are found inside this {@code String}, then they are replaced 167 * with an appropriate number of space characters, according to a relative tab-policy, as 168 * described above. 169 * 170 * @return A properly shifted-indented Java source-code block. 171 */ 172 @LinkJavaSource(handle="SetCodeIndentTabsPolicy") 173 public static String setCodeIndent_WithTabsPolicyRelative 174 (String codeAsStr, int requestedIndent, int spacesPerTab) 175 { return SetCodeIndentTabsPolicy.run(codeAsStr, requestedIndent, spacesPerTab); } 176 177 /** 178 * Helper Method for calculating the number of space characters to be used at the beginning 179 * of a line of code, all the while obeying a particular tabs-policy. 180 * 181 * <BR /><BR /><B STYLE='color: red;'>IMPORTANT:</B> None of the parameters to this method 182 * will be checked for errors. This method is often used inside of a loop, and improper 183 * input should be presumed to cause indeterminate results. 184 * 185 * @param code A Java source-code {@code String}, that has been converted into a Java 186 * {@code char[]}-Array. The line of code whose leading white-space is being computed may be 187 * located anywhere in the array. 188 * 189 * <BR /><BR /><B>NOTE:</B> These types of arrays are easily creaated by invoking the 190 * {@code java.lang.String} method {@code 'toCharArray()'} 191 * 192 * @param lineFirstCharacterPos The array-index to be considered as the first character of 193 * non-new-line character data. 194 * 195 * @param spacesPerTab The number of spaces that a tab-character ({@code '\t'}) intends to 196 * represent. 197 * 198 * <BR /><BR />When {@code FALSE} is passed to this parameter, a tab-character will represent 199 * a {@code String} of space-characters whose length is equal to the number of space-characters 200 * that remain until the next modulo-{@code spacesPerTab} boundary. 201 * 202 * @return The number of space-characters ({@code ' '}) that should preceede the line of source 203 * code. 204 * 205 * <BR /><BR /><B STYLE='color: red'>NOTE:</B> If this line of source-code is a white-space 206 * <B STYLE='color: red;'>ONLY</B> line, then {@code -1} will be returned. 207 */ 208 public static int computeEffectiveLeadingWhiteSpace 209 (char[] code, int lineFirstCharacterPos, int spacesPerTab) 210 { 211 int ret = 0; 212 int relativeCount = 0; 213 214 for (int i=lineFirstCharacterPos; i < code.length; i++) 215 216 if (! Character.isWhitespace(code[i])) return ret; 217 218 else switch (code[i]) 219 { 220 case ' ' : 221 ret++; 222 relativeCount = (relativeCount + 1) % spacesPerTab; 223 break; 224 225 case '\t' : 226 ret += (spacesPerTab - relativeCount); 227 relativeCount = 0; 228 break; 229 230 case '\r' : 231 case '\f' : break; 232 case '\n' : return -1; 233 default: throw new UnreachableError(); 234 } 235 236 return -1; 237 } 238 239 /** 240 * Replaces tab-characters ({@code '\t'}) in a single-line of source-code with a 241 * relative-number of space-characters ({@code ' '}). 242 * 243 * <EMBED CLASS='external-html' DATA-FILE-ID=SI_REL_TABS> 244 * 245 * <BR /><BR /><B STYLE='color: red;'>IMPORTANT:</B> None of the parameters to this method 246 * will be checked for errors. This method is often used inside of a loop, and improper 247 * input should be presumed to cause indeterminate results. 248 * 249 * @param code This should be the source-code, converted to a character-array. The specific 250 * line in the source-code being properly space-adjusted may be located anywhere in this 251 * array. 252 * 253 * <BR /><BR /><B>NOTE:</B> These types of arrays are easily creaated by invoking the 254 * {@code java.lang.String} method {@code 'toCharArray()'} 255 * 256 * @param numLeadingSpaces The number of spaces that have been placed before the start of this 257 * line of code. This is needed because <B STYLE='color: red;'>relative</B>-tabs are computed 258 * based on integral-multiples of the tab-width ({@code 'spacesPerTab'}). 259 * 260 * <BR /><BR />This method is a helper & example method that may be used in conjunction 261 * with properly indenting source-code. Note that the number of leading-spaces may not be 262 * identicaly to the actual number of white-space characters in the array. <I>After converting 263 * tab-characters ({@code '\t'}) to spaces ({@code ' '}), this number will often change.</I> 264 * 265 * @param fcPos This parameter should contain the location of the first source-code character 266 * in the line of code. This parameter should be an array-index that 267 * <B STYLE='color: red;'>does not</B> contain white-space. 268 * 269 * @param spacesPerTab The number of spaces that are used to replaces tab-characters. Since 270 * this method performs relative tab-replacement, this constitutes the 271 * <B STYLE='color: red;'>maximum</B> number of space characters that will be used to replace 272 * a tab. 273 * 274 * @return A line of code, as a {@code String}, without any leading white-space, and one in 275 * which all tab-characters have been replaced by spaces. 276 */ 277 @LinkJavaSource(handle="LOCAsStr") 278 public static String lineOfCodeAsStr 279 (char[] code, int numLeadingSpaces, int fcPos, int spacesPerTab) 280 { return LOCAsStr.run(code, numLeadingSpaces, fcPos, spacesPerTab); } 281 282 /** 283 * This performs a variation of "indentation" on a Java {@code String} - simply put - it 284 * replaces each new-line character ({@code '\n'}) with a {@code String} that begins with a 285 * new-line, and is followed by {@code 'n'} blank white-space characters {@code ' '}. 286 * 287 * If the input {@code String} parameter {@code 's'} is of zero-length, then the zero-length 288 * {@code String} is returned. If the final character in the input {@code String} is a 289 * new-line, that new-line is not padded. 290 * 291 * @param s Any {@code java.lang.String} - preferably one that contains new-line characters. 292 * 293 * @param n The number of white-space characters to use when pre-pending white-space to each 294 * line of text in input-parameter {@code 's'} 295 * 296 * @return A new {@code java.lang.String} where each line of text has been indented by 297 * {@code 'n'} blank white-space characters. If the text ends with a new-line, that line of 298 * text is not indented. 299 * 300 * @throws NException If parameter {@code 'n'} is less than one. 301 */ 302 public static String indent(String s, int n) 303 { 304 if (n < 1) throw new NException( 305 "The value passed to parameter 'n' was [" + n + "], but this is expected to be an " + 306 "integer greater than or equal to 1." 307 ); 308 309 if (s.length() == 0) return ""; 310 311 String padding = String.format("%1$" + n + "s", " "); 312 boolean lastIsNewLine = s.charAt(s.length() - 1) == '\n'; 313 314 s = padding + s.replace("\n", "\n" + padding); 315 316 return lastIsNewLine 317 ? s.substring(0, s.length() - padding.length()) 318 : s; 319 } 320 321 /** 322 * Identical to {@link #indent(String, int)}, but pre-pends a {@code TAB} character {@code 'n'} 323 * times, rather than a space-character {@code ' '}. 324 * 325 * @param s Any {@code java.lang.String} - preferably one that contains new-line characters. 326 * 327 * @param n The number of tab ({@code '\t'}) characters to use when pre-pending to each line of 328 * text within input-parameter {@code 's'} 329 * 330 * @return A new {@code java.lang.String} where each line of text has been indented by 331 * {@code 'n'} tab characters. If the text ends with a new-line, that line of text is not 332 * indented. 333 * 334 * @throws NException If parameter {@code 'n'} is less than one. 335 */ 336 public static String indentTabs(String s, int n) 337 { 338 if (n < 1) throw new NException( 339 "The value passed to parameter 'n' was [" + n + "], but this is expected to be an " + 340 "integer greater than or equal to 1." 341 ); 342 343 if (s.length() == 0) return ""; 344 345 String padding = String.format("%1$"+ n + "s", "\t"); 346 boolean lastIsNewLine = s.charAt(s.length() - 1) == '\n'; 347 348 s = padding + s.replace("\n", "\n" + padding); 349 350 return lastIsNewLine 351 ? s.substring(0, s.length() - padding.length()) 352 : s; 353 } 354 355 /** 356 * <EMBED CLASS='external-html' DATA-FILE-ID=SI_INDENT_DESC> 357 * 358 * @param s Any {@code java.lang.String} - preferably one that contains new-line characters. 359 * 360 * @param n The number of tab ({@code '\t'}) characters to use when pre-pending to each line of 361 * text in input-parameter {@code 's'} 362 * 363 * @param spaceOrTab When this parameter is passed <B>{@code TRUE}</B>, the space character 364 * {@code ' '} is used for indentation. When <B>{@code FALSE}</B>, the tab-character 365 * {@code '\t'} is used. 366 * 367 * @return <EMBED CLASS='external-html' DATA-FILE-ID=SI_INDENT_RET> 368 * @throws NException If parameter {@code 'n'} is less than one. 369 */ 370 @LinkJavaSource(handle="Indent") 371 public static String indent 372 (final String s, int n, boolean spaceOrTab, boolean trimBlankLines) 373 { return Indent.run(s, n, spaceOrTab, trimBlankLines); } 374 375 /** 376 * Throws a new {@code ToDoException} 377 * 378 * @return Will (one day) return an unindented String. 379 */ 380 public static String unIndent( 381 String s, int n, 382 boolean trimBlankLines, 383 boolean rightTrimLines, 384 boolean throwOnTab, 385 boolean throwOnNotEnough, 386 boolean dontThrowOnWhiteSpaceOnlyLines 387 ) 388 { 389 if (n < 1) throw new NException( 390 "The value that was passed to parameter 'n' was [" + n + "], but unfortunately this " + 391 "expected to be a positive integer, greater than zero." 392 ); 393 394 char[] cArr = s.toCharArray(); 395 throw new ToDoException(); 396 } 397 398 /** 399 * Performs an indenting of {@code String} of text, but does not indent the first line. This 400 * is used quit frequently by code-generators that need to assign or invoke something, and want 401 * to make sure that subsequent lines of piece of code are indented (after the first line of 402 * text). 403 * 404 * @param s Any instance of {@code java.lang.String}. 405 * @param n The number of space-characters to insert after each newline {@code '\n'} character. 406 * @param spaceOrTab When {@code TRUE}, then there shall be {@code 'n'}-number of space 407 * characters ({@code ' '}) inserted at the beginning of each line of text. When 408 * {@code FALSE}, then this function will insert {@code 'n'} tab characters. 409 * @param trimBlankLines When {@code TRUE}, requests that blank lines be trimmed to only 410 * a single newline ({@code '\n'}) character. 411 * @return The indented {@code String} 412 */ 413 public static String indentAfter2ndLine 414 (String s, int n, boolean spaceOrTab, boolean trimBlankLines) 415 { 416 int pos = s.indexOf('\n'); // If there are no newlines, then return the original string. 417 if (pos == -1) return s; 418 419 pos++; 420 return // return the first string, as is, and then indent subsequent lines. 421 s.substring(0, pos) + 422 indent(s.substring(pos), n, spaceOrTab, trimBlankLines); 423 } 424 425 /** 426 * This will replaced leading tabs for each line of text in a source code file with a specified 427 * number of spaces. If tabs are supposed to represent {@code 4} spaces, then if a line in a 428 * source-code file had three leading tab-characters, then those three leading 429 * {@code char '\t'} would be replaced with {@code 12} leading space characters {@code ' '}. 430 * 431 * @param javaSrcFileAsStr A Java Source-Code File, loaded as a {@code String}. 432 * 433 * @param numSpacesPerTab This identifies the number of spaces a {@code char '\t'} is supposed 434 * to represent in any source-code editor's settings. In Google Cloud Server, the default 435 * value is '4' spaces. 436 */ 437 public static String tabsToSpace(String javaSrcFileAsStr, int numSpacesPerTab) 438 { 439 String spaces = StringParse.nChars(' ', numSpacesPerTab); 440 String[] lines = javaSrcFileAsStr.split("\n"); 441 442 for (String line:lines) System.out.println("LINE: " + line); 443 444 for (int i=0; i < lines.length; i++) 445 { 446 int numTabs = 0; 447 448 while ((numTabs < lines[i].length()) && (lines[i].charAt(numTabs) == '\t')) 449 numTabs++; 450 451 if (numTabs == 0) 452 lines[i] = lines[i] + '\n'; 453 else 454 lines[i] = StringParse.nStrings(spaces, numTabs) + 455 lines[i].substring(numTabs) + '\n'; 456 } 457 458 StringBuilder sb = new StringBuilder(); 459 460 for (String line : lines) sb.append(line); 461 462 return sb.toString(); 463 } 464}