package tools; import java.util.ArrayList; /** * @author Jiangcheng Oliver Chu */ public class StrTools { public StrTools() { super(); } /** * Searches for a substring inside a mainString, where the searching begins * at beginIndex. Returns the offset where the substring starts. * @param mainString * @param beginIndex * @param subString * @return */ public static int search(String mainString,int beginIndex, String subString) { //Chop off the part of the string before the beginIndex, since we don't //need that part. //Equivalent to Python's mainString = mainString[beginIndex:] mainString = slice(mainString,beginIndex); int maxLen = mainString.length(); int subLen = subString.length(); char firstChar = subString.charAt(0); for (int x=0; x+subLen <= maxLen; x++) { //To avoid wasting time, we make sure the first character is //a match, and then check the whole string. if (mainString.charAt(x) == firstChar) { String testString = slice(mainString,x,x+subLen); if (testString.equals(subString)) return beginIndex + x; } } return -1; } /** * Searches for a substring inside a mainString, where the searching begins * at beginIndex. Returns the offset where the substring starts.<br /> * Does not pay attention to case (capitalization). * @param mainString * @param beginIndex * @param subString * @return */ public static int searchIgnoreCase(String mainString,int beginIndex, String subString) { //Remove the part of the string before the beginIndex, since we don't //need that part. //Equivalent to Python's mainString = mainString[beginIndex:] mainString = slice(mainString,beginIndex); int maxLen = mainString.length(); int subLen = subString.length(); String firstChar = ""+subString.charAt(0); for (int x=0; x+subLen <= maxLen; x++) { //To avoid wasting time, we make sure the first character is //a match, and then check the whole string. String checkedChar = ""+mainString.charAt(x); if (checkedChar.equalsIgnoreCase(firstChar)) { String testString = slice(mainString,x,x+subLen); if (testString.equalsIgnoreCase(subString)) return beginIndex + x; } } return -1; } /** * Searches for a substring inside a mainString, where the searching begins * at beginIndex. Returns the offset where the substring starts.<br /> * Does not pay attention to case (capitalization). * @param mainString * @param beginIndex * @param subString * @return */ public static int searchIgnoreCaseInRange(String mainString,int beginIndex, int endIndex, String subString) { if (beginIndex == endIndex) return -1; int subLen = subString.length(); String firstChar = ""+subString.charAt(0); for (int x = beginIndex; x+subLen <= endIndex; x++) { //To avoid wasting time, we make sure the first character is //a match, and then check the whole string. String checkedChar = ""+mainString.charAt(x); if (checkedChar.equalsIgnoreCase(firstChar)) { String testString = slice(mainString,x,x+subLen); if (testString.equalsIgnoreCase(subString)) return x; } } return -1; } public static int countOccurrences(String text,String subString, boolean isIgnoringCase) { int occurrences = 0; if (isIgnoringCase) { int position = 0; while (true) { position = searchIgnoreCase(text,position,subString); if (position == -1) break; position++; occurrences++; } } else { int position = 0; while (true) { position = search(text,position,subString); if (position == -1) break; position++; occurrences++; } } return occurrences; } public static String multiplyChars(char character,int multiplier) { String output = ""; for (int i = 0; i < multiplier; i++) { output += character; } return output; } public static int searchBackwards(String mainString,String subString) { int maxLen = mainString.length(); return searchBackwards(mainString,maxLen - 1,subString); } public static int searchBackwardsIgnoreCase(String mainString, String subString) { int maxLen = mainString.length(); return searchBackwardsIgnoreCase(mainString,maxLen - 1,subString); } /** * Searches for a substring inside a mainString, where the searching begins * at beginIndex. Returns the offset where the substring starts.<br /> * The searching is done backwards by decrementing the beginning index. * @param mainString * @param beginIndex * @param subString * @return */ public static int searchBackwards(String mainString,int startPos, String subString) { int subLen = subString.length(); char firstChar = subString.charAt(0); for (int x = startPos; x >= 0; x--) { //To avoid wasting time, we make sure the first character is //a match, and then check the whole string. if (mainString.charAt(x) == firstChar) { String testString = StrTools.slice(mainString,x,x+subLen); if (testString.equals(subString)) return x; } } return -1; } /** * Searches for a substring inside a mainString, where the searching begins * at beginIndex. Returns the offset where the substring starts.<br /> * The searching is done backwards by decrementing the beginning index. * <br />Does not care about case or capitalization. * @param mainString * @param beginIndex * @param subString * @return */ public static int searchBackwardsIgnoreCase(String mainString,int startPos, String subString) { int subLen = subString.length(); String firstChar = ""+subString.charAt(0); for (int x = startPos; x >= 0; x--) { //To avoid wasting time, we make sure the first character is //a match, and then check the whole string. String checkedChar = ""+mainString.charAt(x); /* TESTING */ // FileIO.appendTextStrToFile("The char: "+checkedChar+"\n"+ // "The string: "+StrTools.slice(mainString,x,x+subLen)+"\n","finder_debug.log"); /*END TESTING */ if (checkedChar.equalsIgnoreCase(firstChar)) { String testString = StrTools.slice(mainString,x,x+subLen); if (testString.equalsIgnoreCase(subString)) return x; } } return -1; } // public static String removeLastLine(String text) { // int startOfLastLine = searchBackwards(text,"\n"); // if (startOfLastLine == -1) // return text; // return StrTools.slice(text,0,startOfLastLine); // } public static boolean containsIgnoreCase(String text,String toBeFound) { int result = searchIgnoreCase(text,0,toBeFound); if (result == -1) return false; return true; } /** * This method converts a quoted string with escape characters in it to * a regular string. For example it will convert:<br /> * "C:\\Users\\Booster" to C:\Users\Booster<br /><br /> * Escape characters are specialized for ASM defines parsing. Here they * are:<br /> * \" is " (quote)<br /> * \\ is \<br /> * \/ is / (for using forward-slash comments inside defines)<br /> * \semi is ; (for using asm comments inside defines) * @param quotedString * @return simple string */ public static String convertQuotedEscapedTextToStr(String quotedString) { //Remove the quotes first. quotedString = slice(quotedString,1,-1); // Converts \" -> " quotedString = quotedString.replace("\\\"","\""); // Converts \\ -> \ quotedString = quotedString.replace("\\\\","\\"); // Converts \/ -> / quotedString = quotedString.replace("\\/","/"); // Converts \semi -> ; quotedString = quotedString.replace("\\semi",";"); return quotedString; } /** * Copies the contents of a string starting from the beginning offset index * and ending at the first occurrence of the char wantedChar after that * index. * If the char isn't found, return the rest of the string. * @param mainString * @param index * @param wantedChar * @return Copied substring (which does not include the wantedChar itself) */ public static String copyFromIndexToChar(String mainString,int index, char wantedChar) { mainString = slice(mainString,index); String outString = ""; int maxLen = mainString.length(); for (int x=0; x < maxLen; x++) { char currentChar = mainString.charAt(x); if (currentChar == wantedChar) return outString; outString += currentChar; } //If the char couldn't be found, return the whole rest of the string: return outString; } public static String copyFromIndexToStr(String mainString,int index, String wantedString) { mainString = slice(mainString,index); String outString = ""; int maxLen = mainString.length(); for (int x=0; x < maxLen; x++) { if (isSubstringAt(mainString,wantedString,x)) return outString; outString += mainString.charAt(x); } //If the substr couldn't be found, return the whole rest of the string: return outString; } /** * slice() is a more intelligent version of String.substring(beginIndex, * endIndex) * If endIndex is larger than the length of mainString, maxLen is used * instead of endIndex. * If beginIndex is greater than or equal to endIndex, "" will be returned. * (This method is intended to emulate Python's string slicing.)<br /> * Give a negative number for either index, and the index will count * backwards from the end of the string. So index = -6 really means * index = maxlength - 6 * @param mainString * @param beginIndex * @param endIndex * @return */ public static String slice(String mainString,int beginIndex,int endIndex) { int maxLen = mainString.length(); if (beginIndex < 0) beginIndex = maxLen + beginIndex; if (endIndex < 0) endIndex = maxLen + endIndex; if (endIndex > maxLen) endIndex = maxLen; if (beginIndex >= endIndex) return ""; return mainString.substring(beginIndex,endIndex); } /** * Returns mainString.substring(beginIndex), or returns "" if the * beginIndex is greater than the length of the mainString. * (This method is intended to emulate Python's string slicing.)<br /> * Give a negative number for beginIndex, and the slice will count * BACKWARDS for the starting index. * That is, slice(string,-8) is string[-8:] * @param mainString * @param beginIndex * @return */ public static String slice(String mainString,int beginIndex) { int maxLen = mainString.length(); if (beginIndex < 0) return slice(mainString,maxLen+beginIndex,maxLen); else if (beginIndex < maxLen) return mainString.substring(beginIndex); else return ""; } /** * Replaces known \n newlines with system newlines (\r\n for Windows). * @param instring * @return */ public static String convertNewlines(String instring) { String newline = System.getProperty("line.separator"); String output = instring.replace("\n",newline); return output; } /** * Converts Mac newlines (\r) or Windows newlines (\r\n) * into Unix newlines (\n) * @param instring * @return */ public static String unixifyNewlines(String instring) { String sysNewline = System.getProperty("line.separator"); String output = instring.replace(sysNewline,"\n"); return output; } private static int countCharsInString(String string,char wantedChar) { char[] stringAsChars = string.toCharArray(); int count = 0; for (char eachChar : stringAsChars) { if (eachChar == wantedChar) count++; } return count; } /** * Tests if mainString has the substr subString at the index beginIndex. * If not, returns false. Otherwise, returns true. * Useful for combing through a big string and checking for matches. * This method would be great inside a Find/Replace command. * @param mainString * @param subString * @param beginIndex * @return True only if subString begins at the beginIndex of mainString. */ public static boolean isSubstringAt(String mainString,String subString, int beginIndex) { String testedString = slice(mainString,beginIndex, beginIndex + subString.length()); return testedString.equals(subString); } /** * Searches text backwards until one of the strings given is found. * @param text * @param givenStrings * @param startPos * @return {position,index of the given string found} */ static int[] searchBackwardsForAnyGiven(String text,String[] givenStrings, int startPos) { int maxStrings = givenStrings.length; for (int x = startPos; x >= 0; x--) { for (int j = 0; j < maxStrings; j++) { if (isSubstringAt(text,givenStrings[j],x)) { int[] results = {x,j}; return results; } } } int[] results = {-1,-1}; return results; } /** * Removes all C++ style comments from a string, as well as ASM semicolon * comments. Does not remove linefeeds (\n). * @param commentedText * @return text with comments gone */ public static String removeCommentsAndPreserveNewlines(String commentedText) { int maxLen = commentedText.length(); int index = 0; String outString = ""; while (index < maxLen) { if (isSubstringAt(commentedText,"//",index)) { index = search(commentedText,index+2,"\n"); } else if (isSubstringAt(commentedText,"/*",index)) { int newIndex = 2+search(commentedText,index+2,"*/"); int newLineCount = countCharsInString( slice(commentedText,index+2,newIndex),'\n'); for (int i=0; i < newLineCount; i++) { outString += '\n'; } index = newIndex; } else if (isSubstringAt(commentedText,";",index)) { index = search(commentedText,index+1,"\n"); } if (index == -1) return outString; else if (index >= maxLen) break; else { outString += commentedText.charAt(index); index++; } } return outString; } /** * Removes all C++ style comments from a string, as well as ASM semicolon * comments.<br /> * Do not remove this method because we need it for binary table * compilation. * @param commentedText * @return text with comments gone */ public static String removeComments(String commentedText) { int maxLen = commentedText.length(); int index = 0; String outString = ""; while (index < maxLen) { if (isSubstringAt(commentedText,"//",index)) index = search(commentedText,index+2,"\n"); else if (isSubstringAt(commentedText,"/*",index)) index = 2+search(commentedText,index+2,"*/"); else if (isSubstringAt(commentedText,";",index)) index = search(commentedText,index+1,"\n"); if (index == -1) return outString; else if (index >= maxLen) break; else { outString += commentedText.charAt(index); index++; } } return outString; } /** * Removes the trailing and leading whitespace (tabs, spaces) for the * given string. * @param inString * @return inString with leading/trailing whitespace removed. */ public static String sideStrip(String inString) { int sidesOkay = 0; while (sidesOkay != 2) { sidesOkay = 0; if (inString.startsWith(" ") || inString.startsWith("\t")) inString = StrTools.slice(inString,1); else sidesOkay++; if (inString.endsWith(" ") || inString.endsWith("\t")) inString = StrTools.slice(inString,0,-1); else sidesOkay++; } return inString; } /** * Removes the portion of the string from offset begin to offset end. * @param begin * @param end * @param inString * @return inString without the substring that starts at begin and ends at * end. */ public static String excise(String inString,int begin,int end) { return slice(inString,0,begin)+slice(inString,end); } /** * Removes the portion of the string from offset begin to offset end, but * keeps the newlines that were once inside the removed portion. * @param begin * @param end * @param inString * @return inString without the substring that starts at begin and ends at * end, but keeps all newlines. */ public static String exciseAndKeepNewlines(String inString,int begin, int end) { int newLines = countNewLines(slice(inString,begin,end)); String newLineString = ""; for (int x=0; x < newLines; x++) { newLineString += '\n'; } return slice(inString,0,begin)+newLineString+slice(inString,end); } /** * If there are lots of newlines in a string, like "\n\n\n...", then this * will combine all the multiple newlines into single newlines. * @param inString * @return inString with no continuous series of newlines. */ public static String consolidateNewlines(String inString) { if (inString.isEmpty()) return inString; if (!inString.contains("\n\n")) return inString; String outString = ""; int maxLength = inString.length(); int newLineCounter = 0; for (int i=0; i < maxLength; i++) { char currentChar = inString.charAt(i); if (currentChar == '\n') { newLineCounter++; } else { //We have not encountered a newline. if (newLineCounter > 0) { //A series of newlines was recently encountered. outString += "\n"+currentChar; newLineCounter = 0; } else { outString += currentChar; } } } return outString; } /** * If there are lots of newlines in a string, like "\n\n\n...", then this * will combine all the multiple newlines into single newlines or double * newlines. * @param inString * @return inString with no continuous series of newlines greater than two * \n characters. */ public static String consolidateNewlinesToDoubleNewlines(String inString) { if (inString.isEmpty()) return inString; if (!inString.contains("\n\n\n")) return inString; String outString = ""; int maxLength = inString.length(); int newLineCounter = 0; for (int i=0; i < maxLength; i++) { char currentChar = inString.charAt(i); if (currentChar == '\n') { newLineCounter++; } else { //We have not encountered a newline. if (newLineCounter >= 2) { //A series of newlines was recently encountered. outString += "\n\n"+currentChar; newLineCounter = 0; } else if (newLineCounter > 0) { outString += "\n"+currentChar; newLineCounter = 0; } else { outString += currentChar; } } } return outString; } /** * If there are lots of spaces in a string, like " ", then this * will combine all the multiple spaces into single spaces. * @param inString * @return inString with no continuous series of spaces. */ public static String consolidateSpaces(String inString) { if (inString.isEmpty()) return inString; if (!inString.contains(" ")) return inString; String outString = ""; int maxLength = inString.length(); int spaceCounter = 0; for (int i=0; i < maxLength; i++) { char currentChar = inString.charAt(i); if (currentChar == ' ') { spaceCounter++; } else { //We have not encountered a space. if (spaceCounter > 0) { //A series of spaces was recently encountered. outString += " "+currentChar; spaceCounter = 0; } else { outString += currentChar; } } } return outString; } /** * Given some text that has newlines, this method will grab all the * strings "in between" those newlines and return them as a list. * Does not ignore blank lines. * @param text * @return List containing text chunks between the newlines. */ public static ArrayList<String> splitLinesAndKeepBlankLines(String text) { int maxLength = text.length(); int textIndex = 0; ArrayList<String> lineList = new ArrayList<String>(); while (textIndex < maxLength) { String currentLine = copyFromIndexToChar(text,textIndex,'\n'); lineList.add(currentLine); textIndex += currentLine.length() + 1; } return lineList; } /** * Given some text that has newlines, this method will grab all the * strings "in between" those newlines and return them as a list. * Ignores blank lines. * @param text * @return List containing text chunks between the newlines. */ public static ArrayList<String> splitLines(String text) { text = consolidateNewlines(text); int maxLength = text.length(); int textIndex = 0; ArrayList<String> lineList = new ArrayList<String>(); while (textIndex < maxLength) { String currentLine = copyFromIndexToChar(text,textIndex,'\n'); lineList.add(currentLine); textIndex += currentLine.length() + 1; } //Remove all blank lines, if any. if (lineList.contains("")) { boolean containsBlankLines = true; while (containsBlankLines) { containsBlankLines = lineList.remove(""); } } return lineList; } /** * Given some text that has a certain character, this method will grab all * the strings "in between" those characters and return them as a list. * Ignores blank lines. * @param text * @return List containing text chunks between the characters. */ public static ArrayList<String> splitTextAtChars(String text, char character) { int maxLength = text.length(); int textIndex = 0; ArrayList<String> lineList = new ArrayList<String>(); while (textIndex < maxLength) { String currentLine = copyFromIndexToChar(text,textIndex,character); lineList.add(currentLine); textIndex += currentLine.length() + 1; } //Remove all blank lines, if any. if (lineList.contains("")) { boolean containsBlankLines = true; while (containsBlankLines) { containsBlankLines = lineList.remove(""); } } return lineList; } public static int getMaxStringLength(ArrayList<String> stringList) { int max = 0; for (String eachString : stringList) { int currentLength = eachString.length(); if (currentLength > max) max = currentLength; } return max; } /** * Inserts the string <i>insertion</i> at the offset <i>index</i>. * Appends the string to the end of the text if the index is too high. * @param text * @param insertion * @param index * @return Text with insertion inserted at index. */ public static String insert(String text,String insertion,int index) { return slice(text,0,index) + insertion + slice(text,index); } public static int getLineNumber(String text,int position) { int maxLength = text.length(); if (position > maxLength) { return countNewLines(text)+1; } //Negative positions are valid. They just count backwards from the //end of the string. text = slice(text,0,position); return countNewLines(text)+1; } public static int getLineNumberForDisplay(String text,int position) { int maxLength = text.length(); if (position > maxLength) { return countNewLines(text)+1; } //Negative positions are valid. They just count backwards from the //end of the string. text = slice(text,0,position+1); return countNewLines(text)+1; } // OLD METHOD. // public static int getLineNumber(String text,int position) { // int maxLength = text.length(); // if (position > maxLength) { // return countNewLines(text)+1; // } // //Negative positions are valid. They just count backwards from the // //end of the string. // text = slice(text,position); // return countNewLines(text)+1; // } /** * Given some text, this method counts how many newlines (\n) are in it. * @param text * @return Number of Counted Newlines */ public static int countNewLines(String text) { int maxLength = text.length(); int counter = 0; for (int x=0; x < maxLength; x++) { if (text.charAt(x) == '\n') counter++; } return counter; } public static int[] getLineStartAndEnd(String text,int lineNumber) { if (!text.contains("\n")) { int[] noNewlinesArray = {0,text.length()}; return noNewlinesArray; } int maxLength = text.length(); int currentLine = 1; int startPos = -1; int endPos = -1; int lastLineStart = -1; int[] results = {0,0}; for (int x=0; x < maxLength; x++) { if (currentLine == lineNumber && startPos == -1) startPos = x; if (currentLine == lineNumber+1 && endPos == -1) //Use minus 1, because we do not want to include the newline. endPos = x - 1; if (endPos != -1 && startPos != -1) { results[0] = startPos; results[1] = endPos; return results; } if (text.charAt(x) == '\n') { currentLine++; lastLineStart = x + 1; } } //We did not find the end position, so we just select the last line: results[0] = lastLineStart; results[1] = maxLength; //If something went horribly wrong, fix it. if (results[0] == -1 || results[1] == -1 || results[0]>results[1]) { results[0] = 0; results[1] = 0; } return results; } /** * Checks if the particular string array contains a certain string. * This method will accept partial strings too. For example:<br /> * {"doggy","kitty","snake"} does contain "dog" because "dog" is part of * "doggy". * @param array * @param target * @return true or false */ static boolean doesStringArrayContain(String[] array,String target) { for (String eachString : array) { if (eachString.contains(target)) return true; } return false; } }//END MAIN CLASS