1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | package Torello.Java; import Torello.Java.Verbosity; import Torello.Java.IOExceptionHandler; import Torello.Java.FileNode; import Torello.Java.FileRW; import Torello.Java.StringParse; import Torello.Java.Q; import Torello.Java.Additional.AppendableSafe; import Torello.Java.Additional.BiAppendable; import java.util.regex.Pattern; import java.util.regex.MatchResult; import java.util.List; import java.util.function.Function; import java.util.function.Consumer; import java.util.stream.Stream; import java.util.stream.Collectors; import java.io.IOException; class MultiLineRegExMatch { // Re-Use the Pointer, I guess private static final String I4 = Helper.I4; // Only Method static List<FileNode> match( final Iterable<FileNode> files, final Pattern regEx, final Function<MatchResult, String> replaceFunction, final boolean askFirst, final IOExceptionHandler ioeh, final Appendable outputSaver, final boolean useUNIXColors, final Verbosity verbosity ) throws IOException { Helper.CHECK(askFirst, verbosity); // This Stream-Builder contains the list of FileNode's that are modified. Stream.Builder<FileNode> ret = Stream.builder(); Appendable appendable = (outputSaver == null) // If no 'outputSaver' was provided, then just send text to Standard-Out ? System.out // This just allows for printing to **BOTH** System.out **AND** the 'outputSaver' : new BiAppendable (System.out, new AppendableSafe(outputSaver, AppendableSafe.USE_APPENDABLE_ERROR)); // A very short "Consumer" that really just prints the file-name, nothing more. Consumer<String> fileNamePrinter = Helper.getFileNamePrinter (appendable, useUNIXColors, verbosity.level); // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // MAIN-LOOP: Iterate all the FileNode's // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** for (FileNode file : files) { // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // Load the File, and then Find any / all Matches // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** String fileName = file.getFullPathName(); String fileAsStr = null; // Print the file-name, if the verbosity level mandates that it be printed fileNamePrinter.accept(fileName); try { fileAsStr = FileRW.loadFileToString(fileName); } catch (IOException e) { if (ioeh != null) ioeh.accept(file, e); else throw e; // if ioeh ignores the exception rather than halting the program, then just continue // the loop on to the next match continue; } // Retrieve the starting String-index of each and every match in the file MatchResult[] matchResults = StringParse.getAllMatches(fileAsStr, regEx, true); // If there aren't any matches, then skip to the next file. if (matchResults.length == 0) continue; // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // For-Loop: Print the Matches to System.out // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // "Printing Stream" saves PrintingRecMultiLine instances. Stream.Builder<PrintingRecMultiLine> PRMLB = Stream.builder(); // Loop-Variable for retaining the previous PrintingRecMultiLine. It has the File // Line-Number information for the previous match. The Previous-Line Number info is // how Line-Number's are computed much more efficiently. PrintingRecMultiLine prevSaverRecord = null; for (MatchResult mr : matchResults) PRMLB.accept( prevSaverRecord = new PrintingRecMultiLine( fileAsStr, mr.start(), // match Starting-Position mr.end(), // match Ending-{osition replaceFunction.apply(mr), // NOTE: The "value" assigned to this reference is **NOT-UPDATED** until // after it has been passed to this method's parameter. prevSaverRecord, useUNIXColors )); // All Printing-Records as an Array PrintingRecMultiLine[] recs = PRMLB.build().toArray(PrintingRecMultiLine[]::new); if (recs.length == 0) continue; // unless Verbosity.Silent was requested, print the matches. if (verbosity.level > 0) PrintingRecMultiLine.printAll(recs, appendable, useUNIXColors, verbosity); // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // Query the User, Write the Replacement // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** if (askFirst) if (! Q.YN("Re-Write the Updated File to Disk?")) continue; StringBuilder newFileText = new StringBuilder(); PrintingRecMultiLine prevPRML = null; for (PrintingRecMultiLine prml : recs) { // Doing this on a separate line so it is readable, could insert into line below int prevEndIndex = (prevPRML == null) ? 0 : prevPRML.matchPosEnd; newFileText .append(fileAsStr.substring(prevEndIndex, prml.matchPosStart)) .append((prevPRML = prml).replaceStr); } try { FileRW.writeFile(newFileText, fileName); } catch (IOException e) { if (ioeh != null) ioeh.accept(file, e); else throw e; // if ioeh ignores the exception rather than halting the program, then just continue // the loop on to the next match continue; } // In "Verbosity.Verbose" mode, tell the user how many changes were updated in the file if (verbosity.level == 3) appendable.append(I4 + "Updated " + recs.length + " Matches"); // This is a file that was updated, so put it in the returned list of "updated files" ret.accept(file); } // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** // Finished, so return the list of modified FileNode's // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** return ret.build().collect(Collectors.toList()); } } |