001package Torello.Java; 002 003import static Torello.Java.C.*; 004 005import java.io.IOException; 006import java.io.FilenameFilter; 007import java.io.File; 008 009import java.util.List; 010import java.util.regex.Pattern; 011import java.util.regex.MatchResult; 012import java.util.function.Function; 013 014import Torello.JavaDoc.LinkJavaSource; 015 016/** 017 * Based on the UNIX {@code 'SED'} Command, this class updates files based on 018 * <B STYLE='color: red;'><I>either</I></B> {@code String}-Literal matches & replacements, or 019 * <B STYLE='color: red;'><I>or</I></B> Regular-Expression matches. 020 * 021 * <EMBED CLASS='external-html' DATA-FILE-ID=SED> 022 */ 023@Torello.JavaDoc.StaticFunctional 024public class SED 025{ 026 private SED() { } 027 028 029 // ******************************************************************************************** 030 // ******************************************************************************************** 031 // Some Filters 032 // ******************************************************************************************** 033 // ******************************************************************************************** 034 035 036 /** A {@code java.io.FilenameFilter} that looks for JavaDoc Upgrader HTML-Files. */ 037 public static final FilenameFilter filterFilesHTML = (File dir, String fileName) -> 038 { 039 if (! fileName.endsWith(".html")) return false; 040 041 if (! StrCmpr.containsAND(dir.getPath(), "upgrade-files", "external-html")) return false; 042 043 return true; 044 }; 045 046 /** A {@code java.io.FilenameFilter} that looks for {@code '.java'} Files. */ 047 public static final FilenameFilter filterFilesJava = (File dir, String fileName) -> 048 { 049 if (! fileName.endsWith(".java")) return false; 050 051 return true; 052 }; 053 054 055 // ******************************************************************************************** 056 // ******************************************************************************************** 057 // Single-Line String-Match 058 // ******************************************************************************************** 059 // ******************************************************************************************** 060 061 062 /** 063 * Convenience Method. 064 * <BR />Invokes: {@link #singleLine(Iterable, String, String, boolean, IOExceptionHandler, 065 * Appendable, boolean, Verbosity)} 066 * <BR />Passes: <CODE> 067 * <B STYLE='color: blue'>askFirst</B>: TRUE, 068 * <B STYLE='color: blue'>ioeh</B>: null, 069 * <B STYLE='color: blue'>outputSaver</B>: null, 070 * <B STYLE='color: blue'>useUNIXColors</B>: TRUE, 071 * <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal 072 * </CODE> 073 */ 074 @LinkJavaSource(handle="SingleLineStrMatch") 075 @LinkJavaSource(handle="PrintingRecSingleLine") 076 public static List<FileNode> singleLine 077 (Iterable<FileNode> files, String matchStr, String replaceStr) 078 throws IOException 079 { 080 return SingleLineStrMatch.match 081 (files, matchStr, replaceStr, true, null, null, true, Verbosity.Normal); 082 } 083 084 /** 085 * Makes updates to Text-Files listed in parameter {@code 'files'}. 086 * 087 * <BR /><BR />In the example below, all {@code '.java'} Source-Code Files in the Java-HTML 088 * Library are loaded into a {@code Vector<FileNode>}. Using this particular {@code 'SED'} 089 * method, each substring that looks like <B STYLE='color: blue'>{@code <B>TRUE</B>}</B> (in 090 * all {@code '.java'} Source-Files) is converted to look like 091 * <B STYLE='color: blue'><CODE>{@code TRUE}</CODE></B> 092 * 093 * <DIV CLASS=EXAMPLE>{@code 094 * StringBuilder log = new StringBuilder(); 095 * 096 * // Load all Java-HTML Source-Files (in all packages) into a FileNode-Vector 097 * Vector<FileNode> files = FileNode 098 * .createRoot("Torello/") 099 * .loadTree(-1, SED.filterFilesJava, null) 100 * .flattenJustFiles(RTC.VECTOR()); 101 * 102 * // Invoke this method 103 * singleLine(files, "<B>TRUE</B>", "{@code TRUE}", true, null, log, true, Verbosity.Normal); 104 * 105 * // Save the Changes that were made to a log 106 * FileRW.writeFile(log, "ChangesMade.log.txt"); 107 * }</DIV> 108 * 109 * @param matchStr Any Java {@code String}-Literal. It is required that this {@code String} 110 * not contain any new-line ({@code '\n'}) characters, or an {@code IllegalArgumentException} 111 * will throw. 112 * 113 * @param replaceStr For all {@code 'files'}, each instance of the {@code String}-Literal 114 * parameter {@code 'matchStr'} found in any file will be replaced by {@code 'replaceStr'}. 115 * 116 * <BR /><BR />As with parameter {@code 'matchStr'}, if this {@code String} contains any 117 * new-line ({@code '\n'}) characters, an {@code IllegalArgumentException} will throw. 118 * 119 * @return A list of {@link FileNode} instances whose File-Contents were modified / updated 120 * by this method. 121 * 122 * @throws IllegalArgumentException If either of the {@code String} parameters 123 * {@code 'matchStr'} or {@code 'replaceStr'} contain any newline ({@code '\n'}) characters. 124 * 125 * <BR /><BR />If a {@code 'SED'} replacement needs to be done using a {@code String}-Match 126 * that spans more than one line, use the Multi-Line method provided in this class. 127 * 128 * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR> 129 * @throws IOException On File-System read / write error. 130 */ 131 @LinkJavaSource(handle="SingleLineStrMatch") 132 @LinkJavaSource(handle="PrintingRecSingleLine") 133 public static List<FileNode> singleLine( 134 Iterable<FileNode> files, 135 String matchStr, 136 String replaceStr, 137 boolean askFirst, 138 IOExceptionHandler ioeh, 139 Appendable outputSaver, 140 boolean useUNIXColors, 141 Verbosity verbosity 142 ) 143 throws IOException 144 { 145 return SingleLineStrMatch.match 146 (files, matchStr, replaceStr, askFirst, ioeh, outputSaver, useUNIXColors, verbosity); 147 } 148 149 150 // ******************************************************************************************** 151 // ******************************************************************************************** 152 // Single-Line Regular-Expression-Match 153 // ******************************************************************************************** 154 // ******************************************************************************************** 155 156 157 /** 158 * Convenience Method. 159 * <BR />Invokes: {@link #singleLine(Iterable, Pattern, Function, boolean, IOExceptionHandler, 160 * Appendable, boolean, Verbosity)} 161 * <BR />Passes: <CODE> 162 * <B STYLE='color: blue'>askFirst</B>: TRUE, 163 * <B STYLE='color: blue'>ioeh</B>: null, 164 * <B STYLE='color: blue'>outputSaver</B>: null, 165 * <B STYLE='color: blue'>useUNIXColors</B>: TRUE, 166 * <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal 167 * </CODE> 168 */ 169 @LinkJavaSource(handle="SingleLineRegExMatch") 170 @LinkJavaSource(handle="PrintingRecSingleLine") 171 public static List<FileNode> singleLine 172 (Iterable<FileNode> files, Pattern regEx, Function<MatchResult, String> replaceFunction) 173 throws IOException 174 { 175 return SingleLineRegExMatch.match 176 (files, regEx, replaceFunction, true, null, null, true, Verbosity.Normal); 177 } 178 179 /** 180 * Makes updates to Text-Files listed in parameter {@code 'files'}. Matches that are eligible 181 * for replacement are identified using the Regular-Expression parameter {@code 'regEx'}. 182 * 183 * <BR /><BR />Replacement-{@code String's} are obtained by invoking the 184 * {@code 'replaceFunction'} parameter. 185 * 186 * <BR /><BR />In the example below, all Java HTML Library Source-Files are loaded into a 187 * {@code Vector<FileNode>}, and then iterated by this method. In each file, any instances of 188 * an {@code EMBED}-Tag that contains a {@code FILE-ID} Data-Attribute whose value is wrapped 189 * in Double Quotation-Marks has that value extracted and re-inserted without any 190 * Quotation-Marks. 191 * 192 * <DIV CLASS=EXAMPLE>{@code 193 * StringBuilder log = new StringBuilder(); 194 * 195 * // Load all Java-HTML Source-Files (in all packages) into a FileNode-Vector 196 * Vector<FileNode> files = FileNode 197 * .createRoot("Torello/") 198 * .loadTree(-1, filterFilesJava, null) 199 * .flattenJustFiles(RTC.VECTOR()); 200 * 201 * // Matches an <EMBED> Tag's FILE-ID Data-Attribute. There are parenthesis placed around the 202 * // actual ID-Name. The contents of the parenthesis are "Reg-Ex Match-Group #1" 203 * 204 * Pattern regEx = Pattern.compile("DATA-FILE-ID=\"(\\w+)\""); 205 * 206 * // This Function extracts Reg-Ex Group-1, and re-inserts it **WITHOUT** the Double 207 * // Quotation-Marks that were placed around it. Look Closely at Previous Line of Code, the 208 * // 'replacer' function does not include the Double-Quotes. 209 * 210 * Function<MatchResult, String> replacer = (MatchResult mr) -> "DATA-FILE-ID=" + mr.group(1); 211 * 212 * // Invoke this method 213 * singleLine(files, regEx, replacer, true, null, log, true, Verbosity.Normal); 214 * 215 * // Write the changes that have been made to a log-file 216 * FileRW.writeFile(log, "ChangesMade.log.txt"); 217 * }</DIV> 218 * 219 * @param regEx Any Java Regular-Expression. This will be used to match against the text 220 * inside each file returned by the {@code Iterable} parameter {@code 'files'}. 221 * 222 * <BR /><BR />If this Regular-Expression returns a match that spans more than a single line of 223 * text, this method will throw an exception. To perform matches that span multiple lines of 224 * text, use the Multi-Line methods, provided by this class 225 * 226 * @param replaceFunction Any Lambda-Expression or Function-Pointer that accepts a 227 * {@code java.util.regex.MatchResult}, and returns a Replacement-{@code String}. 228 * 229 * <BR /><BR />If this Replace-Function returns a match that contains any newline {@code '\n'} 230 * characters, this method will throw a {@link RegExException}. 231 * 232 * @return A list of {@link FileNode} instances whose File-Contents were modified / updated 233 * by this method. 234 * 235 * @throws RegExExpression Throws if the {@code Pattern} parameter {@code 'regEx'} returns a 236 * match that covers more than one line of text in any of the files that are scanned. 237 * 238 * <BR /><BR />This exception will also throw if the Lambda-Target / Function-Pointer parameter 239 * {@code 'replaceFunction'} ever returns a {@code String} that spans multiple lines. 240 * 241 * <BR /><BR />In both of these cases, the logic simply checks for the presence of a newline 242 * {@code '\n'} character. 243 * 244 * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR> 245 * @throws IOException On File-System read / write error. 246 */ 247 @LinkJavaSource(handle="SingleLineRegExMatch") 248 @LinkJavaSource(handle="PrintingRecSingleLine") 249 public static List<FileNode> singleLine( 250 Iterable<FileNode> files, 251 Pattern regEx, 252 Function<MatchResult, String> replaceFunction, 253 boolean askFirst, 254 IOExceptionHandler ioeh, 255 Appendable outputSaver, 256 boolean useUNIXColors, 257 Verbosity verbosity 258 ) 259 throws IOException 260 { 261 return SingleLineRegExMatch.match 262 (files, regEx, replaceFunction, askFirst, ioeh, outputSaver, useUNIXColors, verbosity); 263 } 264 265 266 // ******************************************************************************************** 267 // ******************************************************************************************** 268 // Multi-Line String-Match 269 // ******************************************************************************************** 270 // ******************************************************************************************** 271 272 273 /** 274 * Convenience Method. 275 * <BR />Invokes: {@link #multiLine(Iterable, String, String, boolean, IOExceptionHandler, 276 * Appendable, boolean, Verbosity)} 277 * <BR />Passes: <CODE> 278 * <B STYLE='color: blue'>askFirst</B>: TRUE, 279 * <B STYLE='color: blue'>ioeh</B>: null, 280 * <B STYLE='color: blue'>outputSaver</B>: null, 281 * <B STYLE='color: blue'>useUNIXColors</B>: TRUE, 282 * <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal 283 * </CODE> 284 */ 285 @LinkJavaSource(handle="MultiLineStrMatch") 286 @LinkJavaSource(handle="PrintingRecMultiLine") 287 public static List<FileNode> multiLine 288 (Iterable<FileNode> files, String matchStr, String replaceStr) 289 throws IOException 290 { 291 return MultiLineStrMatch.match 292 (files, matchStr, replaceStr, true, null, null, true, Verbosity.Normal); 293 } 294 295 296 /** 297 * Makes updates to Text-Files listed in parameter {@code 'files'}. 298 * 299 * <BR /><BR /><B CLASS=JDDescLabel>Single-Line & Multi-Line:</B> 300 * 301 * <BR />If either parameters {@code 'matchStr'} or {@code 'replaceStr'} receive a 302 * {@code String} that does not actually contain any new-line characters ({@code '\n'}), 303 * <I>this method will not throw any exceptions. All User-Requested Text-Updates will be 304 * processed to completion!</I> 305 * 306 * <BR /><BR />This somewhat in contrast to method {@code 'singleLine'} which will, indeed, 307 * throw an {@code IllegalArgumentException} if either {@code 'matchStr'} or 308 * {@code 'replaceStr'} contain newline characters. 309 * 310 * @param matchStr Any Java {@code String}-Literal. 311 * 312 * @param replaceStr For all {@code 'files'}, each instance of the {@code String}-Literal 313 * parameter {@code 'matchStr'} found in any file will be replaced by {@code 'replaceStr'}. 314 * 315 * @return A list of {@link FileNode} instances whose File-Contents were modified / updated 316 * by this method. 317 * 318 * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR> 319 * @throws IOException On File-System read / write error. 320 */ 321 @LinkJavaSource(handle="MultiLineStrMatch") 322 @LinkJavaSource(handle="PrintingRecMultiLine") 323 public static List<FileNode> multiLine( 324 Iterable<FileNode> files, 325 String matchStr, 326 String replaceStr, 327 boolean askFirst, 328 IOExceptionHandler ioeh, 329 Appendable outputSaver, 330 boolean useUNIXColors, 331 Verbosity verbosity 332 ) 333 throws IOException 334 { 335 return MultiLineStrMatch.match 336 (files, matchStr, replaceStr, askFirst, ioeh, outputSaver, useUNIXColors, verbosity); 337 } 338 339 340 // ******************************************************************************************** 341 // ******************************************************************************************** 342 // Multi-Line Regular-Expression Match 343 // ******************************************************************************************** 344 // ******************************************************************************************** 345 346 347 /** 348 * Convenience Method. 349 * <BR />Invokes: {@link #multiLine(Iterable, Pattern, Function, boolean, IOExceptionHandler, 350 * Appendable, boolean, Verbosity)} 351 * <BR />Passes: <CODE> 352 * <B STYLE='color: blue'>askFirst</B>: TRUE, 353 * <B STYLE='color: blue'>ioeh</B>: null, 354 * <B STYLE='color: blue'>outputSaver</B>: null, 355 * <B STYLE='color: blue'>useUNIXColors</B>: TRUE, 356 * <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal 357 * </CODE> 358 */ 359 @LinkJavaSource(handle="MultiLineRegExMatch") 360 @LinkJavaSource(handle="PrintingRecMultiLine") 361 public static List<FileNode> multiLine 362 (Iterable<FileNode> files, Pattern regEx, Function<MatchResult, String> replaceFunction) 363 throws IOException 364 { 365 return MultiLineRegExMatch.match 366 (files, regEx, replaceFunction, true, null, null, true, Verbosity.Normal); 367 } 368 369 /** 370 * Makes updates to Text-Files listed in parameter {@code 'files'}. 371 * 372 * <BR /><BR /><B CLASS=JDDescLabel>Single-Line & Multi-Line:</B> 373 * 374 * <BR />When unsure whether to use the {@code 'singleLine'} versus the {@code 'multiLine'} 375 * variants class {@coce 'SED'}, given a {@code 'regEx'} that might match either one or many 376 * lines of text - <I>always use the Multi-Line Version (this method) to avoid exception 377 * throws!</I> 378 * 379 * <BR /><BR />The main differences between methods {@code 'singleLine'} and 380 * {@code 'multiLine'} lays with the formatting of the User-Output Text that is printed by this 381 * method as matches are found. 382 * 383 * <BR /><BR />This method can easily handle falling back to printing only one line of 384 * Matching-Text. However, {@code 'singleLine'} is simply incapable of printing a Multi-Line 385 * Regular-Expression Match, and will instead throw an exception if such a match is identified. 386 * 387 * @param regEx Any Java Regular-Expression. This will be used to match against the text 388 * inside each file returned by the {@code Iterable} parameter {@code 'files'}. 389 * 390 * <BR /><BR />The Regular-Expression passed may match any number of characters or lines in the 391 * Source-File. If just one line of the original file is matched by {@code 'regEx'}, this is 392 * perfectly fine and will not produce any exception throws. 393 * 394 * <BR /><BR />This method's User Output-Text Printing-Mechanism is written to handle 395 * {@code 'regEx'} Match-Text of any size. 396 * 397 * @param replaceFunction Any Lambda-Expression or Function-Pointer that accepts a 398 * {@code java.util.regex.MatchResult}, and returns a Replacement-{@code String}. 399 * 400 * @return A list of {@link FileNode} instances whose File-Contents were modified / updated 401 * by this method. 402 * 403 * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR> 404 * @throws IOException On File-System read / write error. 405 */ 406 @LinkJavaSource(handle="MultiLineRegExMatch") 407 @LinkJavaSource(handle="PrintingRecMultiLine") 408 public static List<FileNode> multiLine( 409 Iterable<FileNode> files, 410 Pattern regEx, 411 Function<MatchResult, String> replaceFunction, 412 boolean askFirst, 413 IOExceptionHandler ioeh, 414 Appendable outputSaver, 415 boolean useUNIXColors, 416 Verbosity verbosity 417 ) 418 throws IOException 419 { 420 return MultiLineRegExMatch.match( 421 files, regEx, replaceFunction, askFirst, ioeh, outputSaver, 422 useUNIXColors, verbosity 423 ); 424 } 425}