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); } } |