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

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

class StrArrToCharArr
{
    static String replace(
                final boolean   ignoreCase,
                final String    s,
                final String[]  matchStrs,
                final char[]    replaceChars
            )
    {
        // Check that these arrays are parallel, and if not, throw ParallelArrayException
        // If 'matchStr' has a null, throw NullPointerException

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


        // The first stream is used to save the indices where matches are found.
        // The second stream is used to save WHICH MATCH has occurred, in order to retrieve the
        // replacement character.
        //
        // NOTE: This builder used to save the indices where matches are found.
        //
        // The IntStream that we are building will be "interleaved" in the sense that each integer
        // in an *EVEN* location in the output array shall represent the starting-index of a 
        // sub-string match, and the *ODD* integer (the one that immediately follows it) represents
        // the ending-index of a sub-string match.

        IntStream.Builder whereB = IntStream.builder();

        // This is saving which match occurred
        IntStream.Builder whichB = IntStream.builder();

        // This is used to keep track of the size-change of the output string
        int delta = 0;


        // Main Loop: Builds a replacement StringBuilder.  Looks for matches between the input
        // String 's', and the matchStrs in array String[] matchStrs.

        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()))
                {
                    // SEE NOTE ABOVE: When a match is found, the starting-index of the 
                    // substring match is appended to the IntStream, then IMMEDIATELY AFTERWARDS
                    // the ending-index of the substring match is appended.

                    int len = matchStrs[j].length();

                    whereB.accept(i);
                    whereB.accept(i + len);


                    // The second IntStream shall store WHICH MATCH has occurred.  The match that
                    // occurred is identified by an array-index into the "replaceChars" char[] array
                    // that was provided by the user to this method through the parameter list at
                    // the top of this.

                    whichB.accept(j);


                    // The change in size of the output String is precisely the length of the
                    // match minus 1.  A substring is being replaced by a character.

                    delta += (len - 1);


                    // A Match was found, so skip to the next location.  Move past the match
                    // that was just identified.

                    i += (len -1);
                    continue TOP;
                }
        
        // Keeps track of all the locations in the original string where matches occurred.
        int[] whereArr = whereB.build().toArray();
        int[] whichArr = whichB.build().toArray();

        // If there were no matches, return the original String
        if (whichArr.length == 0) return s;

        // The output string will be stored here.
        char[] cArr = new char[s.length() - delta];

        // These are the array indices of both arrays, and the original string
        int i           = 0;    // Index into Match-String Location Pointer Array 
        int oldStrPos   = 0;    // Pointer / index into Input-String
        int newStrPos   = 0;    // Pointer / index into Output Char-Array
        int rArrPos     = 0;    // Pointer / index into Replace Characters Char-Array

        // Iterate and replace the substrings.
        while (i < whereArr.length)
        {
            if (oldStrPos == whereArr[i])
            {
                // Retrieve the replacement character from the 'replaceChars' array.
                // The *CORRECT INDEX* from the replacement-char array is the next location
                // in the whichArr... And *THIS INDEX* is called 'rArrPos'
                // NOTE: Good Variable Names became uncompromisingly difficult here. 

                cArr[newStrPos++] = replaceChars[whichArr[rArrPos++]];


                // Advance the "pointers" (advance the array indices)
                //
                // The pointer to the "source array" (a.k.a. the "original array") should be
                // advanced to the location of the end of the match we just found.  That end
                // was pointed at by the start-end *PAIR* whereArr[i] ... whereArr[i+1].
                //
                // NOTE: the substring match is "exclusive of" the actual character
                //       located at whereArr[i+1].  Specifically, in the original string, there
                //       was a sub-string match to replace with a single character that began
                //       at original-string index/location whereArr[i] and continuing to 
                //       index/location (whereArr[i+1]-1)
                //
                //       Java's java.lang.String.subtring(star, end) is *ALWAYs* exclusive of
                //       the character located at 'end'

                oldStrPos = whereArr[i+1];


                // Skip the "pair" (starting-index and ending-index).   Note that at the end
                // of the loop, variable 'i' shall *ALWAYS* be an even-number.

                i += 2;
            }

            else cArr[newStrPos++] = s.charAt(oldStrPos++);
        }


        // Just like in HTMLNode Parse, the trailing ("tail") of characters after the final
        // match in the String need to be append to the output char[] array.  These are "missed"
        // or "skipped" by the above replacement loop.  (Similar to a "trailing read")
        //
        // OLD CODE - REPLACED WITH LINE BELOW
        // while (newStrPos < cArr.length) cArr[newStrPos++] = s.charAt(oldStrPos++);

        s.getChars(oldStrPos, s.length(), cArr, newStrPos);

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