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 153 154 155 156 157 158 159 160 161 162 | package Torello.Java; class RemoveGeneric { static String remove(String typeAsStr) { int leftPos = typeAsStr.indexOf('<'); if (leftPos == -1) { int pos = typeAsStr.indexOf('>'); if (pos == -1) return typeAsStr.trim(); throw REM_GENERIC_ERROR_MSG(typeAsStr, pos); } char[] cArr = typeAsStr.toCharArray(); int count = 1; // The number of OPENING-CLOSING tags (same as Inclusive) int END = cArr.length; // This is the location JUST-AFTER the last USEABLE-char int delta = 0; // How many characters have been deleted already. // NOTE: This is zero, because the loop hasn't started. // If there is a "Shift" this will be PRECISELY-EQUAL // to the size of the last generic parameter-expression. // ALSO: The only purpose of this is for error-reporting. // check for a closing '>' before the first opening '<' for (int j=0; j < leftPos; j++) if (cArr[j] == '>') throw REM_GENERIC_ERROR_MSG(typeAsStr, j); // Check for in-valid characters // // This is a lot of lines of code, but these methods are extremely short, and the input // string (for all VALID) input will be very short. This is peace of mind. It checks... for (int pos=0; pos < cArr.length; pos++) { char c = cArr[pos]; if (! Character.isJavaIdentifierPart(c)) if (! Character.isIdentifierIgnorable(c)) if (! Character.isWhitespace(c)) if ( (c != '[') && (c != ']') && (c != '?') && (c != '<') && (c != '>') && (c != ',') && (c != '.') ) throw REM_GENERIC_ERROR_MSG(typeAsStr, pos); } do { // Keeps a count on the number of "Opening Braces" and "Closing Braces" // This is the same thing as the whole "Inclusive" deal, but with braces instead. // // count: At loop start, count is '1' If it ever reaches 0, the loop exits. // leftPos: The location of the '<' that has been found. int i = leftPos + 1; while ((count > 0) && (i < END)) { if (cArr[i] == '<') count++; else if (cArr[i] == '>') count--; if (count > 0) i++; } // The '<' and the '>' didn't match up. Better to throw exception, than ignore it. if ((count != 0) && (i == END)) throw REM_GENERIC_ERROR_MSG(typeAsStr, leftPos); int rightPos = i; // 'i' is currently pointing to the '>' // Erase the most recently found <...> expression int sourcePos = rightPos + 1; // Pointing at first VALID / NEED-TO-COPY char int destPos = leftPos; // Pointing at '<' boolean possiblyAnother = false; while (sourcePos < END) { // The next character to copy... check it first to see if it is valid! char c = cArr[sourcePos]; // continue to shift all the characters left to erase the expression. cArr[destPos] = c; if (! possiblyAnother) // Haven't found an opening '<' { // If there is a '>' - ***AND NO '<' HAS BEEN FOUND***, this is an error. if (c == '>') throw REM_GENERIC_ERROR_MSG(typeAsStr, delta + sourcePos); // If there is another '<', then it is possible another expression awaits us if (c == '<') { // Reset the outer-loop variables for the next iteration. There is going // to be another iteration - guaranteed. // // NOTE: Delta is supposed to hold how many characters are being deleted. // This is used for proper error-reporting (only) // // This is how many chars are in the current <...> expression delta = rightPos - leftPos + 1; leftPos = destPos; // Now pointing at the next open '<' char (just found!) count = 1; // There was a new-unclosed '>', prepares for next loop // You know it possiblyAnother = true; } } sourcePos++; destPos++; } // Completed without errors, and without another expression being found. // NOTE: This used to be a one-line return call. // ADDED: This now does a String.trim(). These little loops skip leading and // trailing white-space BEFORE returning the String // // WORKS-NO-TRIM: return new String(cArr, 0, destPos); // replace loop-body with the above line to get rid of trim() if (! possiblyAnother) { int sPos = 0; int len = destPos; // REMEMBER: new String(char[], int OFFSET, int COUNT) // NOT: new String(char[], int SPOS, int EPOS) // Skip LEADING-WHITESPACE while ((sPos < cArr.length) && (destPos > 0) && Character.isWhitespace(cArr[sPos])) { sPos++; destPos--; } // Advance start, *AND* shorten "count" // Skip TRAILING WHITE-SPACE while ((destPos > 1) && Character.isWhitespace(cArr[sPos + destPos-1])) destPos--; // Shorten length *ONLY* return new String(cArr, sPos, destPos); } END = destPos; // Pointing at the first invalid / unused / ALREADY-MOVED char } while (true); } private static StringFormatException REM_GENERIC_ERROR_MSG(String s, int charPos) { return new StringFormatException( /* "The opening '<' and closing '>' symbols in the type-string have not been " + "properly placed.\n" + */ "Generic Type-String Error, Beginning at Noted Location:\n" + StrSource.caretBeneath(s, charPos) ); } } |