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

import Torello.Java.ParallelArrayException;
import java.util.stream.IntStream;

class StrArrToStrArr
{
    static String replace(
            final boolean   ignoreCase,
            final String    s,
            final String[]  matchStrs,
            final String[]  replaceStrs
        )
    {
        // Make sure these arrays are parallel, and if not throw ParallelArrayException
        // If there are any 'null' values in these arrays, throw NullPointerException

        ParallelArrayException.check
            (matchStrs, "matchStrs", true, replaceStrs, "replaceStrs", true);

        // Java Stream's shall keep records of where and which the matches occurred
        IntStream.Builder   whereB  = IntStream.builder();
        IntStream.Builder   whichB  = IntStream.builder();
        int                 delta   = 0;


        // This part of the code finds the locations of all the matches in the input string.
        // It does not build the new String, but rather, finds indexes first.  This way a
        // char[] array can be built, and then populated with the updated sub-strings.

        TOP:
        for (int i=0; i < s.length(); i++)

            for (int j=0; j < matchStrs.length; j++)

                if (s.regionMatches(ignoreCase, i, matchStrs[j], 0, matchStrs[j].length()))
                {
                    // Save the "original String index" of WHERE the match occurred
                    whereB.accept(i);

                    // Save the "match index" of WHICH match has occurred
                    whichB.accept(j);


                    // Keep a record of the 'delta' - which is the change in size of the 
                    // output/returned String

                    delta = delta - matchStrs[j].length() + replaceStrs[j].length();
                    
                    // Make sure to advance the index-pointer, skip the most recent match
                    i += matchStrs[j].length() - 1;
                    
                    continue TOP;
                }

        // List of indices into the input-String for WHERE matches occurred.
        int[] whereArr = whereB.build().toArray();

        // List of indices into the match-array for WHICH matches occurred.
        int[] whichArr = whichB.build().toArray();


        // The new "Char Array" which will be built into a String.  The "change in size" was
        // computed earlier

        char[] cArr = new char[s.length() + delta];


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // If there were no matches, return the original string
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        if (whereArr.length == 0) return s;


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // "Pre-Loop Priming Update" or "Priming Read"
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // This just copies the first non-matching sub-string portion to the cArr[]

        s.getChars(0, whereArr[0], cArr, 0);

        // These are the loop-control variables
        int oldStrPos   = whereArr[0];
        int newStrPos   = whereArr[0];
        int i           = 0;

        while (i < whichArr.length)
        {
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Copy the next match from the "Replacement Strings Array"
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

            String  replaceStr  = replaceStrs[whichArr[i]];

            replaceStr.getChars(0, replaceStr.length(), cArr, newStrPos);

            // Advance the pointers
            newStrPos += replaceStr.length();
            oldStrPos += matchStrs[whichArr[i]].length();
            i++;


            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Copy the next non-matching sub-string section from the "Old Input String"
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

            int end = (i < whichArr.length) ? whereArr[i] : s.length();
            s.getChars(oldStrPos, end, cArr, newStrPos);

            // Advance the pointers
            newStrPos += (end - oldStrPos);
            oldStrPos = end;
        }

        // Convert the character array into a String
        return new String(cArr);
    }    
}