/* * RapidMiner * * Copyright (C) 2001-2008 by Rapid-I and the contributors * * Complete list of developers available at our web site: * * http://rapid-i.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class is used for refactoring RapidMiners operator classes * in order to ensure, that every parameter reference uses a certain * predefined String variable. * * @author Helge Homburg, Tobias Beckers * @version $Id: ParameterRefactoring.java,v 1.14 2008/07/13 16:39:42 ingomierswa Exp $ */ public class ParameterRefactoring { private BufferedReader srcFile; private BufferedWriter dstFile; private BufferedWriter logFile; private boolean isProperty = false; private boolean containsTODOs = false; private String file = ""; private String workingDir, logDir; private StringBuffer newEntries = new StringBuffer(""); private List<File> files = new LinkedList<File>(); private int currentPosition = 0, nextIndex = 0, numberParameter = 0, numberProperty = 0; private int parameterDeclaration = 0, propertyDeclaration = 0; /** By adding a parameter or property name to this blacklist it becomes transparent to the * refactoring methods.*/ List<String> blackList = Arrays.asList("user.dir", "rapidminer.home", "os.name", "noWordSep", "keep_example_set", "min_similarity", "java.class.path", "user.home", "line.separator"); private static class JAVAFilter implements FilenameFilter { public boolean accept(File dir, String name) { return (name.endsWith(".java")); } } public ParameterRefactoring(String workingDir, String logDir) { this.workingDir = workingDir; this.logDir = logDir; } public void transformFiles() { // get all files in workingDir getAllFiles(new File(workingDir)); // get current timestamp Date dt = new Date(); logFile = openOutputWriter(new File(logDir, "refactor-"+ dt.getTime() +".log")); writeToLog("Refactoring started "+ dt +", "); writeToLog("changes were made to the following classes:\n" ); // modify all files in workingDir for (int i = 0; i < files.size(); i++) { file = ""; currentPosition = 0; nextIndex = 0; containsTODOs = false; newEntries = new StringBuffer(""); File currentFile = files.get(i); String[] fileName = currentFile.getName().split("\\."); String className = fileName[0]; srcFile = openSourceFile(currentFile); if (srcFile != null) { fillBuffer(); } else { System.out.println("Target file not found!"); break; } closeSourceFile(); writeToLog("\n--> "+className+" class at \""+currentFile.toString()+"\"\n\n"); // perform substitution for every suitable ParameterType declaration int currentIndex = getNextParameterTokenIndex(); while (currentIndex != -1) { replaceParameterToken(currentIndex); currentIndex = getNextParameterTokenIndex(); } // write statements setDeclarations(className); checkForNonStandardVariables(); findOtherStatements(); if (!newEntries.toString().equals("") || containsTODOs) { String path = currentFile.getParent(); if (path != null) { dstFile = openOutputWriter(new File(path, className+".java")); } else { break; } writeOutput(); } } writeToLog("\nSome strings remained unchanged:\n\n\t[parameter]: "+numberParameter+"\t[property]: "+numberProperty); writeToLog("\n\t[parameter declaration]: "+parameterDeclaration+"\t[property declaration]: "+propertyDeclaration); try { logFile.flush(); logFile.close(); } catch (IOException e) { e.printStackTrace(); } } private void replaceParameterToken(int pos) { String key = "[parameter]"; // get the parameter name int startOfName = file.indexOf('"', pos); int endOfName = file.indexOf('"', startOfName + 1); String name = file.substring(startOfName, endOfName + 1); // get the parameter description int startOfDescription = file.indexOf('"', endOfName + 1); int endOfDescription = file.indexOf('"', startOfDescription +1); boolean isEndOfDescription = false; while (!isEndOfDescription) { String indicator = file.substring(endOfDescription - 1, endOfDescription + 1); if (indicator.equals("\\\"")) { endOfDescription = file.indexOf('"', endOfDescription +1); } else { isEndOfDescription = true; } } String descriptionName = file.substring(startOfDescription, endOfDescription + 1); if (!this.isProperty) { // construct new parameter declaration String comment = "\n\t/** The parameter name for ""+descriptionName.substring(1, descriptionName.length() - 1)+"" */"; newEntries.append(comment); String newName = "PARAMETER_"+file.substring(startOfName + 1, endOfName).toUpperCase().trim(); String fullStatement = "\n\tpublic static final String "+newName+" = "+name+";\n"; newEntries.append(fullStatement); // replace the old string by the new parameter statement Pattern nameReference = Pattern.compile("([Ligset]*Parameter[Set]*[^\\(]*\\(\\s*)"+name); Matcher findNameReference = nameReference.matcher(file); file = findNameReference.replaceAll("$1"+newName); Pattern parameterTypeDeclaration = Pattern.compile("(new\\s+ParameterType[^\\(]*\\(\\s*)"+name); Matcher findDeclaration = parameterTypeDeclaration.matcher(file); file = findDeclaration.replaceAll("$1"+newName); } else { key = "[property]"; // construct new parameter declaration String comment = "\n\t/** The property name for ""+descriptionName.substring(1, descriptionName.length() - 1)+"" */"; newEntries.append(comment); String newName = "PROPERTY_"+name.substring(1, name.length() - 1).toUpperCase().replace('.', '_').trim(); String fullStatement = "\n\tpublic static final String "+newName+" = "+name+";\n"; newEntries.append(fullStatement); Pattern nameReference = Pattern.compile("([gs]+etProperty[^\\(]*\\(\\s*)"+name); Matcher findNameReference = nameReference.matcher(file); file = findNameReference.replaceAll("$1"+newName); Pattern parameterTypeDeclaration = Pattern.compile("(Property\\s*\\(\\s*new\\s+ParameterType[^\\(]*\\(\\s*)"+name); Matcher findDeclaration = parameterTypeDeclaration.matcher(file); file = findDeclaration.replaceAll("$1"+newName); } // look for further occurrences of the old string int currentIndex = findOccurranceOf(name, 0); while (currentIndex >= 0) { writeToLog("\t"+key+" "+name+"\t occurs at position "+currentIndex+" (line "+getLineNumber(currentIndex)+")\n"); currentIndex = findOccurranceOf(name, currentIndex + 1); } } private int getNextParameterTokenIndex() { // find next the occurance of a ParameterType declaration and move // the pointer to the starting position of the parameter name string int nextParameterIndex = -1, nextPropertyIndex = -1; boolean isRegular = false; try { Pattern parameterTypeDeclaration = Pattern.compile("(new\\s+ParameterType[^\\(]*\\(\\s*)([A-Z\"])"); Matcher findParameterDeclaration = parameterTypeDeclaration.matcher(file); findParameterDeclaration.find(currentPosition); if (!findParameterDeclaration.group(2).equals("\"")) { isRegular = true; } nextParameterIndex = findParameterDeclaration.end(1) - 1; } catch (RuntimeException e) { nextParameterIndex = -2; } try { Pattern propertyTypeDeclaration = Pattern.compile("Property\\s*\\(\\s*(new\\s+ParameterType[^\\(]*\\(\\s*)([A-Z\"])"); Matcher findPropertyDeclaration = propertyTypeDeclaration.matcher(file); findPropertyDeclaration.find(currentPosition); nextPropertyIndex = findPropertyDeclaration.end(1) - 1; if (!findPropertyDeclaration.group(2).equals("\"")) { isRegular = true; nextIndex = nextPropertyIndex; } } catch (RuntimeException e) { nextPropertyIndex = -3; } if (nextParameterIndex == -2 && nextPropertyIndex == -3) { return -1; } if (nextParameterIndex == nextPropertyIndex) { this.isProperty = true; } else { this.isProperty = false; } nextIndex = nextParameterIndex; // find the ending bracket of the current declaration in order to // ignore any inner declaration of ParameterType Character currentChar = Character.valueOf('c'); int currentIndex = nextIndex + 1, numberOfOpenBrackets = 1, numberOfClosedBrackets = 0; boolean notAllBracketsClosed = true; while (notAllBracketsClosed) { currentChar = file.charAt(currentIndex); // skip a comment if (currentChar.equals('"')) { currentIndex++; currentChar = file.charAt(currentIndex); while (!currentChar.equals('"')) { currentIndex++; currentChar = file.charAt(currentIndex); } } // count all brackets if (currentChar.equals('(')) { numberOfOpenBrackets += 1; } if (currentChar.equals(')')) { numberOfClosedBrackets += 1; } if (numberOfClosedBrackets == numberOfOpenBrackets) { notAllBracketsClosed = false; } currentIndex++; } this.currentPosition = currentIndex; // if the current declaration already uses a constant for reference, go ahead // to the next possible occurrance of a parameter type declaration. if (isRegular) { return getNextParameterTokenIndex(); } return nextIndex; } private void checkForNonStandardVariables() { boolean parameterMatch = true, propertyMatch = true; int nextParameterIndex = 0; int nextPropertyIndex = 0; while (parameterMatch || propertyMatch) { try { Pattern parameterTypeDeclaration = Pattern.compile("[^y]\\s*\\(\\s*new\\s+ParameterType[^\\(]*\\(\\s*([^P\\s,]\\w*\\.?[^P\\s,.]\\w*\\s*),"); Matcher findParameterDeclaration = parameterTypeDeclaration.matcher(file); findParameterDeclaration.find(nextParameterIndex); nextParameterIndex = findParameterDeclaration.start(1); String parameterStatement = file.substring(nextParameterIndex, findParameterDeclaration.end(1)); writeToLog("\t"+"[parameter declaration]"+" "+parameterStatement+"\t occurs at position "+nextParameterIndex+" (line "+getLineNumber(nextParameterIndex)+")\n"); int todoPosition = getPositionOfNextLineFeed(nextParameterIndex); String start = file.substring(0, todoPosition); String end = file.substring(todoPosition); file = start + " // TODO [parameter declaration]" + end; parameterDeclaration++; containsTODOs = true; } catch (RuntimeException e) { parameterMatch = false; } try { Pattern propertyTypeDeclaration = Pattern.compile("Property\\s*\\(\\s*new\\s+ParameterType[^\\(]*\\(\\s*([^P\\s,]\\w*\\.?[^P\\s,.]\\w*\\s*),"); Matcher findPropertyDeclaration = propertyTypeDeclaration.matcher(file); findPropertyDeclaration.find(nextPropertyIndex); nextPropertyIndex = findPropertyDeclaration.start(1); String propertyStatement = file.substring(nextPropertyIndex, findPropertyDeclaration.end(1)); writeToLog("\t"+"[property declaration]"+" "+propertyStatement+"\t occurs at position "+nextPropertyIndex+" (line "+getLineNumber(nextPropertyIndex)+")\n"); int todoPosition = getPositionOfNextLineFeed(nextPropertyIndex); String start = file.substring(0, todoPosition); String end = file.substring(todoPosition); file = start + " // TODO [property declaration]" + end; propertyDeclaration++; containsTODOs = true; } catch (RuntimeException e) { propertyMatch = false; } } } private void findOtherStatements() { // look for further statements using strings for reference instead of string variables boolean parameterMatch = true, propertyMatch = true; int parameterIndex = 0; int propertyIndex = 0; while(parameterMatch || propertyMatch) { try { Pattern reference = Pattern.compile("([Ligset]*)Parameter[\\w]*\\(\\s*\"([^\\)\"]*)\"\\s*\\)"); Matcher findReference = reference.matcher(file); findReference.find(parameterIndex); parameterIndex = findReference.start(1); String parameter = file.substring(findReference.start(2), findReference.end(2)); if (blackList.contains(parameter)) { parameterIndex = findReference.end(2); } else { writeToLog("\t"+"[parameter]"+" \""+parameter+"\"\t occurs at position "+parameterIndex+" (line "+getLineNumber(parameterIndex)+")\n"); parameterIndex = findReference.end(2); int todoPosition = getPositionOfNextLineFeed(parameterIndex); String start = file.substring(0, todoPosition); String end = file.substring(todoPosition); file = start + " // TODO [parameter]" + end; numberParameter++; containsTODOs = true; } } catch (RuntimeException e) { parameterMatch = false; } try { Pattern reference = Pattern.compile("([gs])+etProperty[\\w]*\\(\\s*\"([^\\)\"]*)\"\\s*\\)"); Matcher findReference = reference.matcher(file); findReference.find(propertyIndex); propertyIndex = findReference.start(1); String property = file.substring(findReference.start(2), findReference.end(2)); if (blackList.contains(property)) { propertyIndex = findReference.end(2); } else { writeToLog("\t"+"[property]"+" "+property+"\t occurs at position "+propertyIndex+" (line "+getLineNumber(propertyIndex)+")\n"); propertyIndex = findReference.end(2); int todoPosition = getPositionOfNextLineFeed(propertyIndex); String start = file.substring(0, todoPosition); String end = file.substring(todoPosition); file = start + " // TODO [property]" + end; numberProperty++; containsTODOs = true; } } catch (RuntimeException e) { propertyMatch = false; } } } private int getPositionOfNextLineFeed(int currentPos) { int index = file.indexOf('\n', currentPos); if (index >= 0) { return index; } else { return file.length() - 1; } } private void setDeclarations(String className) { Pattern headOfClass = Pattern.compile("(class\\s+"+className+"\\s[\\w\\s,]*\\{\\s*)\n"); Matcher findClassDeclaration = headOfClass.matcher(file); if (newEntries.length() > 0) { file = findClassDeclaration.replaceFirst("$1"+newEntries.toString()+"\n"); } } private int findOccurranceOf(String name, int pos) { int nextIndex = -1; try { Pattern parameterTypeDeclaration = Pattern.compile("(\\()\\s*"+name+"\\s*\\)"); Matcher findParameterDeclaration = parameterTypeDeclaration.matcher(file); findParameterDeclaration.find(pos); nextIndex = findParameterDeclaration.end(1) + 1; } catch (RuntimeException e) { return -1; } return nextIndex; } private int getLineNumber(int pos) { int lineNumber = 0; int currentIndex = 0; while (currentIndex <= pos) { currentIndex = file.indexOf("\n", currentIndex + 1); lineNumber++; } return lineNumber; } private void writeOutput() { try { dstFile.write(file); dstFile.flush(); dstFile.close(); } catch (IOException e) { e.printStackTrace(); } } private void writeToLog(String logOutput) { try { logFile.write(logOutput); } catch (IOException e) { e.printStackTrace(); } } private BufferedWriter openOutputWriter(File path) { BufferedWriter newFile = null; try { newFile = new BufferedWriter(new FileWriter(path)); } catch (IOException e) { e.printStackTrace(); } return newFile; } private BufferedReader openSourceFile(File path) { BufferedReader file = null; try { file = new BufferedReader(new FileReader(path)); } catch (IOException e) { e.printStackTrace(); } return file; } private void closeSourceFile() { try { srcFile.close(); } catch (IOException e) { e.printStackTrace(); } } private void fillBuffer() { try { while (srcFile.ready()) { file += srcFile.readLine() + "\n"; } } catch (IOException e) { e.printStackTrace(); } try { srcFile.close(); } catch (IOException e) { e.printStackTrace(); } } private void getAllFiles(File root) { if (root.isFile()) { files.add(root); } if (root.isDirectory()) { // get all java files FilenameFilter filter = new JAVAFilter(); File[] innerFiles = root.listFiles(filter); for (int i = 0; i < innerFiles.length; i++) { files.add(innerFiles[i]); } // apply getAllFiles() recursively to all inner dirctories File[] innerDirectories = root.listFiles(); for (int i = 0; i < innerDirectories.length; i++) { if (innerDirectories[i].isDirectory()) { getAllFiles(innerDirectories[i]); } } } } public static void main(String[] args) { if (args[0] != null && args[1] != null) { ParameterRefactoring pM = new ParameterRefactoring(args[0], args[1]); pM.transformFiles(); } else { System.out.println("Please specify two directories (working and logging directory) " + "to perform parameter refactoring."); } } }