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
package Torello.Java;

import Torello.Java.Verbosity;
import Torello.Java.IOExceptionHandler;
import Torello.Java.FileNode;
import Torello.Java.FileRW;
import Torello.Java.StrIndexOf;
import Torello.Java.Q;

import Torello.Java.Additional.AppendableSafe;
import Torello.Java.Additional.BiAppendable;

import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.io.IOException;

class MultiLineStrMatch
{
    // Re-Use the Pointer, I guess
    private static final String I4 = Helper.I4;

    static void CHECK_STRS(final String matchStr, final String replaceStr)
    {
        if (matchStr.equals(replaceStr)) throw new IllegalArgumentException(
            "The String passed to parameter 'matcheStr' is identical to the String passed to " +
            "parameter 'replaceStr'.  There is nothing for SED to do."
        );
    }

    // 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 int[] posArr = StrIndexOf.all(fileAsStr, userConfig.matchStr);

        if (posArr.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 (int i=0; i < posArr.length; i++)
        {
            // Loop-Variable that represents the current match, indexing the 'posArr' Array
            final int pos = posArr[i];

            // Overlapping matches need to be skipped
            if ((i > 0) && (pos < (posArr[i-1] + userConfig.MATCH_STR_LEN))) continue;

            else PRMLB.accept(

                prevSaverRecord = new PrintingRecMultiLine(
                    fileAsStr,
                    pos,                            // match Starting-Position
                    pos + userConfig.MATCH_STR_LEN, // match Ending-{osition
                    userConfig.replaceStr,


                    // NOTE: The "value" assigned to this reference is **NOT-UPDATED** until
                    //       after it has been passed to this method's parameter.

                    prevSaverRecord,
                    userConfig.useUNIXColors
                ));
        }

        // All Printing-Records as an Array
        final PrintingRecMultiLine[] prmlArr =
            PRMLB.build().toArray(PrintingRecMultiLine[]::new);

        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;


        // Remember, this single line of code does the actual replacement!
        String newFileAsStr = fileAsStr.replace(userConfig.matchStr, userConfig.replaceStr);

        return new InternalSED.ResultsSED(newFileAsStr, prmlArr.length);
    }
}