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 | 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.MatchResult;
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 InternalSED.ResultsSED handleOneFile(
final String fileName,
final FileNode file,
final String fileAsStr,
final CONFIG_RECORD userConfig
)
throws IOException
{
// Retrieve the starting String-index of each and every match in the file
final MatchResult[] matchResults =
StringParse.getAllMatches(fileAsStr, userConfig.regEx, true);
// If there aren't any matches, then skip to the next file.
if (matchResults.length == 0) return InternalSED.NO_CHANGES_RET;
// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
// For-Loop: Print the Matches to System.out
// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
// "Printing Stream" saves PrintingRecMultiLine instances.
final 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 (final MatchResult mr : matchResults)
{
// Overlapping matches need to be skipped
if (mr.start() < prevSaverRecord.matchPosEnd) continue;
final String replStr = userConfig.replaceFunction.apply(mr);
// if the user has supplied 'null', it is intended to be interpreted as "skip"
if (replStr == null) continue;
// If the user has just spat out the same exact original string, then just skip ahead
// to the next match. From the perspective of the UI-Interaction, this is kind of an
// important check.
//
// NOTE: I got *BURNED ALIVE* by the case / situation where "replStr.length() == 0"
// In that case, "regionMaches" is actually returning TRUE - because there is
// simply no comparison that is even occuring!
//
// THEREFORE: I have added the initial "replStr.length() > 0" pre-condition check!
if (replStr.length() > 0)
if (fileAsStr.regionMatches(
mr.start(), // toffset
replStr, // other
0, // ooffset
replStr.length() // len
))
continue;
// The "value" assigned to 'prevSaverRecord' is **NOT-UPDATED** until after it has been
// passed to the constructor's parameter.
prevSaverRecord = new PrintingRecMultiLine(
fileAsStr,
mr.start(), // match Starting-Position
mr.end(), // match Ending-Position
replStr,
prevSaverRecord,
userConfig.useUNIXColors
);
PRMLB.accept(prevSaverRecord);
}
// All Printing-Records as an Array
final PrintingRecMultiLine[] prmlArr =
PRMLB.build().toArray(PrintingRecMultiLine[]::new);
// Note that it is possible that there still weren't any changes, due to the check that
// goes on inside the for-loop
if (prmlArr.length == 0) return InternalSED.NO_CHANGES_RET;
// unless Verbosity.Silent was requested, print the matches.
if (userConfig.verbosity.level > 0) PrintingRecMultiLine.printAll
(prmlArr, userConfig.appendable, userConfig.useUNIXColors, userConfig.verbosity);
// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
// Query the User, Write the Replacement
// *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
if (userConfig.askFirst) if (! Q.YN("Re-Write the Updated File to Disk?"))
return InternalSED.NO_CHANGES_RET;
final StringBuilder newFileText = new StringBuilder();
PrintingRecMultiLine prevPRML = null;
for (final PrintingRecMultiLine prml : prmlArr)
{
// Doing this on a separate line so it is readable, could insert into line below
final int prevEndIndex = (prevPRML == null) ? 0 : prevPRML.matchPosEnd;
newFileText
.append(fileAsStr.substring(prevEndIndex, prml.matchPosStart))
.append((prevPRML = prml).replaceStr);
}
// A.I. told me to add this line. I haven't checked it yet, but this is usually
// exactly what you are supposed to do after a loop like the one directly above
newFileText.append(fileAsStr.substring(prevPRML.matchPosEnd));
return new InternalSED.ResultsSED(newFileText.toString(), prmlArr.length);
}
}
|