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 | package Torello.Java; import Torello.Java.Function.ToCharIntTFunc; import Torello.Java.StringParse; import java.util.stream.IntStream; class StrArrToCharReplFunc { static String replace( final String s, final boolean ignoreCase, final String[] matchStrs, final ToCharIntTFunc<String> replaceFunction ) { // Loop simply checks for null pointers. for (int i=0; i < matchStrs.length; i++) if (matchStrs[i] == null) throw new NullPointerException( "The " + i + StringParse.ordinalIndicator(i) + " of array parameter " + "'matchStrs' is null." ); // 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 b = 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(); b.accept(i); b.accept(i + len); // 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 = b.build().toArray(); // If there were no matches, return the original String if (whereArr.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; // Match-String Location Pointer Array int oldStrPos = 0; // Pointer to Input-String index int newStrPos = 0; // Pointer to Output Char-Array index // Iterate and replace the substrings. while (i < whereArr.length) { if (oldStrPos == whereArr[i]) { // Ask the "Replace Function" for the replacement character for the matched // substring // // NOTE: Each *EVEN* location in the whereArr contains a start-index, and the // *ODD* location immediately following contains an end-index. These // start-end pair identify the substring matches that were found. // // NOW: Grab that substring, and pass it to the parameter-provided (user // provided) "replaceFunction" which informs this method what character // to use when replacing a substring cArr[newStrPos++] = replaceFunction.apply (whereArr[i], s.substring(whereArr[i], whereArr[i+1])); // 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 appended to the output char[] array. These are // "missed" or "skipped" by the above replacement loop. (Similar to a "trailing read") while (newStrPos < cArr.length) cArr[newStrPos++] = s.charAt(oldStrPos++); // AGAIN: All of the replacements where done on a "char[] array". Convert that array // to an actual String, and return it to the user. return new String(cArr); } } |