diff --git a/.gitignore b/.gitignore index b3dafd1..c5e82d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -bin -testes \ No newline at end of file +bin \ No newline at end of file diff --git a/src/Driver.rsc b/src/Driver.rsc index 17315ee..a2a7c13 100644 --- a/src/Driver.rsc +++ b/src/Driver.rsc @@ -19,6 +19,9 @@ import lang::java::refactoring::AnonymousToLambda; import lang::java::refactoring::ExistPatternToLambda; import lang::java::refactoring::FilterPattern; +import lang::java::refactoring::forloop::EnhancedForLoopRefactorer; +import lang::java::analysis::ExceptionFinder; + import lang::java::util::ManageCompilationUnit; import lang::java::m3::M3Util; import lang::java::\syntax::Java18; @@ -55,7 +58,11 @@ public void refactorProjects(loc input, bool verbose = true) { case /DI/: executeTransformations(projectFiles, toInt(projectDescriptor[3]), verbose, refactorDiamond, "diamond"); case /AC/: executeTransformations(projectFiles, toInt(projectDescriptor[3]), verbose, refactorAnonymousInnerClass, "aic"); case /EP/: executeTransformations(projectFiles, toInt(projectDescriptor[3]), verbose, refactorExistPattern, "exist pattern"); - case /FP/: executeTransformations(projectFiles, toInt(projectDescriptor[3]), verbose, refactorFilterPattern, "filter pattern"); + case /FP/: executeTransformations(projectFiles, toInt(projectDescriptor[3]), verbose, refactorFilterPattern, "filter pattern"); + case /FUNC/: { + checkedExceptionClasses = findCheckedExceptions(projectFiles); + executeTransformations(projectFiles, toInt(projectDescriptor[3]), verbose, refactorForLoopToFunctional, "ForLoopToFunctional"); + } default: logMessage(" ... nothing to be done"); } } @@ -93,7 +100,7 @@ public void executeTransformations(list[loc] files, int percent, bool verbose, t int totalOfChangedFiles = exportResults(toApply, processedFiles, verbose, name); t1 = now(); logMessage("- Number of files: " + toString(size(files))); - logMessage("- Processed Filies: " + toString(size(processedFiles))); + logMessage("- Processed Files: " + toString(size(processedFiles))); logMessage("- Exported Files: " + toString(size(toApply))); logMessage("- Total of files changed: " + toString(totalOfChangedFiles)); logMessage("- Total of transformations: " + toString(totalOfTransformations)); diff --git a/src/io/IOUtil.rsc b/src/io/IOUtil.rsc index f8eb26c..175ba6a 100644 --- a/src/io/IOUtil.rsc +++ b/src/io/IOUtil.rsc @@ -9,7 +9,7 @@ list[loc] findAllFiles(loc location, str ext) { res = []; list[loc] allFiles; - if((isDirectory(location)) || (location.extension == "jar") || (location.extension == "zip")) { + if(isDirectory(location) || (location.extension == "jar") || (location.extension == "zip")) { allFiles = location.ls; } else { diff --git a/src/lang/java/analysis/ExceptionFinder.rsc b/src/lang/java/analysis/ExceptionFinder.rsc new file mode 100644 index 0000000..9ceefa3 --- /dev/null +++ b/src/lang/java/analysis/ExceptionFinder.rsc @@ -0,0 +1,105 @@ +module lang::java::analysis::ExceptionFinder + +import IO; +import lang::java::m3::M3Util; +import lang::java::\syntax::Java18; +import ParseTree; +import Set; +import Map; +import util::Math; + +private map[str, set[str]] superClassesBySubClasses = (); +private set[str] checkedExceptionClasses = {"Exception"}; +private list[loc] fileLocationsThatCouldNotBeParsed = []; + +private data ClassAndSuperClass = classAndSuperClass(str className, str superClassName); + +private bool printAllFileNamesThatCouldNotBeParsed = false; + +public set[str] findCheckedExceptions(list[loc] javaFilesLocations) { + initializeClassesFound(); + for(javaFileLocation <- javaFilesLocations) + tryToVisitFileLookingForClassesWithSubClasses(javaFileLocation); + + printJavaFilesThatCouldNotBeParsed(); + return checkedExceptionClasses; +} + +private void initializeClassesFound() { + superClassesBySubClasses = (); + checkedExceptionClasses = {"Exception"}; + fileLocationsThatCouldNotBeParsed = []; +} + +private void tryToVisitFileLookingForClassesWithSubClasses(loc javaFileLocation) { + javaFileContent = readFile(javaFileLocation); + try + visitFileLookingForClassesWithSubClasses(javaFileContent); + catch: + fileLocationsThatCouldNotBeParsed += javaFileLocation; +} + +private void visitFileLookingForClassesWithSubClasses(str javaFileContent) { + compilationUnit = parse(#CompilationUnit, javaFileContent); + classesAndSuperClasses = retrieveClassesAndSuperClassesFromCompilationUnit(compilationUnit); + for(classAndSuperClass <- classesAndSuperClasses) + handleIfClassIsAnException(classAndSuperClass); +} + +private list[ClassAndSuperClass] retrieveClassesAndSuperClassesFromCompilationUnit(compilationUnit) { + list[ClassAndSuperClass] classesAndSuperClasses = []; + visit(compilationUnit) { + case(NormalClassDeclaration) ` class extends `: + classesAndSuperClasses += classAndSuperClass(unparse(className), unparse(superClassName)); + } + return classesAndSuperClasses; +} + +private void handleIfClassIsAnException(ClassAndSuperClass cas) { + if (cas.superClassName in checkedExceptionClasses) + addClassAndItsSubClassesAsExceptions(cas.className); + else + addClassAsASubClassOfItsSuperClass(cas); +} + +private void addClassAndItsSubClassesAsExceptions(str className) { + checkedExceptionClasses += className; + if (className in superClassesBySubClasses) + addAllSubClassesOf(className); +} + +private void addClassAsASubClassOfItsSuperClass(ClassAndSuperClass cas) { + if (cas.superClassName in superClassesBySubClasses) + superClassesBySubClasses[cas.superClassName] += {cas.className}; + else + superClassesBySubClasses[cas.superClassName] = {cas.className}; +} + +private void addAllSubClassesOf(str className) { + directSubClasses = getAllDirectSubClassesOf(className); + checkedExceptionClasses += directSubClasses; + superClassesBySubClasses = delete(superClassesBySubClasses, className); + + for (str className <- directSubClasses) + addAllSubClassesOf(className); +} + +private set[str] getAllDirectSubClassesOf(str className) { + directSubClasses = {}; + if (className in superClassesBySubClasses) + directSubClasses = superClassesBySubClasses[className]; + return directSubClasses; +} + +private void printJavaFilesThatCouldNotBeParsed() { + if (printAllFileNamesThatCouldNotBeParsed) { + str filesNotParsedCount = toString(size(fileLocationsThatCouldNotBeParsed)); + println(filesNotParsedCount + " Java File Locations that could not be parsed. "); + + for(fileLoc <- fileLocationsThatCouldNotBeParsed) + print(fileLoc.file + ", "); + println(); + } + + println(); +} \ No newline at end of file diff --git a/src/lang/java/analysis/ParseTreeVisualization.rsc b/src/lang/java/analysis/ParseTreeVisualization.rsc index 9a1bf55..2a6da90 100644 --- a/src/lang/java/analysis/ParseTreeVisualization.rsc +++ b/src/lang/java/analysis/ParseTreeVisualization.rsc @@ -6,7 +6,7 @@ import vis::Figure; import vis::ParseTree; import vis::Render; +void visualize(Tree t) { + render(visParsetree(t)); +} -void visualize(CompilationUnit unit) { - render(visParsetree(unit)); -} \ No newline at end of file diff --git a/src/lang/java/analysis/test/ExceptionFinderTest.rsc b/src/lang/java/analysis/test/ExceptionFinderTest.rsc new file mode 100644 index 0000000..422db10 --- /dev/null +++ b/src/lang/java/analysis/test/ExceptionFinderTest.rsc @@ -0,0 +1,63 @@ +module lang::java::analysis::\test::ExceptionFinderTest + +import lang::java::m3::M3Util; +import IO; +import lang::java::analysis::ExceptionFinder; + +private loc zipFile = |jar:///D:/exception-hierarchy.zip!|; +private list[loc] javaClassesLocations = listAllJavaFiles(zipFile); + +public test bool shouldReturnExceptionClass() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "Exception" in checkedExceptions; +} + +public test bool shouldReturnLevelTwoHierarchyClasses() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "AException" in checkedExceptions && "BException" in checkedExceptions && + "CException" in checkedExceptions && "ZException" in checkedExceptions; +} + +public test bool shouldReturnLevelThreeHierarchyClasses() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "DException" in checkedExceptions && "FException" in checkedExceptions && + "IException" in checkedExceptions; +} + +public test bool shouldReturnLevelFourHierarchyClasses() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "LException" in checkedExceptions && "EException" in checkedExceptions && + "JException" in checkedExceptions && "HException" in checkedExceptions; +} + +public test bool shouldReturnLevelFiveHierarchyClasses() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "GException" in checkedExceptions && "KException" in checkedExceptions && + "NException" in checkedExceptions; +} + +public test bool shouldReturnLevelSixHierarchyClasses() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "MException" in checkedExceptions; +} + +public test bool shouldNotReturnUncheckedExceptions() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "RuntimeException" notin checkedExceptions && + "UncheckedLevelFourRuntimeException" notin checkedExceptions && + "UncheckedLevelThreeRuntimeException" notin checkedExceptions && + "UncheckedLevelTwoRuntimeException" notin checkedExceptions; +} + +public test bool shouldReturnAbstractCheckedException() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "AbstractCheckedException" in checkedExceptions; +} + +public test bool shouldReturnStaticInnerCheckedExceptions() { + set[str] checkedExceptions = findCheckedExceptions(javaClassesLocations); + return "ExceptionClassWithOnlyZeroArgCtor" in checkedExceptions && + "CustomServiceLocatorException3" in checkedExceptions && + "CustomServiceLocatorException2" in checkedExceptions && + "DeepNestedStaticException" in checkedExceptions; +} \ No newline at end of file diff --git a/src/lang/java/m3/M3Util.rsc b/src/lang/java/m3/M3Util.rsc index 3808717..48e4fb5 100644 --- a/src/lang/java/m3/M3Util.rsc +++ b/src/lang/java/m3/M3Util.rsc @@ -88,3 +88,7 @@ list[M3] createM3FromClassPath(list[loc] locations) { list[loc] classes = [ c | l <- locations, c <- listAllClassFiles(l) ]; return [ createM3FromJarClass(c) | c <- classes ]; } + +list[loc] listAllJavaFiles(loc location) { + return findAllFiles(location, "java"); +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/AvailableVariables.rsc b/src/lang/java/refactoring/forloop/AvailableVariables.rsc new file mode 100644 index 0000000..857adcc --- /dev/null +++ b/src/lang/java/refactoring/forloop/AvailableVariables.rsc @@ -0,0 +1,46 @@ +module lang::java::refactoring::forloop::AvailableVariables + +import Set; +import lang::java::\syntax::Java18; +import ParseTree; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::ProspectiveOperation; + +// XXX assess the necessity to verify these others conditions: + // fields declared in class, inherited and visible from imported classes ?? + // variables declared in the Prospective Operation ?? +// Right now they are being verified by elimination. +public set[str] retrieveAvailableVariables(ProspectiveOperation prOp, set[MethodVar] methodVars) { + availableVars = {}; + + withinMethod = retrieveNotDeclaredWithinLoopNames(methodVars); + withinLoop = retrieveDeclaredWithinLoopNames(methodVars); + + availableVars += withinMethod; + availableVars -= withinLoop; + // Probably redundant somehow. This will catch some withinLoop. + availableVars += retrieveLocalVariableDeclarations(prOp); + + return availableVars; +} + +private set[str] retrieveLocalVariableDeclarations(ProspectiveOperation prOp) { + if (isFilter(prOp)) return {}; + + localVars = {}; + Tree stmt; + + if (isLocalVariableDeclarationStatement(prOp.stmt)) + stmt = parse(#LocalVariableDeclarationStatement, prOp.stmt); + else + stmt = parse(#Statement, prOp.stmt); + + visit(stmt) { + case LocalVariableDeclaration lvdl: { + visit(lvdl) { + case (VariableDeclaratorId) ``: localVars += unparse(id); + } + } + } + return localVars; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/BreakIntoStatements.rsc b/src/lang/java/refactoring/forloop/BreakIntoStatements.rsc new file mode 100644 index 0000000..710b805 --- /dev/null +++ b/src/lang/java/refactoring/forloop/BreakIntoStatements.rsc @@ -0,0 +1,71 @@ +module lang::java::refactoring::forloop::BreakIntoStatements + +import IO; +import List; +import String; +import lang::java::\syntax::Java18; +import ParseTree; + +data Stmt = stmtBrokenInto(Tree statement, str stmtType); + +public list[str] breakIntoStatementsAsStringList(str stmt) { + stmts = breakIntoStatements(stmt); + return [ "" | Stmt stmt <- stmts ]; +} + +public list[str] breakIntoStatementsAsStringList(Block block) { + stmts = breakIntoStatements(block); + return [ "" | Stmt stmt <- stmts ]; +} + +public list[Stmt] breakIntoStatements(str stmt) { + if(isBlock(stmt)) + return breakIntoStatements(parse(#Block, stmt)); + return breakIntoStatements(parse(#Statement, stmt)); +} + +public bool isBlock(str stmt) { + try { + parse(#Block, stmt); + return true; + } catch: return false; +} + +public list[Stmt] breakIntoStatements(Block block) { + list [Stmt] stmts = []; + top-down-break visit(block) { + case Statement stmt: + stmts += breakIntoStatements(stmt); + case LocalVariableDeclarationStatement lvdlStmt: + stmts += stmtBrokenInto(lvdlStmt, "LocalVariableDeclarationStatement"); + } + return stmts; +} + +public list[Stmt] breakIntoStatements(Statement statement) { + list[Stmt] stmts = []; + top-down-break visit(statement) { + case IfThenStatement ifStmt: { + stmts += stmtBrokenInto(ifStmt, "IfThenStatement"); + } + case ExpressionStatement expStmt: { + stmts += stmtBrokenInto(expStmt, "ExpressionStatement"); + } + case LocalVariableDeclarationStatement lvdlStmt: { + stmts += stmtBrokenInto(lvdlStmt, "LocalVariableDeclarationStatement"); + } + + case IfThenElseStatement ifElseStmt: throw "Not Refactoring If/Else for now"; + case ForStatement _: throw "Not Refactoring Inner Loops for now"; + case WhileStatement _: throw "Not Refactoring While Loops inside ForStatement for now"; + } + return stmts; +} + +public void printStmtsBrokenInto(list[Stmt] stmts) { + for (stmt <- stmts) { + println("type: "); + println("stmt: "); + println(); + } +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/ClassFieldsFinder.rsc b/src/lang/java/refactoring/forloop/ClassFieldsFinder.rsc new file mode 100644 index 0000000..d991895 --- /dev/null +++ b/src/lang/java/refactoring/forloop/ClassFieldsFinder.rsc @@ -0,0 +1,62 @@ +module lang::java::refactoring::forloop::ClassFieldsFinder + +import IO; +import lang::java::\syntax::Java18; +import ParseTree; +import String; +import Set; +import lang::java::refactoring::forloop::MethodVar; + +// M3 isn't very helpful here. +// We can't really get type + var name +public set[MethodVar] findClassFields(CompilationUnit unit) { + return findCurrentClassFields(unit); +} + +private set[MethodVar] findCurrentClassFields(CompilationUnit unit) { + classFields = {}; + + // syntax FieldDeclaration = fieldDeclaration: FieldModifier* UnannType VariableDeclaratorList ";"+ ; + + visit(unit) { + case (FieldDeclaration) ` ;`: { + visit(vdl) { + case (VariableDeclaratorId) ` `: + classFields += createEffectiveFinalVar(figureIfIsFinal(varMod), varId, varType, dims); + + } + } + } + + return classFields; +} + +private bool figureIfIsFinal(FieldModifier* varMod) { + return contains("", "final"); +} + +// Class fields are effectively final for the purpose of ForLoopToFunctional +private MethodVar createEffectiveFinalVar(bool isFinal, Identifier varId, UnannType varType, Dims? dims) { + name = trim(unparse(varId)); + varTypeStr = trim(unparse(varType)); + dimsStr = trim(unparse(dims)); + + // Standarizing arrays to have varType == [] + if(dimsStr == "[]") + varTypeStr += "[]"; + + bool isParameter = false; + bool isDeclaredWithinLoop = false; + bool isEffectiveFinal = true; + return methodVar(isFinal, name, varTypeStr, isParameter, isDeclaredWithinLoop, isEffectiveFinal); +} + +// XXX hard +private set[MethodVar] findInheritedFields() { + return {}; +} + +// XXX hard +private set[MethodVar] findImportedFields() { + return {}; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/EnhancedForLoopRefactorer.rsc b/src/lang/java/refactoring/forloop/EnhancedForLoopRefactorer.rsc new file mode 100644 index 0000000..03d2d1d --- /dev/null +++ b/src/lang/java/refactoring/forloop/EnhancedForLoopRefactorer.rsc @@ -0,0 +1,156 @@ +module lang::java::refactoring::forloop::EnhancedForLoopRefactorer + +import IO; +import lang::java::\syntax::Java18; +import ParseTree; +import util::Math; +import lang::java::refactoring::forloop::LocalVariablesFinder; +import lang::java::refactoring::forloop::EnhancedLoopExpression; +import lang::java::refactoring::forloop::ForLoopBodyReferences; +import lang::java::refactoring::forloop::ForLoopToFunctional; +import lang::java::refactoring::forloop::ClassFieldsFinder; +import lang::java::refactoring::forloop::MethodVar; + +private bool PRINT_DEBUG = false; + +public set[str] checkedExceptionClasses = {}; + +private set[MethodVar] currentClassFields = {}; + +private bool alreadyComputedClassFields = false; + +private int refactoredCount = 0; + +// Method for debugging. Not used by driver +public void forLoopToFunctional(list[loc] locs, set[str] checkedExceptions) { + refactoredCount = 0; + checkedExceptionClasses = checkedExceptions; + for(fileLoc <- locs) { + javaFileContent = readFile(fileLoc); + try { + unit = parse(#CompilationUnit, javaFileContent); + refactorForLoopToFunctional(unit); + } catch: + continue; + } + println("refactoredCount: " + toString(refactoredCount)); +} + +// Losing formatting after a method is refactored. +public tuple[int occurrences, CompilationUnit unit] refactorForLoopToFunctional(CompilationUnit unit) { + int occurrences = 0; + alreadyComputedClassFields = false; + CompilationUnit refactoredUnit = visit(unit) { + case (MethodDeclaration) ` `: { + refactoredCountBeforeCurrentMethod = refactoredCount; + + MethodBody refactoredMethodBody = visit(mBody) { + case MethodBody methodBody: { + refactored = refactorEnhancedForStatementsInMethodBody(unit, methodHeader, methodBody); + occurrences += refactored.occurrences; + insert refactored.body; + } + }; + + // Avoiding adding extra blank spaces when method is not refactored. + // Not sure why, also not a big deal, but is annoying + methodWasRefactored = refactoredCountBeforeCurrentMethod != refactoredCount; + if (methodWasRefactored) + insert((MethodDeclaration) ` `); + } + }; + + return ; +} + +// TODO What happens when two for statements are refactored inside the same method? +public tuple[MethodBody body, int occurrences] refactorEnhancedForStatementsInMethodBody(CompilationUnit unit, MethodHeader methodHeader, MethodBody methodBody) { + set[MethodVar] availableVars = {}; + alreadyComputedCurrentMethodAvailableVars = false; + occurrences = 0; + + MethodBody refactoredMethodBody = methodBody; + + top-down visit(methodBody) { + case EnhancedForStatement enhancedForStmt: + visit(enhancedForStmt) { + + case (EnhancedForStatement) `for ( : ) `: { + + if(!alreadyComputedClassFields) { + currentClassFields = findClassFields(unit); + alreadyComputedClassFields = true; + } + + if(!alreadyComputedCurrentMethodAvailableVars) { + methodVars = findLocalVariables(methodHeader, methodBody); + availableVars = retainLocalVariablesIfDuplicates(currentClassFields, methodVars); + alreadyComputedAvailableVars = true; + } + + if(isLoopRefactorable(availableVars, collectionId, loopBody)) { + + try { + refactoredMethodBody = refactorEnhancedToFunctional(availableVars, enhancedForStmt, methodBody, iteratedVarName, collectionId); + occurrences += 1; + + refactoredCount += 1; + + if(PRINT_DEBUG) { + println("refactored: " + toString(refactoredCount)); + println(enhancedForStmt); + println("---"); + println(refactoredMethodBody); + println(); + } + } catch: { + // ignore. continuing + // 'continue' do not works as expected in 'visit' statements + ; + } + } + } + + } + + case (EnhancedForStatementNoShortIf) `for ( : ) `: + println("TODO"); + } + + return ; +} + +private bool isLoopRefactorable(set[MethodVar] availableVariables, Expression collectionId, Statement loopBody) { + return loopBodyPassConditions(loopBody) && isIteratingOnCollection(collectionId, availableVariables) && + atMostOneReferenceToNonEffectiveFinalVar(availableVariables, loopBody); +} + +// TODO extract module and test it +private bool loopBodyPassConditions(Statement loopBody) { + returnCount = 0; + visit(loopBody) { + case (ThrowStatement) `throw new ( );`: { + if ("" in checkedExceptionClasses) return false; + } + + case (BreakStatement) `break ;`: return false; + + case (ReturnStatement) `return ;`: { + returnExpStr = unparse(returnExp); + if(returnExpStr != "true" || returnExpStr != "false") + return false; + + returnCount += 1; + } + + // TODO remove in case we refactor loops with if with continue + case ContinueStatement continueStmt: return false; + + // labeled continue. + case (ContinueStatement) `continue ;`: return false; + } + + if (returnCount > 1) return false; + + return true; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/EnhancedLoopExpression.rsc b/src/lang/java/refactoring/forloop/EnhancedLoopExpression.rsc new file mode 100644 index 0000000..b7ceb8c --- /dev/null +++ b/src/lang/java/refactoring/forloop/EnhancedLoopExpression.rsc @@ -0,0 +1,52 @@ +module lang::java::refactoring::forloop::EnhancedLoopExpression + +import lang::java::\syntax::Java18; +import ParseTree; +import String; +import IO; +import lang::java::refactoring::forloop::MethodVar; + +// XXX Only checking iterable variables defined in method (local and parameter) +// Need to verify class and instance variables too! +// Doing the full check on a method call will be an entire new problem +// example: for (Object rowKey : table.rowKeySet()) + +// Relying on compiler to help finding if it's an array or not +// Compiler gives error if expression is not Array/Collection +// Therefore we only check if the expression is an Array +public bool isIteratingOnCollection(Expression exp, set[MethodVar] availableVariables) { + if (!isMethodInvocation(exp)) + return isIdentifierACollection(exp, availableVariables); + else + return false; +} + +// XXX Ignoring Casts too. +// Redundant for now, because any method invocation will contain '(' +// But not everything that have '(' will be a method invocation. (Casts for instance) +private bool isMethodInvocation(Expression exp) { + expStr = ""; + return contains(expStr, "(") && parsesAsMethodInvocation(expStr); +} + +private bool parsesAsMethodInvocation(str expStr) { + try { + parse(#MethodInvocation, expStr); + return true; + } catch: + return false; +} + +private bool isIdentifierACollection(Expression exp, set[MethodVar] availableVariables) { + varName = trim(unparse(exp)); + // TODO eventually change/remove when dealing correctly with fields + local variables + varName = replaceFirst(varName, "this.", ""); + + var = findByName(availableVariables, varName); + return !isTypePlainArray(var) && !isIterable(var); +} + +// FIXME +private bool isExpressionReturningACollection(Expression exp) { + return false; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/ForLoopBodyReferences.rsc b/src/lang/java/refactoring/forloop/ForLoopBodyReferences.rsc new file mode 100644 index 0000000..c322377 --- /dev/null +++ b/src/lang/java/refactoring/forloop/ForLoopBodyReferences.rsc @@ -0,0 +1,53 @@ +module lang::java::refactoring::forloop::ForLoopBodyReferences + +import lang::java::\syntax::Java18; +import String; +import ParseTree; +import IO; +import Set; +import lang::java::refactoring::forloop::MethodVar; + +public bool atMostOneReferenceToNonEffectiveFinalVar(set[MethodVar] localVariables, Statement loopBody) { + return getTotalOfNonEffectiveFinalVarsReferenced(localVariables, loopBody) <= 1; +} + +public int getTotalOfNonEffectiveFinalVarsReferenced(set[MethodVar] localVariables, EnhancedForStatement forStmt) { + return getTotalOfNonEffectiveFinalVarsReferenced(localVariables, retrieveLoopBodyFromEnhancedFor(forStmt)); +} + +public Statement retrieveLoopBodyFromEnhancedFor(EnhancedForStatement forStmt) { + top-down-break visit (forStmt) { + case (EnhancedForStatement) `for ( : ) `: + return loopBody; + } + throw "Error"; +} + +public int getTotalOfNonEffectiveFinalVarsReferenced(set[MethodVar] localVariables, Statement loopBody) { + varsReferencedNames = findVariablesReferenced(loopBody); + nonEffectiveFinalVarsReferencedCount = 0; + + for (varReferencedName <- varsReferencedNames) { + var = findByName(localVariables, varReferencedName); + if (!isEffectiveFinal(var) && !var.isDeclaredWithinLoop) + nonEffectiveFinalVarsReferencedCount += 1; + } + + return nonEffectiveFinalVarsReferencedCount; +} + +// XXX UsedVariables could use the same thing +// XXX Ignoring class fields. ('this.x') +public set[str] findVariablesReferenced(Statement loopBody) { + set[str] varsReferenced = {}; + + visit (loopBody) { + case ExpressionName expName: { + visit(expName) { + case Identifier id: varsReferenced += unparse(id); + } + } + } + + return varsReferenced; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/ForLoopToFunctional.rsc b/src/lang/java/refactoring/forloop/ForLoopToFunctional.rsc new file mode 100644 index 0000000..6789198 --- /dev/null +++ b/src/lang/java/refactoring/forloop/ForLoopToFunctional.rsc @@ -0,0 +1,395 @@ +module lang::java::refactoring::forloop::ForLoopToFunctional + +import IO; +import List; +import Set; +import String; +import lang::java::\syntax::Java18; +import ParseTree; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::ProspectiveOperation; +import lang::java::refactoring::forloop::UsedVariables; +import lang::java::refactoring::forloop::AvailableVariables; +import lang::java::refactoring::forloop::OperationType; +import lang::java::refactoring::forloop::ForLoopBodyReferences; +import lang::java::refactoring::forloop::BreakIntoStatements; + +public data ComposableProspectiveOperation = composableProspectiveOperation(ProspectiveOperation prOp, set[str] neededVars, set[str] availableVars); + +public MethodBody refactorEnhancedToFunctional(set[MethodVar] methodVars, EnhancedForStatement forStmt, MethodBody methodBody, VariableDeclaratorId iteratedVarName, Expression collectionId) { + refactored = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + forStatement = parse(#Statement, unparse(forStmt)); + refactoredMethodBody = refactorToFunctional(methodBody, forStatement, refactored); + return refactoredMethodBody; +} + +public Statement buildRefactoredEnhancedFor(set[MethodVar] methodVars, EnhancedForStatement forStmt, MethodBody methodBody, VariableDeclaratorId iteratedVarName, Expression collectionId) { + composablePrOps = retrieveComposableProspectiveOperations(methodVars, forStmt); + return buildFunctionalStatement(methodVars, composablePrOps, forStmt, iteratedVarName, collectionId); +} + +MethodBody refactorToFunctional(MethodBody methodBody, Statement forStmt, Statement refactored) = top-down-break visit(methodBody) { + case forStmt + => refactored +}; + +public list[ComposableProspectiveOperation] retrieveComposableProspectiveOperations(set[MethodVar] methodVars, EnhancedForStatement forStmt) { + prospectiveOperations = retrieveProspectiveOperations(methodVars, forStmt); + nonEffectiveOutsideVarsReferencedCount = getTotalOfNonEffectiveFinalVarsReferenced(methodVars, forStmt); + if (canOperationsBeRefactored(prospectiveOperations, nonEffectiveOutsideVarsReferencedCount)) { + composablePrOps = createComposableProspectiveOperationsWithVariableAvailability(prospectiveOperations, methodVars); + + composablePrOps = mergeIntoComposableOperations(composablePrOps); + + return rearrangeMapBodiesIfNeeded(composablePrOps); + } else + throw "CanNotBeRefactored"; +} + +private list[ComposableProspectiveOperation] createComposableProspectiveOperationsWithVariableAvailability(list[ProspectiveOperation] prOps, set[MethodVar] methodVars) { + composablePrOps = []; + for (prOp <- prOps) { + availableVars = retrieveAvailableVariables(prOp, methodVars); + neededVars = retrieveNeededVars(prOp, availableVars); + composablePrOps += composableProspectiveOperation(prOp, neededVars, availableVars); + } + + return composablePrOps; +} + +private set[str] retrieveNeededVars(ProspectiveOperation prOp, set[str] availableVars) { + neededVars = retrieveUsedVariables(prOp); + neededVars -= availableVars; + return neededVars; +} + +private list[ComposableProspectiveOperation] mergeIntoComposableOperations(list[ComposableProspectiveOperation] composablePrOps) { + // exclude first, since we iterate index and index-1 + listIndexes = [1 .. size(composablePrOps)]; + // iterating bottom-up + for (int i <- reverse(listIndexes)) { + curr = composablePrOps[i]; + prev = composablePrOps[i - 1]; + if (!canBeChained(prev, curr)) { + if(neitherCanBeMerged(prev, curr)) + throw "CanNotBeRefactored. Both operations are not mergeable"; + + opsSize = size(composablePrOps); + + if (isFilter(prev.prOp) || isFilter(curr.prOp)) { + while(opsSize > i) { + ComposableProspectiveOperation last = composablePrOps[opsSize - 1]; + ComposableProspectiveOperation beforeLast = composablePrOps[opsSize - 2]; + + merged = mergeComposablePrOps(beforeLast, last); + composablePrOps = slice(composablePrOps, 0, opsSize - 2) + merged; + + opsSize = size(composablePrOps); + } + } else { + merged = mergeComposablePrOps(prev, curr); + composablePrOps = composablePrOps[0..(i - 1)] + merged + composablePrOps[(i + 1)..]; + } + + } + } + return composablePrOps; +} + +private bool canBeChained(ComposableProspectiveOperation prev, ComposableProspectiveOperation curr) { + return size(curr.neededVars) <= 1 && isCurrNeededVarsInPrevAvailabilitySet(curr.neededVars, prev); +} + +private bool isCurrNeededVarsInPrevAvailabilitySet(set[str] currNeededVars, ComposableProspectiveOperation prev) { + prevAvailabilitySet = prev.availableVars + prev.neededVars; + for(currNeededVar <- currNeededVars) + if(currNeededVar notin prevAvailabilitySet) return false; + return true; +} + +private bool neitherCanBeMerged(ComposableProspectiveOperation prev, ComposableProspectiveOperation curr) { + return !isMergeable(prev.prOp) || !isMergeable(curr.prOp); +} + +private ComposableProspectiveOperation mergeComposablePrOps(ComposableProspectiveOperation prev, ComposableProspectiveOperation curr) { + if (isFilter(prev.prOp)) + return mergeIntoAnIfThenStmt(prev, curr); + else + return mergeIntoABlock(prev, curr); +} + +private ComposableProspectiveOperation mergeIntoAnIfThenStmt(ComposableProspectiveOperation prev, ComposableProspectiveOperation curr) { + prOp = mergeTwoOpsInAnIfThenStmt(prev.prOp, curr.prOp); + return mergeComposableProspectiveOperations(prOp, prev, curr); +} + +private ProspectiveOperation mergeTwoOpsInAnIfThenStmt(ProspectiveOperation prev, ProspectiveOperation curr) { + Expression exp = parse(#Expression, prev.stmt); + Statement thenStmt = parse(#Statement, curr.stmt); + ifThenStmt = [IfThenStatement] "if () "; + return prospectiveOperation(unparse(ifThenStmt), curr.operation); +} + +private ComposableProspectiveOperation mergeComposableProspectiveOperations(ProspectiveOperation prOp, ComposableProspectiveOperation prev, ComposableProspectiveOperation curr) { + mergedAvailableVars = mergeAvailableVars(curr.availableVars, prev.availableVars); + mergedNeededVars = mergeNeededVars(curr.neededVars, prev.neededVars, mergedAvailableVars); + return composableProspectiveOperation(prOp, mergedNeededVars, mergedAvailableVars); +} + +private set[str] mergeAvailableVars(set[str] currAvailableVars, prevAvailableVars) { + return currAvailableVars + prevAvailableVars; +} + +private set[str] mergeNeededVars(set[str] currNeededVars, set[str] prevNeededVars, set[str] mergedAvailableVars) { + neededVars = currNeededVars + prevNeededVars; + return neededVars - mergedAvailableVars; +} + +private ComposableProspectiveOperation mergeIntoABlock(ComposableProspectiveOperation prev, ComposableProspectiveOperation curr) { + prevStatements = retrieveAllStatements(prev.prOp); + currStatements = retrieveAllStatements(curr.prOp); + list[str] statements = prevStatements + currStatements ; + Block statementsAsOneBlock = transformStatementsInBlock(statements); + prOp = prospectiveOperation(unparse(statementsAsOneBlock), curr.prOp.operation); + return mergeComposableProspectiveOperations(prOp, prev, curr); +} + +private list[str] retrieveAllStatements(ProspectiveOperation prOp) { + if(isLocalVariableDeclarationStatement(prOp.stmt)) + return [prOp.stmt]; + return breakIntoStatementsAsStringList(prOp.stmt); +} + +private Block transformStatementsInBlock(list[str] stmts) { + str joined = "{\n"; + for(stmt <- stmts) + joined += (stmt + "\n"); + joined += "}"; + return parse(#Block, joined); +} + +private list[ComposableProspectiveOperation] rearrangeMapBodiesIfNeeded(list[ComposableProspectiveOperation] composablePrOps) { + listIndexes = [1 .. size(composablePrOps)]; + for (int i <- reverse(listIndexes)) { + curr = composablePrOps[i - 1]; + next = composablePrOps[i]; + if (isMap(curr.prOp)) + // Modifying in place + composablePrOps[i - 1] = rearrangeMapBody(curr, next.neededVars); + } + + return composablePrOps; +} + +private ComposableProspectiveOperation rearrangeMapBody(ComposableProspectiveOperation curr, set[str] nextNeededVars) { + prOp = curr.prOp; + if(isLocalVariableDeclarationStatement(prOp.stmt)) + return rearrangeLocalVariableDeclarationMapBody(curr, nextNeededVars); + else if(isNumericLiteral(prOp.stmt)) + return curr; + else + return addReturnToMapBody(curr, nextNeededVars); +} + +private ComposableProspectiveOperation rearrangeLocalVariableDeclarationMapBody(ComposableProspectiveOperation curr, set[str] nextNeededVars) { + lvdl = parse(#LocalVariableDeclarationStatement, curr.prOp.stmt); + varName = ""; + visit(lvdl) { + case VariableDeclaratorId varId: varName = trim(unparse(varId)); + } + + if (varName notin nextNeededVars) + return addReturnToMapBody(curr, nextNeededVars); + + return curr; +} + +private bool isNumericLiteral(str stmt) { + // FIXME + return false; +} + +private ComposableProspectiveOperation addReturnToMapBody(ComposableProspectiveOperation curr, set[str] nextNeededVars) { + list[str] stmts = []; + stmt = curr.prOp.stmt; + if (isBlock(stmt)) + stmts += breakIntoStatementsAsStringList(parse(#Block, stmt)); + else + stmts += stmt; + + varName = retrieveLambdaParameterNameWhenNotSingleStmtVarDecl(nextNeededVars); + stmts += "return ;"; + block = transformStatementsInBlock(stmts); + + curr.prOp.stmt = unparse(block); + return curr; +} + +private Statement buildFunctionalStatement(set[MethodVar] methodVars, list[ComposableProspectiveOperation] composablePrOps, EnhancedForStatement forStmt, VariableDeclaratorId iteratedVarName, Expression collectionId) { + if(size(composablePrOps) == 1 && isForEach(composablePrOps[0].prOp)) + return buildStatementForOnlyOneForEach(composablePrOps[0].prOp, iteratedVarName, collectionId); + + return chainOperationsIntoStatement(methodVars, composablePrOps, collectionId); +} + +private Statement buildStatementForOnlyOneForEach(ProspectiveOperation prOp, VariableDeclaratorId iteratedVarName, Expression collectionId) { + stmtBlock = transformIntoBlock(prOp.stmt); + iteratedVarName = trimEndingBlankSpace(iteratedVarName); + return parse(#Statement, ".forEach( -\> );"); +} + +private Block transformIntoBlock(str stmt) { + if(isBlock(stmt)) return parse(#Block, stmt); + return parse(#Block, "{\n\n}"); +} + +private VariableDeclaratorId trimEndingBlankSpace(VariableDeclaratorId varId) { + return parse(#VariableDeclaratorId, trim(unparse(varId))); +} + +private Statement chainOperationsIntoStatement(set[MethodVar] methodVars, list[ComposableProspectiveOperation] composablePrOps, Expression collectionId) { + str chainStr = ".stream()"; + + for(composablePrOp <- composablePrOps) { + chainableOperation = buildChainableOperation(methodVars, composablePrOp); + chainStr = "." + chainableOperation; + } + + return parse(#Statement, ";"); +} + +private str buildChainableOperation(set[MethodVar] methodVars, ComposableProspectiveOperation cPrOp) { + prOp = cPrOp.prOp; + if(isReduce(prOp)) + return buildMapReduceOperation(methodVars, cPrOp); + + return prOp.operation + "(" + retrieveLambdaParameterName(cPrOp) + " -\> " + + retrieveLambdaBody(prOp) + ")"; +} + +private str retrieveLambdaParameterName(ComposableProspectiveOperation cPrOp) { + singleStmtVarDecl = checkIfSingleStmtVarDecl(cPrOp.prOp.stmt); + if (singleStmtVarDecl.condition) + return singleStmtVarDecl.name; + return retrieveLambdaParameterNameWhenNotSingleStmtVarDecl(cPrOp.neededVars); +} + +private tuple[bool condition, str name] checkIfSingleStmtVarDecl(str stmt) { + semiCollonOccurrences = findAll("", ";"); + isSingleStmt = size(semiCollonOccurrences) == 1; + if(isSingleStmt) { + try { + lvdl = parse(#LocalVariableDeclarationStatement, stmt); + visit (lvdl) { + case (VariableDeclaratorId) ``: + return ">; + } + } catch: + return ; + } + return ; +} + +private str retrieveLambdaParameterNameWhenNotSingleStmtVarDecl(set[str] neededVars) { + return isEmpty(neededVars) ? "_item" : getOneFrom(neededVars); +} + +private str retrieveLambdaBody(ProspectiveOperation prOp) { + if(isFilter(prOp) || isAnyMatch(prOp) || isNoneMatch(prOp) || isBlock(prOp.stmt)) + return prOp.stmt; + else if(isMap(prOp)) { + return getLambdaBodyForMap(prOp.stmt); + } + else // isForEach(prOp) + return unparse(transformIntoBlock(prOp.stmt)); +} + +private str getLambdaBodyForMap(str stmt) { + if(isExpressionStatement(stmt)) + return getLambdaBodyForMapWhenExpressionStatement(stmt); + else + return getLambdaBodyForMapWhenLocalVariableDeclaration(stmt); +} + +private bool isExpressionStatement(str stmt) { + try { + parse(#ExpressionStatement, stmt); + return true; + } catch: + return false; +} + +private str getLambdaBodyForMapWhenExpressionStatement(str stmt) { + return removeEndingSemiCollonIfPresent(stmt); +} + +private str removeEndingSemiCollonIfPresent(str stmt) { + if(endsWith(stmt, ";")) + stmt = substring(stmt, 0, size(stmt)-1); + return stmt; +} + +private str getLambdaBodyForMapWhenLocalVariableDeclaration(str stmt) { + stmt = removeEndingSemiCollonIfPresent(stmt); + + lvdl = parse(#LocalVariableDeclaration, stmt); + visit(lvdl) { + case VariableInitializer vi: return unparse(vi); + } + throw "No variable initializer in MAP"; +} + +// TODO check if prefix and postfix increment/decrement after ProspectiveOperation is working +private str buildMapReduceOperation(set[MethodVar] methodVars, ComposableProspectiveOperation cPrOp) { + mapOperation = ""; + reduceOperation = ""; + stmt = parse(#Statement, cPrOp.prOp.stmt); + bottom-up-break visit(stmt) { + case (Assignment) ` `: { + reducingVar = unparse(lhs); + + lambdaParamName = retrieveLambdaParameterName(cPrOp); + mapOperation = "map( -\> )"; + + reduceOperation += buildReduceOperation(methodVars, op, reducingVar); + } + } + + + + return "."; +} + +private str buildReduceOperation(set[MethodVar] methodVars, AssignmentOperator op, str reducingVar) { + reduceOperation = ""; + if("" == "+=") + reduceOperation = buildPlusAssignmentReduce(methodVars, reducingVar); + else + reduceOperation = buildSimpleExplicitReduce("", reducingVar); + + return reduceOperation; +} + +private str buildPlusAssignmentReduce(set[MethodVar] methodVars, str reducingVar) { + if(isString(methodVars, reducingVar)) + return "reduce(, String::concat)"; + else + if(isInteger(methodVars, reducingVar)) + return "reduce(, Integer::sum)"; + else + return buildSimpleExplicitReduce("+=", reducingVar); +} + +private bool isString(set[MethodVar] methodVars, str varName) { + var = findByName(methodVars, varName); + return isString(var); +} + +private bool isInteger(set[MethodVar] methodVars, str varName) { + var = findByName(methodVars, varName); + return isInteger(var); +} + +private str buildSimpleExplicitReduce(str op, str reducingVar) { + return "reduce(, (accumulator, _item) -\> accumulator _item)"; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/LocalVariablesFinder.rsc b/src/lang/java/refactoring/forloop/LocalVariablesFinder.rsc new file mode 100644 index 0000000..0670417 --- /dev/null +++ b/src/lang/java/refactoring/forloop/LocalVariablesFinder.rsc @@ -0,0 +1,153 @@ +module lang::java::refactoring::forloop::LocalVariablesFinder + +import IO; +import lang::java::\syntax::Java18; +import ParseTree; +import Set; +import String; +import lang::java::refactoring::forloop::MethodVar; + +public set[MethodVar] findLocalVariables(MethodHeader methodHeader, MethodBody methodBody) { + return findVariablesAsParameters(methodHeader) + findVariablesInsideBody(methodBody); +} + +private set[MethodVar] findVariablesAsParameters(MethodHeader methodHeader) { + set[MethodVar] methodParams = {}; + visit(methodHeader) { + case (FormalParameter) ` `: + methodParams += createParameterMethodVar(figureIfIsFinal(varMod), varId, varType); + } + return methodParams; +} + +private bool figureIfIsFinal(VariableModifier* varMod) { + return "" := "final"; +} + +private MethodVar createParameterMethodVar(bool isFinal, VariableDeclaratorId varId, UnannType varType) { + name = trim(unparse(varId)); + varTypeStr = trim(unparse(varType)); + bool isParameter = true; + bool isDeclaredWithinLoop = false; + bool isEffectiveFinal = true; + return methodVar(isFinal, name, varTypeStr, isParameter, isDeclaredWithinLoop, isEffectiveFinal); +} + +// XXX ugly and not really DRY way of checking for vars within loop +private set[MethodVar] findVariablesInsideBody(MethodBody methodBody) { + set[MethodVar] methodVars = {}; + set[str] nonEffectiveFinalOutsideLoopVars = {}; + set[str] varsWithinLoopNames = {}; + top-down visit(methodBody) { + + case EnhancedForStatement enhancedForStmt: { + visit(enhancedForStmt) { + case (EnhancedForStatement) `for ( : ) `: + methodVars += createLocalMethodVarWithinLoop(figureIfIsFinal(varMod), varId, varType); + + case (LocalVariableDeclaration) ` `: + visit(vdl) { + case (VariableDeclaratorId) ` `: { + varsWithinLoopNames += unparse(varId); + methodVars += createLocalMethodVarWithinLoop(figureIfIsFinal(varMod), varId, varType, dims); + } + } + } + } + + // XXX Redundant. Doing this inside MethodHeader and MethodBody + // might just visit the MethodDeclaration here. + case (FormalParameter) ` `: + methodVars += createParameterMethodVar(figureIfIsFinal(varMod), varId, varType); + + case (LocalVariableDeclaration) ` `: { + visit(vdl) { + case (VariableDeclaratorId) ` `: { + if(unparse(varId) notin varsWithinLoopNames) + methodVars += createLocalMethodVar(figureIfIsFinal(varMod), varId, varType, dims); + } + } + } + + case (Assignment) ` `: nonEffectiveFinalOutsideLoopVars += ""; + + case (PreIncrementExpression) `++ `: nonEffectiveFinalOutsideLoopVars += ""; + case (PreDecrementExpression) `-- `: nonEffectiveFinalOutsideLoopVars += ""; + case (PostIncrementExpression) ` ++`: nonEffectiveFinalOutsideLoopVars += ""; + case (PostDecrementExpression) ` --`: nonEffectiveFinalOutsideLoopVars += ""; + + case(CatchFormalParameter) ` `: + methodVars += createLocalMethodVar(figureIfIsFinal(varMod), varId, varType); + + } + + + return addNonEffectiveFinalVars(methodVars, nonEffectiveFinalOutsideLoopVars); +} + +private MethodVar createLocalMethodVar(bool isFinal, VariableDeclaratorId varId, UnannType varType) { + name = trim(unparse(varId)); + varTypeStr = trim(unparse(varType)); + bool isParameter = false; + bool isDeclaredWithinLoop = false; + bool isEffectiveFinal = true; + return methodVar(isFinal, name, varTypeStr, isParameter, isDeclaredWithinLoop, isEffectiveFinal); +} + +private MethodVar createLocalMethodVarWithinLoop(bool isFinal, VariableDeclaratorId varId, UnannType varType) { + name = trim(unparse(varId)); + varTypeStr = trim(unparse(varType)); + bool isParameter = false; + bool isDeclaredWithinLoop = true; + bool isEffectiveFinal = true; + return methodVar(isFinal, name, varTypeStr, isParameter, isDeclaredWithinLoop, isEffectiveFinal); +} + +private MethodVar createLocalMethodVar(bool isFinal, Identifier varId, UnannType varType, Dims? dims) { + name = trim(unparse(varId)); + varTypeStr = trim(unparse(varType)); + dimsStr = trim(unparse(dims)); + + // Standarizing arrays to have varType == [] + if(dimsStr == "[]") + varTypeStr += "[]"; + + bool isParameter = false; + bool isDeclaredWithinLoop = false; + bool isEffectiveFinal = true; + return methodVar(isFinal, name, varTypeStr, isParameter, isDeclaredWithinLoop, isEffectiveFinal); +} + +private MethodVar createLocalMethodVarWithinLoop(bool isFinal, Identifier varId, UnannType varType, Dims? dims) { + mv = createLocalMethodVar(isFinal, varId, varType, dims); + mv.isDeclaredWithinLoop = true; + return mv; +} + +private MethodVar createLocalMethodVar(bool isFinal, VariableDeclaratorId varId, CatchType varType) { + name = trim(unparse(varId)); + varTypeStr = trim(unparse(varType)); + bool isParameter = false; + bool isDeclaredWithinLoop = false; + bool isEffectiveFinal = true; + return methodVar(isFinal, name, varTypeStr, isParameter, isDeclaredWithinLoop, isEffectiveFinal); +} + +// XXX ugly handling of non effective finals (mainly due to usage of sets) +private set[MethodVar] addNonEffectiveFinalVars(set[MethodVar] methodVars, set[str] nonEffectiveFinalOutsideLoopVars) { + completeMethodVars = methodVars; + for (methodVar <- methodVars) { + for (nonEffectiveFinalVar <- nonEffectiveFinalOutsideLoopVars) { + if (methodVar.name == nonEffectiveFinalVar) { + completeMethodVars -= methodVar; + completeMethodVars += cloneMethodVarAsNonEffectiveFinal(methodVar); + } + } + } + return completeMethodVars; +} + +private MethodVar cloneMethodVarAsNonEffectiveFinal(MethodVar m) { + bool isEffectiveFinal = false; + return methodVar(m.isFinal, m.name, m.varType, m.isParameter, m.isDeclaredWithinLoop, isEffectiveFinal); +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/MethodVar.rsc b/src/lang/java/refactoring/forloop/MethodVar.rsc new file mode 100644 index 0000000..ee36a71 --- /dev/null +++ b/src/lang/java/refactoring/forloop/MethodVar.rsc @@ -0,0 +1,103 @@ +module lang::java::refactoring::forloop::MethodVar + +import Set; +import String; + +// TODO Review if using a Set is the best choice. Probably not. +public data MethodVar = methodVar(bool isFinal, str name, str varType, bool isParameter, bool isDeclaredWithinLoop, bool isEffectiveFinal); + +public bool isArray(MethodVar methodVar) { + return methodVar.varType == "array"; +} + +public bool isString(MethodVar methodVar) { + return methodVar.varType == "String"; +} + +public bool isInteger(MethodVar methodVar) { + varType = methodVar.varType; + return varType == "int" || varType == "Integer"; +} + +public bool isIterable(MethodVar methodVar) { + varType = methodVar.varType; + return startsWith(varType, "Iterable"); +} + +public bool isParameter(MethodVar methodVar) { + return !methodVar.isParameter; +} + +public set[MethodVar] retrieveFinals(set[MethodVar] methodVars) { + return { var | MethodVar var <- methodVars, var.isFinal }; +} + +public set[MethodVar] retrieveNonFinals(set[MethodVar] methodVars) { + return { var | MethodVar var <- methodVars, !var.isFinal }; +} + +public set[str] retrieveFinalsNames(set[MethodVar] methodVars) { + return { var.name | MethodVar var <- methodVars, var.isFinal }; +} + +public set[str] retrieveNonFinalsNames(set[MethodVar] methodVars) { + return { var.name | MethodVar var <- methodVars, !var.isFinal }; +} + +public MethodVar findByName(set[MethodVar] methodVars, str name) { + return getOneFrom({ var | MethodVar var <- methodVars, var.name == name }); +} + +public set[MethodVar] retrieveParameters(set[MethodVar] methodVars) { + return { var | MethodVar var <- methodVars, var.isParameter }; +} + +public set[MethodVar] retrieveNonParameters(set[MethodVar] methodVars) { + return { var | MethodVar var <- methodVars, !var.isParameter }; +} + +public set[str] retrieveParametersNames(set[MethodVar] methodVars) { + return { var.name | MethodVar var <- methodVars, var.isParameter }; +} + +public set[str] retrieveNonParametersNames(set[MethodVar] methodVars) { + return { var.name | MethodVar var <- methodVars, !var.isParameter }; +} + +public bool isTypePlainArray(MethodVar methodVar) { + return endsWith(methodVar.varType, "[]"); +} + +public set[MethodVar] retrieveDeclaredWithinLoop(set[MethodVar] methodVars) { + return { var | MethodVar var <- methodVars, var.isDeclaredWithinLoop }; +} + +public set[str] retrieveDeclaredWithinLoopNames(set[MethodVar] methodVars) { + return { var.name | MethodVar var <- methodVars, var.isDeclaredWithinLoop }; +} + +public set[MethodVar] retrieveNotDeclaredWithinLoop(set[MethodVar] methodVars) { + return { var | MethodVar var <- methodVars, !var.isDeclaredWithinLoop }; +} + +public set[str] retrieveNotDeclaredWithinLoopNames(set[MethodVar] methodVars) { + return { var.name | MethodVar var <- methodVars, !var.isDeclaredWithinLoop }; +} + +public bool isEffectiveFinal(MethodVar methodVar) { + return methodVar.isFinal || methodVar.isEffectiveFinal; +} + +// FIXME This will break if 'this.varName' is referenced, as we are removing the class field +// Would need to treat 'this.*' and change how we find vars by name +// One idea is to keep both localVariables AND classFields as separate lists. +// if both exists, a reference to a field can only be made by 'this.field' +public set[MethodVar] retainLocalVariablesIfDuplicates(set[MethodVar] classFields, set[MethodVar] localVars) { + duplicatedNames = retrieveAllNames(classFields) & retrieveAllNames(localVars); + duplicatedClassFields = { field | MethodVar field <- classFields, field.name in duplicatedNames }; + return (classFields - duplicatedClassFields) + localVars; +} + +private set[str] retrieveAllNames(set[MethodVar] vars) { + return { var.name | MethodVar var <- vars }; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/OperationType.rsc b/src/lang/java/refactoring/forloop/OperationType.rsc new file mode 100644 index 0000000..9dda9ff --- /dev/null +++ b/src/lang/java/refactoring/forloop/OperationType.rsc @@ -0,0 +1,8 @@ +module lang::java::refactoring::forloop::OperationType + +public str FILTER = "filter"; +public str MAP = "map"; +public str FOR_EACH = "forEach"; +public str REDUCE = "reduce"; +public str ANY_MATCH = "anyMatch"; +public str NONE_MATCH = "noneMatch"; \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/ProspectiveOperation.rsc b/src/lang/java/refactoring/forloop/ProspectiveOperation.rsc new file mode 100644 index 0000000..5c6cdc4 --- /dev/null +++ b/src/lang/java/refactoring/forloop/ProspectiveOperation.rsc @@ -0,0 +1,244 @@ +module lang::java::refactoring::forloop::ProspectiveOperation + +import IO; +import List; +import String; +import lang::java::\syntax::Java18; +import ParseTree; +import java::lang::analysis::ParseTreeVisualization; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::OperationType; +import lang::java::refactoring::forloop::BreakIntoStatements; + +public data ProspectiveOperation = prospectiveOperation(str stmt, str operation); + +private list[MethodVar] methodLocalVars; + +public list[ProspectiveOperation] retrieveProspectiveOperations(set[MethodVar] localVars, EnhancedForStatement forStmt) { + methodLocalVars = localVars; + list[ProspectiveOperation] prospectiveOperations = []; + // TODO can receive as parameter only the loopBody from Refactorer. saving a visit. + top-down visit(forStmt) { + case (EnhancedForStatement) `for ( : ) `: { + prospectiveOperations = retrieveProspectiveOperationsFromStatement(stmt); + prospectiveOperations = markLastStmtAsEager(prospectiveOperations); + } + } + return prospectiveOperations; +} + +private list[ProspectiveOperation] retrieveProspectiveOperationsFromStatement(Statement stmt) { + list[ProspectiveOperation] prOps = []; + top-down-break visit(stmt) { + case Block block: { + prOps += retrieveProspectiveOperationsFromBlock(block); + } + case IfThenStatement ifStmt: { + stmtsBrokenInto = breakIntoStatements(stmt); + prOps += retrieveProspectiveOperationsFromIfThenStatement(ifStmt, stmtsBrokenInto); + } + case ExpressionStatement expStmt: { + statement = parse(#Statement, unparse(expStmt)); + prOps += retrieveProspectiveOperationFromSingleStatement(statement); + } + + case IfThenElseStatement ifElseStmt: throw "Not Refactoring If/Else for now"; + case ForStatement _: throw "Not Refactoring Inner Loops for now"; + case WhileStatement _: throw "Not Refactoring While Loops inside ForStatement for now"; + } + return prOps; +} + +private list[ProspectiveOperation] retrieveProspectiveOperationsFromBlock(Block block) { + list[ProspectiveOperation] prOps = []; + top-down-break visit(block) { + case BlockStatement blockStatement: { + top-down-break visit(blockStatement) { + case (IfThenStatement) `if ( ) `: { + ifThenStmt = [IfThenStatement] "if () "; + stmtsBrokenInto = breakIntoStatements(block); + prOps += retrieveProspectiveOperationsFromIfThenStatement(ifThenStmt, stmtsBrokenInto); + } + case (IfThenElseStatement) `if ( ) else `: { + throw "Not Refactoring If/Else for now"; + } + case LocalVariableDeclarationStatement lvdlStmt: { + // not an if, so it's a map + prOps += prospectiveOperation(unparse(lvdlStmt), MAP); + } + case StatementWithoutTrailingSubstatement otherStmt: { + statement = parse(#Statement, unparse(otherStmt)); + prOps += retrieveProspectiveOperationFromSingleStatement(statement); + } + + case IfThenElseStatement ifElseStmt: throw "Not Refactoring If/Else for now"; + case ForStatement _: throw "Not Refactoring Inner Loops for now"; + case WhileStatement _: throw "Not Refactoring While Loops inside ForStatement for now"; + } + } + } + return prOps; +} + +private list[ProspectiveOperation] retrieveProspectiveOperationsFromIfThenStatement(IfThenStatement ifStmt, list[Stmt] currBlockStmts) { + list[ProspectiveOperation] prOps = []; + foundReturn = false; + top-down-break visit (ifStmt) { + case (IfThenStatement) `if ( ) `: { + top-down-break visit (thenStmt) { + case Statement stmt: { + visit(stmt) { + case (ReturnStatement) `return ;`: { + foundReturn = true; + prOps += createAnyMatchOrNoneMatchPrOp("", ""); + } + + case IfThenElseStatement ifElseStmt: throw "Not Refactoring If/Else for now"; + case ForStatement _: throw "Not Refactoring Inner Loops for now"; + case WhileStatement _: throw "Not Refactoring While Loops inside ForStatement for now"; + } + + if (!foundReturn) { + if (!ifStatementHasNoStatementAfter(ifStmt, currBlockStmts)) + prOps += prospectiveOperation(unparse(ifStmt), MAP); + else { + prOps += prospectiveOperation(unparse(ifExp), FILTER); + prOps += retrieveProspectiveOperationsFromThenStatement(thenStmt); + } + } + } + + } + } + } + return prOps; +} + +private ProspectiveOperation createAnyMatchOrNoneMatchPrOp(str returnExp, str ifExp) { + if (returnExp == "true") + return prospectiveOperation(ifExp, ANY_MATCH); + else // (returnExp == "false") + return prospectiveOperation(ifExp, NONE_MATCH); +} + +private bool ifStatementHasNoStatementAfter(IfThenStatement ifStmt, list[Stmt] currBlockStmts) { + lastStmt = last(currBlockStmts); + return lastStmt.stmtType == "IfThenStatement" && lastStmt.statement == ifStmt; +} + +private list[ProspectiveOperation] retrieveProspectiveOperationsFromThenStatement(Statement thenStmt) { + if (isSingleStatementBlock(thenStmt)) + return [retrieveProspectiveOperationFromSingleStatement(thenStmt)]; + else + return retrieveProspectiveOperationsFromStatement(thenStmt); +} + +// XXX Does this really work? +private bool isSingleStatementBlock(Statement thenStmt) { + if (isBlock("")) { + semiCollonOccurrences = findAll("", ";"); + return size(semiCollonOccurrences) == 1; + } + + return false; +} + +private ProspectiveOperation retrieveProspectiveOperationFromSingleStatement(Statement statement) { + if (isReducer(statement)) + return prospectiveOperation(unparse(statement), REDUCE); + else + return prospectiveOperation(unparse(statement), MAP); +} + +// TODO implement prefix and postfix increment/decrement +private bool isReducer(Statement statement) { + visit (statement) { + case (Assignment) ` `: + return isCompoundAssignmentOperator(assignmentOp) && isReferenceToNonFinalLocalVar(lhs); + } + + return false; +} + +private bool isCompoundAssignmentOperator(AssignmentOperator assignmentOp) { + operatorStr = unparse(assignmentOp); + return operatorStr != "=" && operatorStr != "\>\>\>=" && + operatorStr != "^="; +} + +private bool isReferenceToNonFinalLocalVar(LeftHandSide lhs) { + varName = trim(unparse(lhs)); + var = findByName(methodLocalVars, varName); + return !isEffectiveFinal(var); +} + +private list[ProspectiveOperation] markLastStmtAsEager(list[ProspectiveOperation] prOps) { + lastPrOp = prOps[-1]; + if(lastPrOp.operation == MAP) + lastPrOp.operation = FOR_EACH; + + // all elements but the last + the last one (eagerized or not) + return prefix(prOps) + lastPrOp; +} + +public bool isMergeable(ProspectiveOperation prOp) { + return isFilter(prOp) || isMap (prOp) || isForEach(prOp); +} + +public bool isEagerOperation(ProspectiveOperation prOp) { + return !isLazyOperation(prOp); +} + +public bool isLazyOperation(ProspectiveOperation prOp) { + return isFilter(prOp) || isMap(prOp); +} + +public bool isFilter(ProspectiveOperation prOp) { + return prOp.operation == FILTER; +} + +public bool isReduce(ProspectiveOperation prOp) { + return prOp.operation == REDUCE; +} + +public bool isAnyMatch(ProspectiveOperation prOp) { + return prOp.operation == ANY_MATCH; +} + +public bool isNoneMatch(ProspectiveOperation prOp) { + return prOp.operation == NONE_MATCH; +} + +public bool isMap(ProspectiveOperation prOp) { + return prOp.operation == MAP; +} + +public bool isForEach(ProspectiveOperation prOp) { + return prOp.operation == FOR_EACH; +} + +public bool isLocalVariableDeclarationStatement(str stmt) { + try { + parse(#LocalVariableDeclarationStatement, stmt); + return true; + } catch: return false; +} + +public bool canOperationsBeRefactored(list[ProspectiveOperation] prOps, int nonEffectiveOutsideVarsReferencedCount) { + return !haveEagerOperationAsNonLast(prOps) + && isLoopAReducerIfHasMoreThanOneReferenceToOutsideNonEffectiveFinalVar(prOps, nonEffectiveOutsideVarsReferencedCount); +} + +private bool haveEagerOperationAsNonLast(list[ProspectiveOperation] prOps) { + operationsWithoutLast = prefix(prOps); + for(prOp <- operationsWithoutLast) + if(isEagerOperation(prOp)) return true; + + return false; +} + +private bool isLoopAReducerIfHasMoreThanOneReferenceToOutsideNonEffectiveFinalVar(list[ProspectiveOperation] prOps, int outsideNonEffectiveReferencesCount) { + if (outsideNonEffectiveReferencesCount == 1) + return isReduce(last(prOps)); + return true; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/UsedVariables.rsc b/src/lang/java/refactoring/forloop/UsedVariables.rsc new file mode 100644 index 0000000..e8f2a83 --- /dev/null +++ b/src/lang/java/refactoring/forloop/UsedVariables.rsc @@ -0,0 +1,112 @@ +module lang::java::refactoring::forloop::UsedVariables + +import IO; +import String; +import Set; +import lang::java::\syntax::Java18; +import ParseTree; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::ProspectiveOperation; + +public set[str] retrieveUsedVariables(ProspectiveOperation prOp) { + set[str] usedVariables = {}; + + if(isFilter(prOp)) + usedVariables += retrieveUsedVarsFromFilter(prOp.stmt); + else if (isLocalVariableDeclarationStatement(prOp.stmt)) + usedVariables += retrieveUsedVarsFromLocalVariableDeclarationStmt(prOp.stmt); + else + usedVariables += retrieveUsedVarsFromStatement(prOp.stmt); + + return usedVariables; +} + + +private set[str] retrieveUsedVarsFromFilter(str stmt) { + if(isIfThenStatement(stmt)) + return retrieveUsedVarsFromIfThenStmt(stmt); + else + return retrieveUsedVarsFromExpression(stmt); +} + +public bool isIfThenStatement(str stmt) { + try { + parse(#IfThenStatement, stmt); + return true; + } catch: return false; +} + +// XXX pretty redundant lookups for 'ExpressionName' around this module +private set[str] retrieveUsedVarsFromIfThenStmt(str stmt) { + set[str] usedVariables = {}; + ifThenStmt = parse(#IfThenStatement, stmt); + + visit(ifThenStmt) { + case ExpressionName expName: { + visit(expName) { + case Identifier id: usedVariables += unparse(id); + } + } + } + + return usedVariables; +} + +private set[str] retrieveUsedVarsFromExpression(str stmt) { + set[str] usedVariables = {}; + exp = parse(#Expression, stmt); + + visit(exp) { + case ExpressionName expName: { + visit(expName) { + case Identifier id: usedVariables += unparse(id); + } + } + } + + return usedVariables; +} + +// XXX Parsing twice (isLocal... and this method) the stmt +// should not be a big deal since it's only a stmt from a prospective operation. +// Using this pattern (parsing to check if a stmt is of the type #Something) and then parsing again +// could refactor in the future to return a Tuple, with the bool and the parsed tree. +private set[str] retrieveUsedVarsFromLocalVariableDeclarationStmt(str stmt) { + set[str] usedVariables = {}; + + lvdlStmt = parse(#LocalVariableDeclarationStatement, stmt); + + visit(lvdlStmt) { + case ExpressionName expName: { + visit(expName) { + case Identifier id: usedVariables += unparse(id); + } + } + } + + return usedVariables; +} + +private set[str] retrieveUsedVarsFromStatement(str stmt) { + set[str] usedVariables = {}; + + stmt = parse(#Statement, getCorrectStatementAsString(stmt)); + + visit (stmt) { + case ExpressionName expName: { + visit(expName) { + case Identifier id: usedVariables += unparse(id); + } + } + } + + return usedVariables; +} + +private str getCorrectStatementAsString(str stmt) { + // stmt does not end with ';' it can't be parsed as a statement + // it can also be a block + if(!(endsWith(stmt, ";") || endsWith(stmt, "}"))) + return stmt + ";"; + return stmt; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/AvailableVariablesTest.rsc b/src/lang/java/refactoring/forloop/test/AvailableVariablesTest.rsc new file mode 100644 index 0000000..aae5dc8 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/AvailableVariablesTest.rsc @@ -0,0 +1,70 @@ +module lang::java::refactoring::forloop::\test::AvailableVariablesTest + +import Set; +import IO; +import lang::java::refactoring::forloop::AvailableVariables; +import lang::java::refactoring::forloop::ProspectiveOperation; +import lang::java::refactoring::forloop::OperationType; +import lang::java::refactoring::forloop::MethodVar; + +public test bool methodParamShouldBeAvailableVar() { + prOp = prospectiveOperation("writer.write(thing);", FOR_EACH); + methodVars = {methodVar(false, "thing", "String", false, true, false), + methodVar(false, "writer", "PrintWriter", true, false, false)}; + + availableVars = retrieveAvailableVariables(prOp, methodVars); + + return size(availableVars) == 1 && + "writer" in availableVars; +} + +public test bool varWithinLoopShouldNotBeAvailable() { + prOp = prospectiveOperation("rule.hasErrors()", FILTER); + methodVars = {methodVar(false, "count", "int", false, false, false), + methodVar(false, "rule", "ElementRule", false, true, false)}; + + availableVars = retrieveAvailableVariables(prOp, methodVars); + + return "rule" notin availableVars; +} + +public test bool varNotWithinLoopShouldBeAvailable() { + prOp = prospectiveOperation("rule.hasErrors()", FILTER); + methodVars = {methodVar(false, "count", "int", false, false, false), + methodVar(false, "rule", "ElementRule", false, true, false)}; + + availableVars = retrieveAvailableVariables(prOp, methodVars); + + return "count" in availableVars; +} + +public test bool localVariableDeclarationShouldBeAvailableVar() { + prOp = prospectiveOperation("ClassLoader cl = entry.getKey();", MAP); + methodVars = {}; // Independent in this case + + availableVars = retrieveAvailableVariables(prOp, methodVars); + + return size(availableVars) == 1 && + "cl" in availableVars; +} + +public test bool localVariableDeclAlongWithVarNotWithinLoopShouldBeAvailableVars() { + prOp = prospectiveOperation("ClassLoader cl = entry.getKey();", MAP); + methodVars = {methodVar(false, "result", "List\", false, false, false)}; + + availableVars = retrieveAvailableVariables(prOp, methodVars); + + return size(availableVars) == 2 && + "cl" in availableVars && + "result" in availableVars; +} + +public test bool localVariableDeclarationWithArgsInInitializerShouldBeAvailableVar() { + prOp = prospectiveOperation("ClassLoader cl = entry.getKey(argNeeded);", MAP); + methodVars = {}; // Independent in this case + + availableVars = retrieveAvailableVariables(prOp, methodVars); + + return size(availableVars) == 1 && + "cl" in availableVars; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/BreakIntoStatementsTest.rsc b/src/lang/java/refactoring/forloop/test/BreakIntoStatementsTest.rsc new file mode 100644 index 0000000..0a2230a --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/BreakIntoStatementsTest.rsc @@ -0,0 +1,123 @@ +module lang::java::refactoring::forloop::\test::BreakIntoStatementsTest + +import IO; +import lang::java::refactoring::forloop::BreakIntoStatements; +import lang::java::\syntax::Java18; +import lang::java::refactoring::forloop::\test::resources::ProspectiveOperationTestResources; +import lang::java::refactoring::forloop::ForLoopBodyReferences; +import ParseTree; +import java::lang::analysis::ParseTreeVisualization; + +public test bool ex1() { + fileLoc = |project://rascal-Java8//testes/forloop/ProspectiveOperation/SimpleShortEnhancedLoop|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(enhancedForLoop); + + stmts = breakIntoStatements(loopBody); + + Stmt stmt = stmts[0]; + return size(stmts) == 1 && + "" == "writer.write(thing);" && + stmt.stmtType == "ExpressionStatement"; +} + +public test bool ex2() { + fileLoc = |project://rascal-Java8//testes/forloop/ProspectiveOperation/ContinueAndReturnEnhancedLoop|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(enhancedForLoop); + + stmts = breakIntoStatements(loopBody); + + return size(stmts) == 2 && + "" == "if(e.getGrammarName() == null) continue;" && + stmts[1].stmtType == "IfThenStatement" && + "" == "if(e.getGrammarName().equals(grammarName))\r\n return true;" && + stmts[1].stmtType == "IfThenStatement"; +} + +public test bool ex3() { + fileLoc = |project://rascal-Java8//testes/forloop/ProspectiveOperation/FilterMapReduceEnhancedLoop|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(enhancedForLoop); + + stmts = breakIntoStatements(loopBody); + + Stmt stmt = stmts[0]; + return size(stmts) == 1 && + "" == "if(rule.hasErrors())\r\n count += rule.getErrors().size();" && + stmt.stmtType == "IfThenStatement"; +} + +public test bool ex4() { + enhancedForLoop = parse(#EnhancedForStatement, "for (Entry\ entry : entries) {\n elementsBuilder.add(entry.getElement());\n // cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount();\n i++;\n }"); + loopBody = retrieveLoopBodyFromEnhancedFor(enhancedForLoop); + + stmts = breakIntoStatements(loopBody); + + return size(stmts) == 2 && + "" == "elementsBuilder.add(entry.getElement());" && + stmts[0].stmtType == "ExpressionStatement" && + "" == "i++;" && + stmts[1].stmtType == "ExpressionStatement"; +} + +public test bool ex5() { + enhancedForLoop = parse(#EnhancedForStatement, "for (K key : keysToLoad) {\n V value = newEntries.get(key);\n if (value == null) {\n throw new InvalidCacheLoadException(\"loadAll failed to return a value for \" + key);\n }\n result.put(key, value);\n }"); + loopBody = retrieveLoopBodyFromEnhancedFor(enhancedForLoop); + + stmts = breakIntoStatements(loopBody); + + return size(stmts) == 3 && + "" == "V value = newEntries.get(key);" && + stmts[0].stmtType == "LocalVariableDeclarationStatement" && + "" == "if (value == null) {\n throw new InvalidCacheLoadException(\"loadAll failed to return a value for \" + key);\n }" && + stmts[1].stmtType == "IfThenStatement" && + "" == "result.put(key, value);" && + stmts[2].stmtType == "ExpressionStatement"; +} + +public test bool ex6() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/ForIfWithTwoStmtsInsideAndStmtAfterBlock.java|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileForLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(enhancedForLoop); + + stmts = breakIntoStatements(loopBody); + + return size(stmts) == 1 && + "" == "if (isIncluded(endpoint)) {\r\n\t\t\t\t\tString path = endpointHandlerMapping.getPath(endpoint.getPath());\r\n\t\t\t\t\tpaths.add(path);\r\n\t\t\t\t\tif (!path.equals(\"\")) {\r\n\t\t\t\t\t\tpaths.add(path + \"/**\");\r\n\t\t\t\t\t\t// Add Spring MVC-generated additional paths\r\n\t\t\t\t\t\tpaths.add(path + \".*\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\tpaths.add(path + \"/\");\r\n\t\t\t\t}" && + stmts[0].stmtType == "IfThenStatement"; +} + +// inside the if after the loop from previous example +public test bool ex7() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/ForIfWithTwoStmtsInsideAndStmtAfterBlock.java|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileForLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(enhancedForLoop); + list[Stmt] stmts = []; + top-down-break visit(loopBody) { + case (IfThenStatement) `if ( ) `: + stmts = breakIntoStatements(thenStmt); + } + + return size(stmts) == 4 && + "" == "String path = endpointHandlerMapping.getPath(endpoint.getPath());" && + stmts[0].stmtType == "LocalVariableDeclarationStatement" && + "" == "paths.add(path);" && + stmts[1].stmtType == "ExpressionStatement" && + "" == "if (!path.equals(\"\")) {\r\n\t\t\t\t\t\tpaths.add(path + \"/**\");\r\n\t\t\t\t\t\t// Add Spring MVC-generated additional paths\r\n\t\t\t\t\t\tpaths.add(path + \".*\");\r\n\t\t\t\t\t}" && + stmts[2].stmtType == "IfThenStatement" && + "" == "paths.add(path + \"/\");" && + stmts[3].stmtType == "ExpressionStatement"; +} + +public test bool shouldIncludeLocalVariableDeclarationInsideBlock() { + block = "{\nupdated.put(snapshot.getFolder(), snapshot);\nChangedFiles changedFiles = previous.getChangedFiles(snapshot,\r\n this.triggerFilter);\n}"; + + stmts = breakIntoStatements(block); + + return size(stmts) == 2 && + "" == "updated.put(snapshot.getFolder(), snapshot);" && + stmts[0].stmtType == "ExpressionStatement" && + "" == "ChangedFiles changedFiles = previous.getChangedFiles(snapshot,\r\n this.triggerFilter);" && + stmts[1].stmtType == "LocalVariableDeclarationStatement"; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/ClassFieldsFinderTest.rsc b/src/lang/java/refactoring/forloop/test/ClassFieldsFinderTest.rsc new file mode 100644 index 0000000..40a65a4 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/ClassFieldsFinderTest.rsc @@ -0,0 +1,22 @@ +module lang::java::refactoring::forloop::\test::ClassFieldsFinderTest + +import lang::java::\syntax::Java18; +import ParseTree; +import lang::java::refactoring::forloop::ClassFieldsFinder; +import lang::java::refactoring::forloop::MethodVar; +import Set; + +public test bool shouldReturnAllClassFields() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/ClassWithFields.java|; + unit = parse(#CompilationUnit, fileLoc); + + classFields = findClassFields(unit); + + return size(classFields) == 6 && + methodVar(true,"NAME","String",false,false,true) in classFields && + methodVar(false,"name","String",false,false,true) in classFields && + methodVar(true,"NUMBERS_SIZE","int",false,false,true) in classFields && + methodVar(false,"objArray2","Object[]",false,false,true) in classFields && + methodVar(false,"numbers","List\",false,false,true) in classFields && + methodVar(false,"objArray","Object[]",false,false,true) in classFields; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/EnhancedForLoopRefactorerTest.rsc b/src/lang/java/refactoring/forloop/test/EnhancedForLoopRefactorerTest.rsc new file mode 100644 index 0000000..1babb40 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/EnhancedForLoopRefactorerTest.rsc @@ -0,0 +1,17 @@ +module lang::java::refactoring::forloop::\test::EnhancedForLoopRefactorerTest + +import IO; +import lang::java::refactoring::forloop::EnhancedForLoopRefactorer; +import lang::java::refactoring::forloop::\test::resources::RefactorerTestResources; + +// comparing an entire file is not that practical +// comparing methods then +// but definitely should automate test for entire compilation unit +public test bool shouldRefactorInnerLoopButNoutOuterLoop() { + RefactorableFor refactorable = innerLoopButNotOuterLoop(); + + refactored = refactorEnhancedForStatementsInMethodBody(refactorable.unit, refactorable.header, refactorable.body); + + return refactored.body == refactorable.refactored && + refactored.occurrences == 1; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/EnhancedLoopExpressionTest.rsc b/src/lang/java/refactoring/forloop/test/EnhancedLoopExpressionTest.rsc new file mode 100644 index 0000000..98404e1 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/EnhancedLoopExpressionTest.rsc @@ -0,0 +1,55 @@ +module lang::java::refactoring::forloop::\test::EnhancedLoopExpressionTest + +import IO; +import lang::java::\syntax::Java18; +import ParseTree; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::LocalVariablesFinder; +import lang::java::refactoring::forloop::\test::resources::LocalVariablesFinderTestResources; +import lang::java::refactoring::forloop::ClassFieldsFinder; +import lang::java::refactoring::forloop::EnhancedLoopExpression; + +public test bool iterableShouldReturnFalse() { + params = paramsEnhancedForOnIterable(); + + return isIteratingOnCollection(params.exp, params.localVariables) == false; +} + +private tuple[Expression exp, set[MethodVar] localVariables] paramsEnhancedForOnIterable() { + tuple[MethodHeader methodHeader, MethodBody methodBody] method = getEnhancedForOnIterable(); + localVariables = findLocalVariables(method.methodHeader, method.methodBody); + // Making life easier + exp = parse(#Expression, "keys"); + return ; +} + +private tuple[MethodHeader methodHeader, MethodBody methodBody] getEnhancedForOnIterable() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/EnhancedForOnIterable.java|; + methodDeclaration = parse(#MethodDeclaration, readFile(fileLoc)); + visit(methodDeclaration) { + case (MethodDeclaration) ` `: { + return ; + } + } +} + +public test bool iterableParamShouldReturnFalse() { + exp = parse(#Expression, "types"); + methodHeader = iterableParameterMethodHeader(); + methodBody = iterableParameterMethodBody(); + localVariables = findLocalVariables(methodHeader, methodBody); + + return isIteratingOnCollection(exp, localVariables) == false; +} + +public test bool thisFieldListShouldReturnTrue() { + exp = parse(#Expression, "this.engineValves"); + methodHeader = parse(#MethodHeader, "void configureEngine(Engine engine)"); + methodBodyLoc = |project://rascal-Java8/testes/forloop/classFields/MethodBodyIteratingOnThisField2|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + localVariables = findLocalVariables(methodHeader, methodBody); + classFields = findClassFields(parse(#CompilationUnit, readFile(|project://rascal-Java8/testes/forloop/classFields/TomcatServletWebServerFactory.java|))); + vars = localVariables + classFields; + + return isIteratingOnCollection(exp, vars) == true; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/ForLoopBodyReferencesTest.rsc b/src/lang/java/refactoring/forloop/test/ForLoopBodyReferencesTest.rsc new file mode 100644 index 0000000..9c96254 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/ForLoopBodyReferencesTest.rsc @@ -0,0 +1,80 @@ +module lang::java::refactoring::forloop::\test::ForLoopBodyReferencesTest + +import IO; +import lang::java::\syntax::Java18; +import ParseTree; +import Set; +import lang::java::refactoring::forloop::ForLoopBodyReferences; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::LocalVariablesFinder; + +public test bool variablesReferenced1() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/ForWith3StatementsMapBody.java|; + forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(forStmt); + + result = findVariablesReferenced(loopBody); + + return size(result) == 5 && + "previous" in result && + "snapshot" in result && + "updated" in result && + "changedFiles" in result && + "changeSet" in result; +} + +public test bool variablesReferenced2() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/ForWithMultiStatementMap.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(forStmt); + + result = findVariablesReferenced(loopBody); + + return size(result) == 5 && + "key" in result && + "keysIt" in result && + "value" in result && + "v" in result && + "result" in result; +} + +public test bool variablesReferenced3() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2For2.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + loopBody = retrieveLoopBodyFromEnhancedFor(forStmt); + + result = findVariablesReferenced(loopBody); + + return size(result) == 4 && + "map" in result && + "entry" in result && + "expectedHash" in result && + "expectedEntrySetHash" in result; +} + +public test bool variablesReferenced4() { + forStmt = parse(#EnhancedForStatement, "for (Entry\ entry : entries) {\n elementsBuilder.add(entry.getElement());\n cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount();\n i++;\n }"); + loopBody = retrieveLoopBodyFromEnhancedFor(forStmt); + + result = findVariablesReferenced(loopBody); + + return size(result) == 4 && + "elementsBuilder" in result && + "entry" in result && + "cumulativeCounts" in result && + "i" in result; + +} + +public test bool shouldBeRefactorableWhenOneReferenceFound() { + methodHeader = parse(#MethodHeader, "\ ImmutableSortedMultiset\ copyOfSortedEntries(Comparator\ comparator, Collection\\> entries)"); + methodBodyLoc = |project://rascal-Java8/testes/forloop/localVariables/MethodBodyWithTwoReferencesToOutsideNonEffectiveVars|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + localVariables = findLocalVariables(methodHeader, methodBody); + forStmt = parse(#EnhancedForStatement, "for (Entry\ entry : entries) {\n elementsBuilder.add(entry.getElement());\n cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount();\n i++;\n }"); + + total = getTotalOfNonEffectiveFinalVarsReferenced(localVariables, forStmt); + refactorable = atMostOneReferenceToNonEffectiveFinalVar(localVariables, retrieveLoopBodyFromEnhancedFor(forStmt)); + + return total == 1 && refactorable; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/ForLoopToFunctionalTest.rsc b/src/lang/java/refactoring/forloop/test/ForLoopToFunctionalTest.rsc new file mode 100644 index 0000000..0ae6fe9 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/ForLoopToFunctionalTest.rsc @@ -0,0 +1,235 @@ +module lang::java::refactoring::forloop::\test::ForLoopToFunctionalTest + +import IO; +import String; +import lang::java::\syntax::Java18; +import ParseTree; +import java::lang::analysis::ParseTreeVisualization; + +import lang::java::refactoring::forloop::ForLoopToFunctional; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::LocalVariablesFinder; +import lang::java::refactoring::forloop::ClassFieldsFinder; +import lang::java::refactoring::forloop::\test::resources::LocalVariablesFinderTestResources; +import lang::java::refactoring::forloop::\test::resources::ProspectiveOperationTestResources; + + +public test bool shouldNameLambdaArgWithTheSameNameWhenMapIsASingleStmtVarDeclaration() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T1.java|; + methodBody = parse(#MethodBody, readFile(fileLoc)); + methodHeader = parse(#MethodHeader, "TestSuite createTestSuite()"); + set[MethodVar] methodVars = findLocalVariables(methodHeader, methodBody); + EnhancedForStatement forStmt = parse(#EnhancedForStatement, "for (Class\ testerClass : testers) {\n final TestSuite testerSuite =\n makeSuiteForTesterClass((Class\\>) testerClass);\n if (testerSuite.countTestCases() \> 0) {\n suite.addTest(testerSuite);\n }\n }"); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "testerClass"); + Expression collectionId = parse(#Expression, "testers"); + + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + + return "" == "testers.stream().map(testerSuite -\> makeSuiteForTesterClass((Class\\>) testerClass)).filter(testerSuite -\> testerSuite.countTestCases() \> 0).forEach(testerSuite -\> {\n suite.addTest(testerSuite);\n });"; +} + +public test bool reduceAsNotTheLastOperationShouldNotBeRefactored() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2.java|; + methodBody = parse(#MethodBody, readFile(fileLoc)); + methodHeader = parse(#MethodHeader, "void assertInvariants(Map\ map)"); + set[MethodVar] methodVars = findLocalVariables(methodHeader, methodBody); + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2For.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "key"); + Expression collectionId = parse(#Expression, "keySet"); + + try + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + catch: + return true; + + // Should have thrown exception + return false; +} + +public test bool shouldRefactorReduceWithCompoundPlusAssignmentOperator() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2.java|; + methodBody = parse(#MethodBody, readFile(fileLoc)); + methodHeader = parse(#MethodHeader, "void assertInvariants(Map\ map)"); + set[MethodVar] methodVars = findLocalVariables(methodHeader, methodBody); + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2For2.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "entry"); + Expression collectionId = parse(#Expression, "entrySet"); + + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + + return unparse(refactoredStatement) == "entrySet.stream().map(entry -\> {\nassertTrue(map.containsKey(entry.getKey()));\nreturn entry;\n}).map(entry -\> {\nassertTrue(map.containsValue(entry.getValue()));\nreturn entry;\n}).map(entry -\> {\nint expectedHash =\r\n (entry.getKey() == null ? 0 : entry.getKey().hashCode())\r\n ^ (entry.getValue() == null ? 0 : entry.getValue().hashCode());\nassertEquals(expectedHash, entry.hashCode());\nreturn expectedHash;\n}).map(expectedHash -\> expectedHash).reduce(expectedEntrySetHash, Integer::sum);"; +} + +public test bool shouldAddReturnToMapWithMoreThanOneStatement() { + methodBodyLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/MethodBodyWithMultiStatementMap.java|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + methodHeader = parse(#MethodHeader, "Iterable\\> findAll()"); + set[MethodVar] methodVars = findLocalVariables(methodHeader, methodBody); + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/ForWithMultiStatementMap.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "v"); + Expression collectionId = parse(#Expression, "values"); + + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + + return "" == "values.stream().map(v -\> {\nString key = keysIt.next();\nMetric\ value = deserialize(key, v, this.zSetOperations.score(key));\nreturn value;\n}).filter(value -\> value != null).forEach(value -\> {\r\n\t\t\t\tresult.add(value);\r\n\t\t\t});"; +} + +public test bool shouldAddCorrectReturnTo3StmtsMapBody() { + methodBodyLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/MethodBodyWIth3StatementsMapBody.java|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + methodHeader = parse(#MethodHeader, "void updateSnapshots(Collection\ snapshots)"); + set[MethodVar] methodVars = findLocalVariables(methodHeader, methodBody); + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/ForWith3StatementsMapBody.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "snapshot"); + Expression collectionId = parse(#Expression, "snapshots"); + + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + + return "" == "snapshots.stream().map(snapshot -\> {\nFolderSnapshot previous = this.folders.get(snapshot.getFolder());\nupdated.put(snapshot.getFolder(), snapshot);\nChangedFiles changedFiles = previous.getChangedFiles(snapshot,\r\n this.triggerFilter);\nreturn changedFiles;\n}).filter(changedFiles -\> !changedFiles.getFiles().isEmpty()).forEach(changedFiles -\> {\r\n changeSet.add(changedFiles);\r\n });"; +} + +public test bool shouldThrowExceptionWhenALoopWithOnlyOneReferenceToOutsideNonEffectiveFinalVarIsNotAReducer() { + methodHeader = assignmentInsideForMethodHeader(); + methodBody = assignmentInsideForMethodBody(); + methodVars = findLocalVariables(methodHeader, methodBody); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "entry"); + Expression collectionId = parse(#Expression, "dir"); + EnhancedForStatement forStmt = parse(#EnhancedForStatement, "for (Path entry : dir) {\n exceptions = concat(exceptions, deleteRecursivelyInsecure(entry));\n }"); + + try { + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + } catch: + return true; + + return false; +} + +public test bool shouldThrowExceptionWhenALoopWithOnlyOneReferenceToOutsideNonEffectiveFinalVarIsNotAReducer2() { + methodHeader = parse(#MethodHeader, "\ ImmutableSortedMultiset\ copyOfSortedEntries(Comparator\ comparator, Collection\\> entries)"); + methodBodyLoc = |project://rascal-Java8/testes/forloop/localVariables/MethodBodyWithTwoReferencesToOutsideNonEffectiveVars|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + methodVars = findLocalVariables(methodHeader, methodBody); + forStmt = parse(#EnhancedForStatement, "for (Entry\ entry : entries) {\n elementsBuilder.add(entry.getElement());\n cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount();\n i++;\n }"); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "entry"); + Expression collectionId = parse(#Expression, "entries"); + + try { + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + } catch: + return true; + + return false; +} + +public test bool shouldWorkWithThrowStatementInsideAnIfThatIsNotTheLastStatement() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] loopWithThrowStatement = loopWithThrowStatement(); + methodBodyLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyPostDecrementedVar|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "key"); + Expression collectionId = parse(#Expression, "keysToLoad"); + + refactoredStatement = buildRefactoredEnhancedFor(loopWithThrowStatement.vars, loopWithThrowStatement.loop, methodBody, iteratedVarName, collectionId); + + return "" == "keysToLoad.forEach(key -\> {\nV value = newEntries.get(key);\nif (value == null) {\n throw new InvalidCacheLoadException(\"loadAll failed to return a value for \" + key);\n }\nresult.put(key, value);\n});"; +} + +public test bool loopWithIfWithTwoStatementsInsideBlockShouldRefactorInnerIfAsMap() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] loop = loopWithIfWithTwoStatementsInsideBlock(); + methodBodyLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/MethodBodyIfWithTwoStmtsInsideAndStmtAfterBlock.java|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "endpoint"); + Expression collectionId = parse(#Expression, "endpoints"); + + refactoredStatement = buildRefactoredEnhancedFor(loop.vars, loop.loop, methodBody, iteratedVarName, collectionId); + + return "" == "endpoints.stream().filter(endpoint -\> isIncluded(endpoint)).map(path -\> endpointHandlerMapping.getPath(endpoint.getPath())).map(path -\> {\npaths.add(path);\nreturn path;\n}).map(path -\> {\nif (!path.equals(\"\")) {\r\n\t\t\t\t\t\tpaths.add(path + \"/**\");\r\n\t\t\t\t\t\t// Add Spring MVC-generated additional paths\r\n\t\t\t\t\t\tpaths.add(path + \".*\");\r\n\t\t\t\t\t}\nreturn path;\n}).forEach(path -\> {\npaths.add(path + \"/\");\n});"; +} + +public test bool anotherIfThatIsMap() { + methodHeader = parse(#MethodHeader, "void afterPropertiesSet() throws Exception"); + methodBodyLoc = |project://rascal-Java8/testes/forloop/ForLoopToFunctional/MethodBodyIfAsNotAFilter|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + methodVars = findLocalVariables(methodHeader, methodBody); + forStmt = parse(#EnhancedForStatement, "for (Endpoint\ endpoint : delegates) {if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) {EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);String path = determinePath(endpoint,this.applicationContext.getEnvironment());if (path != null) {adapter.setPath(path);}this.endpoints.add(adapter);}}"); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "endpoint"); + Expression collectionId = parse(#Expression, "delegates"); + + refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); + + return "" == "delegates.stream().filter(endpoint -\> isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()).map(endpoint -\> {\nEndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint);\nString path = determinePath(endpoint,this.applicationContext.getEnvironment());\nif (path != null) {adapter.setPath(path);}\nreturn adapter;\n}).forEach(adapter -\> {\nthis.endpoints.add(adapter);\n});"; +} + +public test bool shouldRefactorLoopIteratingOnThisField() { + methodHeader = parse(#MethodHeader, "WebServer getWebServer(ServletContextInitializer... initializers)"); + methodBodyLoc = |project://rascal-Java8/testes/forloop/classFields/MethodBodyIteratingOnThisField|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + methodVars = findLocalVariables(methodHeader, methodBody); + fileForLoc = |project://rascal-Java8/testes/forloop/classFields/ForIteratingOnThisField|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "additionalConnector"); + Expression collectionId = parse(#Expression, "this.additionalTomcatConnectors"); + + classFields = findClassFields(parse(#CompilationUnit, readFile(|project://rascal-Java8/testes/forloop/classFields/TomcatServletWebServerFactory.java|))); + vars = methodVars + classFields; + + refactoredStatement = buildRefactoredEnhancedFor(vars, forStmt, methodBody, iteratedVarName, collectionId); + + return "" == "this.additionalTomcatConnectors.forEach(additionalConnector -\> {\ntomcat.getService().addConnector(additionalConnector);\n});"; +} + +public test bool shouldRefactorLoopIteratingOnThisField2() { + methodHeader = parse(#MethodHeader, "void configureEngine(Engine engine)"); + methodBodyLoc = |project://rascal-Java8/testes/forloop/classFields/MethodBodyIteratingOnThisField2|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + methodVars = findLocalVariables(methodHeader, methodBody); + fileForLoc = |project://rascal-Java8/testes/forloop/classFields/ForIteratingOnThisField2|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "valve"); + Expression collectionId = parse(#Expression, "this.engineValves"); + + classFields = findClassFields(parse(#CompilationUnit, readFile(|project://rascal-Java8/testes/forloop/classFields/TomcatServletWebServerFactory.java|))); + vars = methodVars + classFields; + + refactoredStatement = buildRefactoredEnhancedFor(vars, forStmt, methodBody, iteratedVarName, collectionId); + + return "" == "this.engineValves.forEach(valve -\> {\nengine.getPipeline().addValve(valve);\n});"; +} + +//public test bool shouldRefactorToReduceWithPostIncrement() { +// throw "Not yet implemented"; +// +// methodHeader = parse(#MethodHeader, "\ ImmutableSortedMultiset\ copyOfSortedEntries(Comparator\ comparator, Collection\\> entries)"); +// methodBodyLoc = |project://rascal-Java8/testes/forloop/localVariables/MethodBodyReduceWithPostIncrement|; +// methodBody = parse(#MethodBody, readFile(methodBodyLoc)); +// methodVars = findLocalVariables(methodHeader, methodBody); +// forStmt = parse(#EnhancedForStatement, "for (Entry\ entry : entries) {\n elementsBuilder.add(entry.getElement());\n // cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount();\n i++;\n }"); +// VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "entry"); +// Expression collectionId = parse(#Expression, "entries"); +// +// try { +// refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); +// } catch: +// return true; +// +// return false; +//} + +// TODO nested loops needed to be changed in ProspectiveOperation +//public test bool nestedLoops() { +// fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/NestedLoops.java|; +// methodBody = parse(#MethodBody, readFile(fileLoc)); +// methodHeader = parse(#MethodHeader, "void testComplexBuilder()"); +// set[MethodVar] methodVars = findLocalVariables(methodHeader, methodBody); +// fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2For.java|; +// EnhancedForStatement forStmt = parse(#EnhancedForStatement, "for (Integer red : colorElem) {\n for (Integer green : colorElem) {\n for (Integer blue : colorElem) {\n webSafeColorsBuilder.add((red \<\< 16) + (green \<\< 8) + blue);\n }\n }\n }"); +// VariableDeclaratorId iteratedVarName = parse(#VariableDeclaratorId, "red"); +// Expression collectionId = parse(#Expression, "colorElem"); +// +// refactoredStatement = buildRefactoredEnhancedFor(methodVars, forStmt, methodBody, iteratedVarName, collectionId); +// +// return false; +//} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/LocalVariablesFinderTest.rsc b/src/lang/java/refactoring/forloop/test/LocalVariablesFinderTest.rsc new file mode 100644 index 0000000..a9b8fe5 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/LocalVariablesFinderTest.rsc @@ -0,0 +1,286 @@ +module lang::java::refactoring::forloop::\test::LocalVariablesFinderTest + +import IO; +import Set; +import lang::java::refactoring::forloop::\test::resources::LocalVariablesFinderTestResources; +import lang::java::refactoring::forloop::LocalVariablesFinder; +import lang::java::refactoring::forloop::MethodVar; + +public test bool shouldHaveTheEnhancedDeclaredVarAsFinal() { + methodBody = enhancedForLoopFinalVarDecl(); + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + finalsNames = retrieveFinalsNames(vars); + + return "listenableFinal" in finalsNames; +} + +public test bool shouldHaveAllFinalVarsInTheEnhancedDeclaredVarAsFinal() { + methodBody = enhancedForLoopFinalVarDecl(); + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + finalsNames = retrieveFinalsNames(vars); + + return "index" in finalsNames && "listenableFinal" in finalsNames && + size(finalsNames) == 2; +} + +public test bool shouldHaveTheNonFinalVarsInEnhancedDeclaredVarAsFinal() { + methodBody = enhancedForLoopFinalVarDecl(); + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + nonFinalsNames = retrieveNonFinalsNames(vars); + + return "i" in nonFinalsNames && "listenableNonFinal" in nonFinalsNames && + size(nonFinalsNames) == 2; +} + +public test bool shouldHaveAllFinalVarsInEnhancedWithException() { + methodBody = enhancedForLoopWithException(); + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + finalsNames = retrieveFinalsNames(vars); + + return "map" in finalsNames && "entrySet" in finalsNames && + "unmappedKey" in finalsNames && "unmappedValue" in finalsNames && + size(finalsNames) == 4; +} + +public test bool shouldHaveAllNonFinalVarsIncludingExceptionInEnhancedWithException() { + methodBody = enhancedForLoopWithException(); + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + nonFinalsNames = retrieveNonFinalsNames(vars); + + return "e" in nonFinalsNames && "entry" in nonFinalsNames && + size(nonFinalsNames) == 2; +} + +public test bool intVarShouldHaveItsCorrectType() { + methodBody = enhancedForLoopFinalVarDecl(); + + vars = findLocalVariables(emptyMethodHeader(), methodBody); + varI = findByName(vars, "i"); + + return varI.varType == "int"; +} + +public test bool encouragedDeclaredArrayVarsShouldBeArrays() { + methodBody = arrayVariables(); + + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + for(methodVar <- getEncouragedArrays(vars)) { + if(!isTypePlainArray(methodVar)) return false; + } + return true; +} + +public test bool discouragedDeclaredArrayVarsShouldBeArrays() { + methodBody = arrayVariables(); + + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + for(methodVar <- getDiscouragedArrays(vars)) { + if(!isTypePlainArray(methodVar)) return false; + } + return true; +} + +public test bool nonFinalArraysShouldBeNonFinal() { + methodBody = arrayVariables(); + + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + for(methodVar <- getAllNonFinalArrays(vars)) { + if(methodVar.isFinal) return false; + } + return true; +} + +public test bool finalArraysShouldBeFinal() { + methodBody = arrayVariables(); + + vars = findLocalVariables(emptyMethodHeader(), methodBody); + + for(methodVar <- getAllFinalArrays(vars)) { + if(!methodVar.isFinal) return false; + } + return true; +} + +public test bool shouldReturnNonFinalSingleParameter() { + methodHeader = nonFinalSingleParameterMethodHeader(); + methodBody = emptyMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + nonLocals = retrieveParameters(vars); + var = findByName(vars, "param"); + + return size(nonLocals) == 1 && !var.isFinal && var.isParameter; +} + +public test bool shouldReturnFinalSingleParameter() { + methodHeader = finalSingleParameterMethodHeader(); + methodBody = emptyMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + nonLocals = retrieveParameters(vars); + var = findByName(vars, "finalParam"); + + return size(nonLocals) == 1 && var.isFinal && var.isParameter; +} + +public test bool shouldReturnCorrectParamsWithLastOneFinal() { + methodHeader = multipleParametersLastFinalMethodHeader(); + methodBody = emptyMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + nonLocals = retrieveParameters(vars); + param = findByName(vars, "param"); + strParam = findByName(vars, "str"); + finalLastParam = findByName(vars, "finalLastParam"); + + return size(nonLocals) == 3 && + !param.isFinal && param.isParameter && + !strParam.isFinal && strParam.isParameter && + finalLastParam.isFinal && finalLastParam.isParameter; +} + +public test bool shouldReturnCorrectParamsWithLastOneNonFinal() { + methodHeader = multipleParametersLastNonFinalMethodHeader(); + methodBody = emptyMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + nonLocals = retrieveParameters(vars); + param = findByName(vars, "param"); + strParam = findByName(vars, "str"); + nonFinalLastParam = findByName(vars, "nonFinalLastParam"); + + return size(nonLocals) == 3 && + !param.isFinal && param.isParameter && + !strParam.isFinal && strParam.isParameter && + !nonFinalLastParam.isFinal && nonFinalLastParam.isParameter; +} + +public test bool shouldReturnCorrectVarsDeclaredWithinLoop() { + methodHeader = varsWithinTheLoopMethodHeader(); + methodBody = varsWithinTheLoopMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + varsWithinLoop = retrieveDeclaredWithinLoop(vars); + withinLoopNames = retrieveDeclaredWithinLoopNames(vars); + + return size(varsWithinLoop) == 3 && + "insideDecl" in withinLoopNames && + "insideBody" in withinLoopNames && + "insideBodyStr" in withinLoopNames; +} + +public test bool shouldReturnCorrectVarsNotDeclaredWithinLoop() { + methodHeader = varsWithinTheLoopMethodHeader(); + methodBody = varsWithinTheLoopMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + varsNotWithinLoop = retrieveNotDeclaredWithinLoop(vars); + notWithinLoopNames = retrieveNotDeclaredWithinLoopNames(vars); + + return size(varsNotWithinLoop) == 5 && + "notWithinLoop" in notWithinLoopNames && + "notWithinLoopAgain" in notWithinLoopNames && + "localVarNotWithinLoop" in notWithinLoopNames && + "localVarNotWithinLoopAgain" in notWithinLoopNames && + "notWithinLoopAfterLoop" in notWithinLoopNames; +} + +public test bool shouldIdentifyNonEffectiveFinalVar() { + methodHeader = nonEffectiveFinalUsedInEnhancedForMethodHeader(); + methodBody = nonEffectiveFinalUsedInEnhancedForMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + nonEffectiveFinalVar = findByName(vars, "prefix"); + + return !isEffectiveFinal(nonEffectiveFinalVar); +} + +public test bool shouldParseCorrectTypeFromIterableParameter() { + methodHeader = iterableParameterMethodHeader(); + methodBody = iterableParameterMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + iterableParam = findByName(vars, "types"); + + return iterableParam.varType == "Iterable\"; +} + +public test bool shouldIdentifyAnonnymousInnerClassMethodParams() { + methodHeader = methodWithAnonnymousInnerClassMethodHeader(); + methodBody = methodWithAnonnymousInnerClassMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + iterableParam = findByName(vars, "types"); + + return iterableParam.varType == "Iterable\"; +} + +public test bool postIncrementedVariableInsideLoopShouldNotBeEffectiveFinal() { + methodHeader = postIncrementedVarMethodHeader(); + methodBody = postIncrementedVarMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + i = findByName(vars, "i"); + + return !i.isEffectiveFinal && !isEffectiveFinal(i); +} + +public test bool postIncrementedVariable2InsideLoopShouldNotBeEffectiveFinal() { + methodHeader = postIncrementedVar2MethodHeader(); + methodBody = postIncrementedVar2MethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + i = findByName(vars, "i"); + + return !i.isEffectiveFinal && !isEffectiveFinal(i); +} + +public test bool postIncrementedVariable3InsideLoopShouldNotBeEffectiveFinal() { + methodHeader = postIncrementedVar3MethodHeader(); + methodBody = postIncrementedVar3MethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + i = findByName(vars, "i"); + + return !i.isEffectiveFinal && !isEffectiveFinal(i); +} + +public test bool postDecrementedVariableInsideLoopShouldNotBeEffectiveFinal() { + methodHeader = postDecrementedVarMethodHeader(); + methodBody = postDecrementedVarMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + misses = findByName(vars, "misses"); + + return !misses.isEffectiveFinal && !isEffectiveFinal(misses); +} + +public test bool variableAssignedInsideLoopShouldNotBeEffectiveFinal() { + methodHeader = assignmentInsideForMethodHeader(); + methodBody = assignmentInsideForMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + exceptions = findByName(vars, "exceptions"); + + return !exceptions.isEffectiveFinal && !isEffectiveFinal(exceptions); +} + +public test bool arrayAssignmentShouldStillKeepVarAsEffectiveFinal() { + methodHeader = twoNonEffectiveFinalVarsInsideLoopMethodHeader(); + methodBody = twoNonEffectiveFinalVarsInsideLoopMethodBody(); + + vars = findLocalVariables(methodHeader, methodBody); + + i = findByName(vars, "i"); + cumulativeCounts = findByName(vars, "cumulativeCounts"); + + return !isEffectiveFinal(i) && isEffectiveFinal(cumulativeCounts); +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/ProspectiveOperationTest.rsc b/src/lang/java/refactoring/forloop/test/ProspectiveOperationTest.rsc new file mode 100644 index 0000000..347a02b --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/ProspectiveOperationTest.rsc @@ -0,0 +1,188 @@ +module lang::java::refactoring::forloop::\test::ProspectiveOperationTest + +import lang::java::\syntax::Java18; +import IO; +import List; +import lang::java::refactoring::forloop::ProspectiveOperation; +import lang::java::refactoring::forloop::\test::resources::ProspectiveOperationTestResources; +import lang::java::refactoring::forloop::OperationType; +import lang::java::refactoring::forloop::MethodVar; + +public test bool shouldReturnAForEachOnSimpleShortExample() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] simpleShort = simpleShort(); + + prospectiveOperations = retrieveProspectiveOperations(simpleShort.vars, simpleShort.loop); + + return size(prospectiveOperations) == 1 && + prospectiveOperations[0].stmt == "writer.write(thing);" && + prospectiveOperations[0].operation == FOR_EACH; +} + +public test bool shouldHandleReduce() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] filterMapReduce = filterMapReduce(); + + prospectiveOperations = retrieveProspectiveOperations(filterMapReduce.vars, filterMapReduce.loop); + + return size(prospectiveOperations) == 2 && + prospectiveOperations[0].stmt == "rule.hasErrors()" && + prospectiveOperations[0].operation == FILTER && + prospectiveOperations[1].stmt == "count += rule.getErrors().size();" && + prospectiveOperations[1].operation == REDUCE; +} + +public test bool shouldHandleAnyMatch() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] continueAndReturn = continueAndReturn(); + + prospectiveOperations = retrieveProspectiveOperations(continueAndReturn.vars, continueAndReturn.loop); + + return prospectiveOperations[1].stmt == "e.getGrammarName().equals(grammarName)" && + prospectiveOperations[1].operation == ANY_MATCH; +} + +public test bool shouldSeparateAndChooseCorrectOperationsOnMultipleStatements() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] filterAndMergedForEach = filterAndMergedForEach(); + + prospectiveOperations = retrieveProspectiveOperations(filterAndMergedForEach.vars, filterAndMergedForEach.loop); + + return size(prospectiveOperations) == 4 && + prospectiveOperations[0].stmt == "isValid(entry)" && + prospectiveOperations[0].operation == FILTER && + prospectiveOperations[1].stmt == "ClassLoader cl = entry.getKey();" && + prospectiveOperations[1].operation == MAP && + prospectiveOperations[2].stmt == "!((WebappClassLoader)cl).isStart()" && + prospectiveOperations[2].operation == FILTER && + prospectiveOperations[3].stmt == "result.add(entry.getValue());" && + prospectiveOperations[3].operation == FOR_EACH; +} + +public test bool shouldSeparateAndChooseCorrectOperationsOnMultipleMapsEndingWithAReduce() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] multipleMapsAndEndingReducer = multipleMapsAndEndingReducer(); + + prospectiveOperations = retrieveProspectiveOperations(multipleMapsAndEndingReducer.vars, multipleMapsAndEndingReducer.loop); + + return size(prospectiveOperations) == 5 && + prospectiveOperations[0].stmt == "assertTrue(map.containsKey(entry.getKey()));" && + prospectiveOperations[0].operation == MAP && + prospectiveOperations[1].stmt == "assertTrue(map.containsValue(entry.getValue()));" && + prospectiveOperations[1].operation == MAP && + prospectiveOperations[2].stmt == "int expectedHash =\r\n (entry.getKey() == null ? 0 : entry.getKey().hashCode())\r\n ^ (entry.getValue() == null ? 0 : entry.getValue().hashCode());" && + prospectiveOperations[2].operation == MAP && + prospectiveOperations[3].stmt == "assertEquals(expectedHash, entry.hashCode());" && + prospectiveOperations[3].operation == MAP && + prospectiveOperations[4].stmt == "expectedEntrySetHash += expectedHash;" && + prospectiveOperations[4].operation == REDUCE; +} + +public test bool shouldThrowExceptionWhenInnerLoopIsFound() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] innerLoop = innerLoop1(); + + try { + prospectiveOperations = retrieveProspectiveOperations(innerLoop.vars, innerLoop.loop); + } catch: + return true; + + return false; +} + +public test bool shouldThrowExceptionWhenAnotherInnerLoopIsFound() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] innerLoop = innerLoop2(); + + try { + prospectiveOperations = retrieveProspectiveOperations(innerLoop.vars, innerLoop.loop); + } catch: + return true; + + return false; +} + +public test bool shouldThrowExceptionWhenLoopWithInnerWhileIsFound() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] loopWithInnerWhile = loopWithInnerWhile(); + + try { + prospectiveOperations = retrieveProspectiveOperations(loopWithInnerWhile.vars, loopWithInnerWhile.loop); + } catch: + return true; + + return false; +} + +public test bool ifAsNotTheLastStatementShouldBeAMap() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] loopWithThrowStatement = loopWithThrowStatement(); + + prospectiveOperations = retrieveProspectiveOperations(loopWithThrowStatement.vars, loopWithThrowStatement.loop); + + return size(prospectiveOperations) == 3 && + prospectiveOperations[0].stmt == "V value = newEntries.get(key);" && + prospectiveOperations[0].operation == MAP && + prospectiveOperations[1].stmt == "if (value == null) {\n throw new InvalidCacheLoadException(\"loadAll failed to return a value for \" + key);\n }" && + prospectiveOperations[1].operation == MAP && + prospectiveOperations[2].stmt == "result.put(key, value);" && + prospectiveOperations[2].operation == FOR_EACH; +} + +// This is actually a really nice example. +// The first if is a filter because it is the last statement from the outer block +// The inner if is not the last statement within the first if block, so it's a map +public test bool innerIfAsNotTheLastStatementShouldBeAMap() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] loop = loopWithIfWithTwoStatementsInsideBlock(); + + prospectiveOperations = retrieveProspectiveOperations(loop.vars, loop.loop); + + return size(prospectiveOperations) == 5 && + prospectiveOperations[0].stmt == "isIncluded(endpoint)" && + prospectiveOperations[0].operation == FILTER && + prospectiveOperations[1].stmt == "String path = endpointHandlerMapping.getPath(endpoint.getPath());" && + prospectiveOperations[1].operation == MAP && + prospectiveOperations[2].stmt == "paths.add(path);" && + prospectiveOperations[2].operation == MAP && + prospectiveOperations[3].stmt == "if (!path.equals(\"\")) {\r\n\t\t\t\t\t\tpaths.add(path + \"/**\");\r\n\t\t\t\t\t\t// Add Spring MVC-generated additional paths\r\n\t\t\t\t\t\tpaths.add(path + \".*\");\r\n\t\t\t\t\t}" && + prospectiveOperations[3].operation == MAP && + prospectiveOperations[4].stmt == "paths.add(path + \"/\");" && + prospectiveOperations[4].operation == FOR_EACH; +} + +public test bool shouldThrowExceptionOnLoopWithInnerLoop() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] loop = outerLoopWithInnerLoop(); + + try { + prospectiveOperations = retrieveProspectiveOperations(loop.vars, loop.loop); + return false; + } catch: + return true; + +} + +//public test bool shouldIdentifyPostIncrementAsReduce() { +// throw "Not yet implemented"; +// +// tuple [set[MethodVar] vars, EnhancedForStatement loop] loopReduceWithPostIncrement = loopReduceWithPostIncrement(); +// +// prospectiveOperations = retrieveProspectiveOperations(loopReduceWithPostIncrement.vars, loopReduceWithPostIncrement.loop); +// +// println(prospectiveOperations); +// +// return false; +//} + +//public test bool shouldHandleAnyMatchAndIfWithContinue() { +// tuple [set[MethodVar] vars, EnhancedForStatement loop] continueAndReturn = continueAndReturn(); +// +// prospectiveOperations = retrieveProspectiveOperations(continueAndReturn.vars, continueAndReturn.loop); +// println(prospectiveOperations); +// +// return size(prospectiveOperations) == 2 && +// prospectiveOperations[0].stmt == "e.getGrammarName() != null" && +// prospectiveOperations[0].operation == FILTER && +// prospectiveOperations[1].stmt == "e.getGrammarName().equals(grammarName)" && +// prospectiveOperations[1].operation == ANY_MATCH; +//} +// +//public test bool shouldHandleIfWithContinue() { +// tuple [set[MethodVar] vars, EnhancedForStatement loop] continueAndReturn = continueAndReturn(); +// +// prospectiveOperations = retrieveProspectiveOperations(continueAndReturn.vars, continueAndReturn.loop); +// println(prospectiveOperations); +// +// return prospectiveOperations[0].stmt == "e.getGrammarName() != null" && +// prospectiveOperations[0].operation == FILTER; +//} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/UsedVariablesTest.rsc b/src/lang/java/refactoring/forloop/test/UsedVariablesTest.rsc new file mode 100644 index 0000000..0574dc7 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/UsedVariablesTest.rsc @@ -0,0 +1,85 @@ +module lang::java::refactoring::forloop::\test::UsedVariablesTest + +import Set; +import IO; +import lang::java::refactoring::forloop::UsedVariables; +import lang::java::refactoring::forloop::ProspectiveOperation; +import lang::java::refactoring::forloop::OperationType; +import lang::java::refactoring::forloop::MethodVar; + +public test bool methodInvocationWithArg() { + prOp = prospectiveOperation("writer.write(thing);", FOR_EACH); + + usedVars = retrieveUsedVariables(prOp); + + return size(usedVars) == 2 && + "writer" in usedVars && + "thing" in usedVars; +} + +public test bool simpleMethodInvocationWithoutEndingSemiCollon() { + prOp = prospectiveOperation("rule.hasErrors()", FILTER); + + usedVars = retrieveUsedVariables(prOp); + + return size(usedVars) == 1 && + "rule" in usedVars; +} + +public test bool variableAssignmentWithInitializer() { + prOp = prospectiveOperation("count = rule.getErrors().size();", MAP); + + usedVars = retrieveUsedVariables(prOp); + + return size(usedVars) == 2 && + "count" in usedVars && + "rule" in usedVars; +} + +public test bool localVariableDeclarationShouldNotReturnItself() { + prOp = prospectiveOperation("ClassLoader cl = entry.getKey(argUsed);", MAP); + + usedVars = retrieveUsedVariables(prOp); + + return "cl" notin usedVars; +} + +public test bool localVariableDeclarationShouldReturnVarsUsedInInitializer() { + prOp = prospectiveOperation("ClassLoader cl = entry.getKey(argUsed);", MAP); + + usedVars = retrieveUsedVariables(prOp); + + return size(usedVars) == 2 && + "entry" in usedVars && + "argUsed" in usedVars; +} + +public test bool expressionIsNotAStatement() { + prOp = prospectiveOperation("!((WebappClassLoader)cl).isStart()", FILTER); + + usedVars = retrieveUsedVariables(prOp); + + return size(usedVars) == 1 && + "cl" in usedVars; +} + +public test bool ifThenStatement() { + prOp = prospectiveOperation("if (!((WebappClassLoader)cl).isStart()) result.add(entry.getValue());", FILTER); + + usedVars = retrieveUsedVariables(prOp); + + return size(usedVars) == 3 && + "cl" in usedVars && + "result" in usedVars && + "entry" in usedVars; +} + +public test bool reduce() { + prOp = prospectiveOperation("count += rule.getErrors().size();", REDUCE); + + usedVars = retrieveUsedVariables(prOp); + + return size(usedVars) == 2 && + "count" in usedVars && + "rule" in usedVars; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/resources/LocalVariablesFinderTestResources.rsc b/src/lang/java/refactoring/forloop/test/resources/LocalVariablesFinderTestResources.rsc new file mode 100644 index 0000000..78c73ad --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/resources/LocalVariablesFinderTestResources.rsc @@ -0,0 +1,193 @@ +module lang::java::refactoring::forloop::\test::resources::LocalVariablesFinderTestResources + +import IO; +import lang::java::\syntax::Java18; +import ParseTree; +import lang::java::refactoring::forloop::MethodVar; + +public MethodHeader emptyMethodHeader() { + header = "void method()"; + return parse(#MethodHeader, header); +} + +public MethodHeader nonFinalSingleParameterMethodHeader() { + header = "void method(int param)"; + return parse(#MethodHeader, header); +} + +public MethodHeader finalSingleParameterMethodHeader() { + header = "void method(final int finalParam)"; + return parse(#MethodHeader, header); +} + +public MethodHeader multipleParametersLastFinalMethodHeader() { + header = "void method(int param, String str, final double finalLastParam)"; + return parse(#MethodHeader, header); +} + +public MethodHeader multipleParametersLastNonFinalMethodHeader() { + header = "void method(int param, String str, double nonFinalLastParam)"; + return parse(#MethodHeader, header); +} + +public MethodBody emptyMethodBody() { + body = "{}"; + return parse(#MethodBody, body); +} + +public MethodBody enhancedForLoopFinalVarDecl() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/EnhancedForLoopFinalVarDecl|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodBody enhancedForLoopWithException() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/EnhancedForLoopWithException|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodBody arrayVariables() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/MultiplePlainArrayDeclarations|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public set[MethodVar] getEncouragedArrays(set[MethodVar] vars) { + return getNonFinalEncouragedArrays(vars) + getFinalEncouragedArrays(vars); +} + +public set[MethodVar] getNonFinalEncouragedArrays(set[MethodVar] vars) { + varIntArray = findByName(vars, "intArray"); + varStrArray = findByName(vars, "strArray"); + varObjArray = findByName(vars, "objArray"); + return {varIntArray, varStrArray, varObjArray}; +} + +public set[MethodVar] getFinalEncouragedArrays(set[MethodVar] vars) { + varFinalObjArray = findByName(vars, "finalObjArray"); + varFinalStrArray = findByName(vars, "finalStrArray"); + return {varFinalObjArray, varFinalStrArray}; +} + +public set[MethodVar] getDiscouragedArrays(set[MethodVar] vars) { + return getNonFinalDiscouragedArrays(vars) + getFinalDiscouragedArrays(vars); +} + +public set[MethodVar] getNonFinalDiscouragedArrays(set[MethodVar] vars) { + varObjDiscouraged = findByName(vars, "objDiscouraged"); + varStrDiscouraged = findByName(vars, "strDiscouraged"); + return {varObjDiscouraged, varStrDiscouraged}; +} + +public set[MethodVar] getFinalDiscouragedArrays(set[MethodVar] vars) { + varFinalObjDiscouraged = findByName(vars, "finalObjDiscouraged"); + varFinalIntDiscouraged = findByName(vars, "finalIntDiscouraged"); + return {varFinalObjDiscouraged, varFinalIntDiscouraged}; +} + +public set[MethodVar] getAllNonFinalArrays(set[MethodVar] vars) { + return getNonFinalEncouragedArrays(vars) + getNonFinalDiscouragedArrays(vars); +} + +public set[MethodVar] getAllFinalArrays(set[MethodVar] vars) { + return getFinalEncouragedArrays(vars) + getFinalDiscouragedArrays(vars); +} + +public MethodHeader varsWithinTheLoopMethodHeader() { + header = "void method(int notWithinLoop, String notWithinLoopAgain)"; + return parse(#MethodHeader, header); +} + +public MethodBody varsWithinTheLoopMethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/EnhancedForLoopVarsWithinLoop|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader nonEffectiveFinalUsedInEnhancedForMethodHeader() { + header = "void set(String group, Collection\\> values)"; + return parse(#MethodHeader, header); +} + +public MethodBody nonEffectiveFinalUsedInEnhancedForMethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/NonEffectiveFinalUsedInEnhancedFor|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader iterableParameterMethodHeader() { + header = "ImmutableList\ collectTypes(Iterable\ types)"; + return parse(#MethodHeader, header); +} + +public MethodBody iterableParameterMethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/IterableParameterMethodBody|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader methodWithAnonnymousInnerClassMethodHeader() { + header = "TypeCollector\ classesOnly()"; + return parse(#MethodHeader, header); +} + +public MethodBody methodWithAnonnymousInnerClassMethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyWithAnonnymousInnerClass|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader postIncrementedVarMethodHeader() { + header = "void processCompleted()"; + return parse(#MethodHeader, header); +} + +public MethodBody postIncrementedVarMethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyPostIncrementedVar|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader postIncrementedVar2MethodHeader() { + header = "void init()"; + return parse(#MethodHeader, header); +} + +public MethodBody postIncrementedVar2MethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyPostIncrementedVar2|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader postIncrementedVar3MethodHeader() { + header = "\ ImmutableMap\ indexMap(Collection\ list)"; + return parse(#MethodHeader, header); +} + +public MethodBody postIncrementedVar3MethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyPostIncrementedVar3|; + return parse(#MethodBody, readFile(fileLoc)); +} + + +public MethodHeader postDecrementedVarMethodHeader() { + header = "ImmutableMap\ getAll(Iterable\ keys) throws ExecutionException"; + return parse(#MethodHeader, header); +} + +public MethodBody postDecrementedVarMethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyPostDecrementedVar|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader assignmentInsideForMethodHeader() { + header = "Collection\ deleteDirectoryContentsInsecure(DirectoryStream\ dir)"; + return parse(#MethodHeader, header); +} + +public MethodBody assignmentInsideForMethodBody() { + fileLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyAssignmentInsideLoop|; + return parse(#MethodBody, readFile(fileLoc)); +} + +public MethodHeader twoNonEffectiveFinalVarsInsideLoopMethodHeader() { + header = "\ ImmutableSortedMultiset\ copyOfSortedEntries(Comparator\ comparator, Collection\\> entries)"; + return parse(#MethodHeader, header); +} + +public MethodBody twoNonEffectiveFinalVarsInsideLoopMethodBody() { + methodBodyLoc = |project://rascal-Java8/testes/forloop/localVariables/MethodBodyWithTwoReferencesToOutsideNonEffectiveVars|; + return parse(#MethodBody, readFile(methodBodyLoc)); +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/resources/ProspectiveOperationTestResources.rsc b/src/lang/java/refactoring/forloop/test/resources/ProspectiveOperationTestResources.rsc new file mode 100644 index 0000000..cdb224b --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/resources/ProspectiveOperationTestResources.rsc @@ -0,0 +1,160 @@ +module lang::java::refactoring::forloop::\test::resources::ProspectiveOperationTestResources + +import IO; +import lang::java::\syntax::Java18; +import ParseTree; +import lang::java::refactoring::forloop::MethodVar; +import lang::java::refactoring::forloop::LocalVariablesFinder; +import lang::java::refactoring::forloop::ClassFieldsFinder; + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] simpleShort() { + fileLoc = |project://rascal-Java8//testes/forloop/ProspectiveOperation/SimpleShortEnhancedLoop|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileLoc)); + return <{}, enhancedForLoop>; +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] continueAndReturn() { + fileLoc = |project://rascal-Java8//testes/forloop/ProspectiveOperation/ContinueAndReturnEnhancedLoop|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileLoc)); + return ; +} + +private set[MethodVar] continueAndReturnVars() { + methodHeader = parse(#MethodHeader, "boolean isEngineExisting(String grammarName)"); + methodBody = parse(#MethodBody, "{\n for(GrammarEngine e : importedEngines) { \n if(e.getGrammarName() == null) continue; \n if(e.getGrammarName().equals(grammarName))\n return true; \n } \n return false; \n}" ); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] filterMapReduce() { + fileLoc = |project://rascal-Java8//testes/forloop/ProspectiveOperation/FilterMapReduceEnhancedLoop|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileLoc)); + return ; +} + +private set[MethodVar] filterMapReduceVars() { + methodHeader = parse(#MethodHeader, "int getNumberOfErrors()"); + methodBody = parse(#MethodBody, "{\n int count = 0;\n for (ElementRule rule : getRules()) {\n if(rule.hasErrors())\n count += rule.getErrors().size();\n }\n return count;\n }"); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] filterAndMergedForEach() { + fileLoc = |project://rascal-Java8//testes/forloop/ProspectiveOperation/FilterAndMergedForEach|; + enhancedForLoop = parse(#EnhancedForStatement, readFile(fileLoc)); + return ; +} + +private set[MethodVar] filterAndMergedForEachVars() { + methodHeader = parse(#MethodHeader, "List\ findReloadedContextMemoryLeaks()"); + methodBody = parse(#MethodBody, "{\n List\ result = new ArrayList\();\n for (Map.Entry\ entry :\n childClassLoaders.entrySet())\n if(isValid(entry)) {\n ClassLoader cl = entry.getKey();\n if (!((WebappClassLoader)cl).isStart())\n result.add(entry.getValue());\n }\n return result;\n }"); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] multipleMapsAndEndingReducer() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2For2.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + return ; +} + +private set[MethodVar] multipleMapsAndEndingReducerVars() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/T2.java|; + methodBody = parse(#MethodBody, readFile(fileLoc)); + methodHeader = parse(#MethodHeader, "void assertInvariants(Map\ map)"); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] innerLoop1() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/InnerLoop1.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + return ; +} + +private set[MethodVar] innerLoop1Vars() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop1.java|; + methodBody = parse(#MethodBody, readFile(fileLoc)); + methodHeader = parse(#MethodHeader, "\ Graph\ transitiveClosure(Graph\ graph)"); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] innerLoop2() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/InnerLoop2.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + return ; +} + +private set[MethodVar] innerLoop2Vars() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop2.java|; + methodBody = parse(#MethodBody, readFile(fileLoc)); + methodHeader = parse(#MethodHeader, "ImmutableList\ getAnnotatedMethodsNotCached(Class\ clazz)"); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] loopWithInnerWhile() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/LoopWithInnerWhile.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + return ; +} + +private set[MethodVar] loopWithInnerWhileVars() { + fileLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/MethodBodyLoopWithInnerWhile.java|; + methodBody = parse(#MethodBody, readFile(fileLoc)); + methodHeader = parse(#MethodHeader, "ImmutableList\ getAnnotatedMethodsNotCached(Class\ clazz)"); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] loopReduceWithPostIncrement() { + forStmt = parse(#EnhancedForStatement, "for (Entry\ entry : entries) {\n elementsBuilder.add(entry.getElement());\n // cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount();\n i++;\n }"); + return ; +} + +private set[MethodVar] loopReduceWithPostIncrementVars() { + methodHeader = parse(#MethodHeader, "\ ImmutableSortedMultiset\ copyOfSortedEntries(Comparator\ comparator, Collection\\> entries)"); + methodBodyLoc = |project://rascal-Java8/testes/forloop/localVariables/MethodBodyReduceWithPostIncrement|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] loopWithThrowStatement() { + forStmt = parse(#EnhancedForStatement, "for (K key : keysToLoad) {\n V value = newEntries.get(key);\n if (value == null) {\n throw new InvalidCacheLoadException(\"loadAll failed to return a value for \" + key);\n }\n result.put(key, value);\n }"); + return ; +} + +private set[MethodVar] loopWithThrowStatementVars() { + methodHeader = parse(#MethodHeader, "ImmutableMap\ getAll(Iterable\ keys) throws ExecutionException"); + methodBodyLoc = |project://rascal-Java8//testes/forloop/localVariables/MethodBodyPostDecrementedVar|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] loopWithIfWithTwoStatementsInsideBlock() { + fileForLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/ForIfWithTwoStmtsInsideAndStmtAfterBlock.java|; + EnhancedForStatement forStmt = parse(#EnhancedForStatement, readFile(fileForLoc)); + return ; +} + +private set[MethodVar] loopWithIfWithTwoStatementsInsideBlockVars() { + methodHeader = parse(#MethodHeader, "String[] getPaths(EndpointHandlerMapping endpointHandlerMapping)"); + methodBodyLoc = |project://rascal-Java8//testes/forloop/ForLoopToFunctional/MethodBodyIfWithTwoStmtsInsideAndStmtAfterBlock.java|; + methodBody = parse(#MethodBody, readFile(methodBodyLoc)); + return findLocalVariables(methodHeader, methodBody); +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] outerLoopWithInnerLoop() { + methodHeader = parse(#MethodHeader, "void scanPackage(ClassPathScanningCandidateComponentProvider componentProvider, String packageToScan)"); + loopLoc = |project://rascal-Java8//testes/forloop/Refactorer/LoopRefactorableInnerLoopButNotOuter|; + loopStr = readFile(loopLoc); + forStmt = parse(#EnhancedForStatement, loopStr); + methodBody = parse(#MethodBody, "{" + loopStr + "}"); + + localVars = findLocalVariables(methodHeader, methodBody); + classLoc = |project://rascal-Java8//testes/forloop/Refactorer/ServletComponentRegisteringPostProcessor.java|; + classFields = findClassFields(parse(#CompilationUnit, classLoc)); + availableVars = localVars + classFields; + + return ; +} + +public tuple [set[MethodVar] vars, EnhancedForStatement loop] innerLoopInsideOuter() { + tuple [set[MethodVar] vars, EnhancedForStatement loop] loop = outerLoopWithInnerLoop(); + loop.loop = "for (ServletComponentHandler handler : HANDLERS) {\n handler.handle(((ScannedGenericBeanDefinition) candidate),\n (BeanDefinitionRegistry) this.applicationContext);\n }"; + return loop; +} \ No newline at end of file diff --git a/src/lang/java/refactoring/forloop/test/resources/RefactorerTestResources.rsc b/src/lang/java/refactoring/forloop/test/resources/RefactorerTestResources.rsc new file mode 100644 index 0000000..eb288d8 --- /dev/null +++ b/src/lang/java/refactoring/forloop/test/resources/RefactorerTestResources.rsc @@ -0,0 +1,18 @@ +module lang::java::refactoring::forloop::\test::resources::RefactorerTestResources + +import IO; +import ParseTree; +import lang::java::\syntax::Java18; + +public data RefactorableFor = refactorableFor(CompilationUnit unit, MethodHeader header, MethodBody body, MethodBody refactored); + +public RefactorableFor innerLoopButNotOuterLoop() { + classLoc = |project://rascal-Java8//testes/forloop/Refactorer/ServletComponentRegisteringPostProcessor.java|; + unit = parse(#CompilationUnit, readFile(classLoc)); + header = parse(#MethodHeader, "void scanPackage(\r\n\t\t\tClassPathScanningCandidateComponentProvider componentProvider,\r\n\t\t\tString packageToScan)"); + body = parse(#MethodBody, "{\r\n\t\tfor (BeanDefinition candidate : componentProvider\r\n\t\t\t\t.findCandidateComponents(packageToScan)) {\r\n\t\t\tif (candidate instanceof ScannedGenericBeanDefinition) {\r\n\t\t\t\tfor (ServletComponentHandler handler : HANDLERS) {\r\n\t\t\t\t\thandler.handle(((ScannedGenericBeanDefinition) candidate),\r\n\t\t\t\t\t\t\t(BeanDefinitionRegistry) this.applicationContext);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}"); + + refactored = parse(#MethodBody, "{\r\n\t\tfor (BeanDefinition candidate : componentProvider\r\n\t\t\t\t.findCandidateComponents(packageToScan)) {\r\n\t\t\tif (candidate instanceof ScannedGenericBeanDefinition) {\r\n\t\t\t\tHANDLERS.forEach(handler -\> {\nhandler.handle(((ScannedGenericBeanDefinition) candidate),\r\n\t\t\t\t\t\t\t(BeanDefinitionRegistry) this.applicationContext);\n});\r\n\t\t\t}\r\n\t\t}\r\n\t}"); + + return refactorableFor(unit, header, body, refactored); +} \ No newline at end of file diff --git a/src/lang/java/syntax/Java18.rsc b/src/lang/java/syntax/Java18.rsc index da187a3..49313c0 100644 --- a/src/lang/java/syntax/Java18.rsc +++ b/src/lang/java/syntax/Java18.rsc @@ -463,7 +463,7 @@ syntax VariableInitializerList = { VariableInitializer "," }+ ; syntax Block = "{" BlockStatements? "}" ; -syntax BlockStatements = BlockStatement+ ; +syntax BlockStatements = BlockStatement BlockStatement* ; syntax BlockStatement = LocalVariableDeclarationStatement | ClassDeclaration diff --git a/testes/exception-hierarchy.zip b/testes/exception-hierarchy.zip new file mode 100644 index 0000000..7c25e56 Binary files /dev/null and b/testes/exception-hierarchy.zip differ diff --git a/testes/forloop/ForLoopToFunctional/EnhancedForOnIterable.java b/testes/forloop/ForLoopToFunctional/EnhancedForOnIterable.java new file mode 100644 index 0000000..923243b --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/EnhancedForOnIterable.java @@ -0,0 +1,15 @@ +@Override + public ImmutableMap getAllPresent(Iterable keys) { + Map result = Maps.newLinkedHashMap(); + for (Object key : keys) { + if (!result.containsKey(key)) { + @SuppressWarnings("unchecked") + K castKey = (K) key; + V value = getIfPresent(key); + if (value != null) { + result.put(castKey, value); + } + } + } + return ImmutableMap.copyOf(result); + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/ForIfWithTwoStmtsInsideAndStmtAfterBlock.java b/testes/forloop/ForLoopToFunctional/ForIfWithTwoStmtsInsideAndStmtAfterBlock.java new file mode 100644 index 0000000..6022143 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/ForIfWithTwoStmtsInsideAndStmtAfterBlock.java @@ -0,0 +1,12 @@ +for (MvcEndpoint endpoint : endpoints) { + if (isIncluded(endpoint)) { + String path = endpointHandlerMapping.getPath(endpoint.getPath()); + paths.add(path); + if (!path.equals("")) { + paths.add(path + "/**"); + // Add Spring MVC-generated additional paths + paths.add(path + ".*"); + } + paths.add(path + "/"); + } + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/ForWith3StatementsMapBody.java b/testes/forloop/ForLoopToFunctional/ForWith3StatementsMapBody.java new file mode 100644 index 0000000..fa1b4e7 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/ForWith3StatementsMapBody.java @@ -0,0 +1,9 @@ +for (FolderSnapshot snapshot : snapshots) { + FolderSnapshot previous = this.folders.get(snapshot.getFolder()); + updated.put(snapshot.getFolder(), snapshot); + ChangedFiles changedFiles = previous.getChangedFiles(snapshot, + this.triggerFilter); + if (!changedFiles.getFiles().isEmpty()) { + changeSet.add(changedFiles); + } + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/ForWithMultiStatementMap.java b/testes/forloop/ForLoopToFunctional/ForWithMultiStatementMap.java new file mode 100644 index 0000000..ec01d61 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/ForWithMultiStatementMap.java @@ -0,0 +1,7 @@ +for (String v : values) { + String key = keysIt.next(); + Metric value = deserialize(key, v, this.zSetOperations.score(key)); + if (value != null) { + result.add(value); + } + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/InnerLoop1.java b/testes/forloop/ForLoopToFunctional/InnerLoop1.java new file mode 100644 index 0000000..ddd4bb8 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/InnerLoop1.java @@ -0,0 +1,12 @@ +for (N node : graph.nodes()) { + if (!visitedNodes.contains(node)) { + Set reachableNodes = reachableNodes(graph, node); + visitedNodes.addAll(reachableNodes); + int pairwiseMatch = 1; // start at 1 to include self-loops + for (N nodeU : reachableNodes) { + for (N nodeV : Iterables.limit(reachableNodes, pairwiseMatch++)) { + transitiveClosure.putEdge(nodeU, nodeV); + } + } + } + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/InnerLoop2.java b/testes/forloop/ForLoopToFunctional/InnerLoop2.java new file mode 100644 index 0000000..4e648e0 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/InnerLoop2.java @@ -0,0 +1,19 @@ +for (Class supertype : supertypes) { + for (Method method : supertype.getDeclaredMethods()) { + if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) { + // TODO(cgdecker): Should check for a generic parameter type and error out + Class[] parameterTypes = method.getParameterTypes(); + checkArgument( + parameterTypes.length == 1, + "Method %s has @Subscribe annotation but has %s parameters." + + "Subscriber methods must have exactly 1 parameter.", + method, + parameterTypes.length); + + MethodIdentifier ident = new MethodIdentifier(method); + if (!identifiers.containsKey(ident)) { + identifiers.put(ident, method); + } + } + } + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/LoopWithInnerWhile.java b/testes/forloop/ForLoopToFunctional/LoopWithInnerWhile.java new file mode 100644 index 0000000..f494d3c --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/LoopWithInnerWhile.java @@ -0,0 +1,16 @@ +for (HttpMessageConverter defaultConverter : defaultConverters) { + Iterator> iterator = processing.iterator(); + while (iterator.hasNext()) { + HttpMessageConverter candidate = iterator.next(); + if (isReplacement(defaultConverter, candidate)) { + combined.add(candidate); + iterator.remove(); + } + } + combined.add(defaultConverter); + if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) { + configurePartConverters( + (AllEncompassingFormHttpMessageConverter) defaultConverter, + converters); + } + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/MethodBodyIfAsNotAFilter b/testes/forloop/ForLoopToFunctional/MethodBodyIfAsNotAFilter new file mode 100644 index 0000000..d292517 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/MethodBodyIfAsNotAFilter @@ -0,0 +1,22 @@ +{ + Collection existing = BeanFactoryUtils + .beansOfTypeIncludingAncestors(this.applicationContext, MvcEndpoint.class) + .values(); + this.endpoints.addAll(existing); + this.customTypes = findEndpointClasses(existing); + @SuppressWarnings("rawtypes") + Collection delegates = BeanFactoryUtils + .beansOfTypeIncludingAncestors(this.applicationContext, Endpoint.class) + .values(); + for (Endpoint endpoint : delegates) { + if (isGenericEndpoint(endpoint.getClass()) && endpoint.isEnabled()) { + EndpointMvcAdapter adapter = new EndpointMvcAdapter(endpoint); + String path = determinePath(endpoint, + this.applicationContext.getEnvironment()); + if (path != null) { + adapter.setPath(path); + } + this.endpoints.add(adapter); + } + } + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/MethodBodyIfWithTwoStmtsInsideAndStmtAfterBlock.java b/testes/forloop/ForLoopToFunctional/MethodBodyIfWithTwoStmtsInsideAndStmtAfterBlock.java new file mode 100644 index 0000000..653303e --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/MethodBodyIfWithTwoStmtsInsideAndStmtAfterBlock.java @@ -0,0 +1,20 @@ +{ + if (endpointHandlerMapping == null) { + return NO_PATHS; + } + Set endpoints = endpointHandlerMapping.getEndpoints(); + Set paths = new LinkedHashSet<>(endpoints.size()); + for (MvcEndpoint endpoint : endpoints) { + if (isIncluded(endpoint)) { + String path = endpointHandlerMapping.getPath(endpoint.getPath()); + paths.add(path); + if (!path.equals("")) { + paths.add(path + "/**"); + // Add Spring MVC-generated additional paths + paths.add(path + ".*"); + } + paths.add(path + "/"); + } + } + return paths.toArray(new String[paths.size()]); + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop1.java b/testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop1.java new file mode 100644 index 0000000..a9dd572 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop1.java @@ -0,0 +1,32 @@ +{ + MutableGraph transitiveClosure = GraphBuilder.from(graph).allowsSelfLoops(true).build(); + // Every node is, at a minimum, reachable from itself. Since the resulting transitive closure + // will have no isolated nodes, we can skip adding nodes explicitly and let putEdge() do it. + + if (graph.isDirected()) { + // Note: works for both directed and undirected graphs, but we only use in the directed case. + for (N node : graph.nodes()) { + for (N reachableNode : reachableNodes(graph, node)) { + transitiveClosure.putEdge(node, reachableNode); + } + } + } else { + // An optimization for the undirected case: for every node B reachable from node A, + // node A and node B have the same reachability set. + Set visitedNodes = new HashSet(); + for (N node : graph.nodes()) { + if (!visitedNodes.contains(node)) { + Set reachableNodes = reachableNodes(graph, node); + visitedNodes.addAll(reachableNodes); + int pairwiseMatch = 1; // start at 1 to include self-loops + for (N nodeU : reachableNodes) { + for (N nodeV : Iterables.limit(reachableNodes, pairwiseMatch++)) { + transitiveClosure.putEdge(nodeU, nodeV); + } + } + } + } + } + + return transitiveClosure; + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop2.java b/testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop2.java new file mode 100644 index 0000000..06915b3 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/MethodBodyInnerLoop2.java @@ -0,0 +1,24 @@ +{ + Set> supertypes = TypeToken.of(clazz).getTypes().rawTypes(); + Map identifiers = Maps.newHashMap(); + for (Class supertype : supertypes) { + for (Method method : supertype.getDeclaredMethods()) { + if (method.isAnnotationPresent(Subscribe.class) && !method.isSynthetic()) { + // TODO(cgdecker): Should check for a generic parameter type and error out + Class[] parameterTypes = method.getParameterTypes(); + checkArgument( + parameterTypes.length == 1, + "Method %s has @Subscribe annotation but has %s parameters." + + "Subscriber methods must have exactly 1 parameter.", + method, + parameterTypes.length); + + MethodIdentifier ident = new MethodIdentifier(method); + if (!identifiers.containsKey(ident)) { + identifiers.put(ident, method); + } + } + } + } + return ImmutableList.copyOf(identifiers.values()); + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/MethodBodyLoopWithInnerWhile.java b/testes/forloop/ForLoopToFunctional/MethodBodyLoopWithInnerWhile.java new file mode 100644 index 0000000..895c4dc --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/MethodBodyLoopWithInnerWhile.java @@ -0,0 +1,22 @@ +{ + List> combined = new ArrayList<>(); + List> processing = new ArrayList<>(converters); + for (HttpMessageConverter defaultConverter : defaultConverters) { + Iterator> iterator = processing.iterator(); + while (iterator.hasNext()) { + HttpMessageConverter candidate = iterator.next(); + if (isReplacement(defaultConverter, candidate)) { + combined.add(candidate); + iterator.remove(); + } + } + combined.add(defaultConverter); + if (defaultConverter instanceof AllEncompassingFormHttpMessageConverter) { + configurePartConverters( + (AllEncompassingFormHttpMessageConverter) defaultConverter, + converters); + } + } + combined.addAll(0, processing); + return combined; + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/MethodBodyWIth3StatementsMapBody.java b/testes/forloop/ForLoopToFunctional/MethodBodyWIth3StatementsMapBody.java new file mode 100644 index 0000000..dc7d677 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/MethodBodyWIth3StatementsMapBody.java @@ -0,0 +1,17 @@ +{ + Map updated = new LinkedHashMap<>(); + Set changeSet = new LinkedHashSet<>(); + for (FolderSnapshot snapshot : snapshots) { + FolderSnapshot previous = this.folders.get(snapshot.getFolder()); + updated.put(snapshot.getFolder(), snapshot); + ChangedFiles changedFiles = previous.getChangedFiles(snapshot, + this.triggerFilter); + if (!changedFiles.getFiles().isEmpty()) { + changeSet.add(changedFiles); + } + } + if (!changeSet.isEmpty()) { + fireListeners(Collections.unmodifiableSet(changeSet)); + } + this.folders = updated; + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/MethodBodyWithMultiStatementMap.java b/testes/forloop/ForLoopToFunctional/MethodBodyWithMultiStatementMap.java new file mode 100644 index 0000000..3bc474c --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/MethodBodyWithMultiStatementMap.java @@ -0,0 +1,18 @@ +{ + + // This set is sorted + Set keys = this.zSetOperations.range(0, -1); + Iterator keysIt = keys.iterator(); + + List> result = new ArrayList<>(keys.size()); + List values = this.redisOperations.opsForValue().multiGet(keys); + for (String v : values) { + String key = keysIt.next(); + Metric value = deserialize(key, v, this.zSetOperations.score(key)); + if (value != null) { + result.add(value); + } + } + return result; + + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/NestedLoops.java b/testes/forloop/ForLoopToFunctional/NestedLoops.java new file mode 100644 index 0000000..7c6098e --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/NestedLoops.java @@ -0,0 +1,33 @@ +{ + List colorElem = asList(0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF); + // javac won't compile this without "this." + ImmutableSet.Builder webSafeColorsBuilder + = this.builder(); + for (Integer red : colorElem) { + for (Integer green : colorElem) { + for (Integer blue : colorElem) { + webSafeColorsBuilder.add((red << 16) + (green << 8) + blue); + } + } + } + ImmutableSet webSafeColors = webSafeColorsBuilder.build(); + assertEquals(216, webSafeColors.size()); + Integer[] webSafeColorArray = + webSafeColors.toArray(new Integer[webSafeColors.size()]); + assertEquals(0x000000, (int) webSafeColorArray[0]); + assertEquals(0x000033, (int) webSafeColorArray[1]); + assertEquals(0x000066, (int) webSafeColorArray[2]); + assertEquals(0x003300, (int) webSafeColorArray[6]); + assertEquals(0x330000, (int) webSafeColorArray[36]); + ImmutableSet addedColor + = webSafeColorsBuilder.add(LAST_COLOR_ADDED).build(); + assertEquals( + "Modifying the builder should not have changed any already built sets", + 216, webSafeColors.size()); + assertEquals("the new array should be one bigger than webSafeColors", + 217, addedColor.size()); + Integer[] appendColorArray = + addedColor.toArray(new Integer[addedColor.size()]); + assertEquals( + getComplexBuilderSetLastElement(), (int) appendColorArray[216]); + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/SimpleForEach.java b/testes/forloop/ForLoopToFunctional/SimpleForEach.java new file mode 100644 index 0000000..56ec92b --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/SimpleForEach.java @@ -0,0 +1,8 @@ +class SimpleForEach { + + public void test1(List things, PrintWriter writer) { + for (String thing: things) { + writer.write(thing); + } + } +} \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/T1.java b/testes/forloop/ForLoopToFunctional/T1.java new file mode 100644 index 0000000..3b16dc4 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/T1.java @@ -0,0 +1,23 @@ +{ + checkCanCreate(); + + logger.fine(" Testing: " + name); + logger.fine("Features: " + formatFeatureSet(features)); + + FeatureUtil.addImpliedFeatures(features); + + logger.fine("Expanded: " + formatFeatureSet(features)); + + // Class parameters must be raw. + List> testers = getTesters(); + + TestSuite suite = new TestSuite(name); + for (Class testerClass : testers) { + final TestSuite testerSuite = + makeSuiteForTesterClass((Class>) testerClass); + if (testerSuite.countTestCases() > 0) { + suite.addTest(testerSuite); + } + } + return suite; + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/T2.java b/testes/forloop/ForLoopToFunctional/T2.java new file mode 100644 index 0000000..d78572f --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/T2.java @@ -0,0 +1,83 @@ +{ + Set keySet = map.keySet(); + Collection valueCollection = map.values(); + Set> entrySet = map.entrySet(); + + assertEquals(map.size() == 0, map.isEmpty()); + assertEquals(map.size(), keySet.size()); + assertEquals(keySet.size() == 0, keySet.isEmpty()); + assertEquals(!keySet.isEmpty(), keySet.iterator().hasNext()); + + int expectedKeySetHash = 0; + for (K key : keySet) { + V value = map.get(key); + expectedKeySetHash += key != null ? key.hashCode() : 0; + assertTrue(map.containsKey(key)); + assertTrue(map.containsValue(value)); + assertTrue(valueCollection.contains(value)); + assertTrue(valueCollection.containsAll(Collections.singleton(value))); + assertTrue(entrySet.contains(mapEntry(key, value))); + assertTrue(allowsNullKeys || (key != null)); + } + assertEquals(expectedKeySetHash, keySet.hashCode()); + + assertEquals(map.size(), valueCollection.size()); + assertEquals(valueCollection.size() == 0, valueCollection.isEmpty()); + assertEquals(!valueCollection.isEmpty(), valueCollection.iterator().hasNext()); + for (V value : valueCollection) { + assertTrue(map.containsValue(value)); + assertTrue(allowsNullValues || (value != null)); + } + + assertEquals(map.size(), entrySet.size()); + assertEquals(entrySet.size() == 0, entrySet.isEmpty()); + assertEquals(!entrySet.isEmpty(), entrySet.iterator().hasNext()); + assertEntrySetNotContainsString(entrySet); + + boolean supportsValuesHashCode = supportsValuesHashCode(map); + if (supportsValuesHashCode) { + int expectedEntrySetHash = 0; + for (Entry entry : entrySet) { + assertTrue(map.containsKey(entry.getKey())); + assertTrue(map.containsValue(entry.getValue())); + int expectedHash = + (entry.getKey() == null ? 0 : entry.getKey().hashCode()) + ^ (entry.getValue() == null ? 0 : entry.getValue().hashCode()); + assertEquals(expectedHash, entry.hashCode()); + expectedEntrySetHash += expectedHash; + } + assertEquals(expectedEntrySetHash, entrySet.hashCode()); + assertTrue(entrySet.containsAll(new HashSet>(entrySet))); + assertTrue(entrySet.equals(new HashSet>(entrySet))); + } + + Object[] entrySetToArray1 = entrySet.toArray(); + assertEquals(map.size(), entrySetToArray1.length); + assertTrue(Arrays.asList(entrySetToArray1).containsAll(entrySet)); + + Entry[] entrySetToArray2 = new Entry[map.size() + 2]; + entrySetToArray2[map.size()] = mapEntry("foo", 1); + assertSame(entrySetToArray2, entrySet.toArray(entrySetToArray2)); + assertNull(entrySetToArray2[map.size()]); + assertTrue(Arrays.asList(entrySetToArray2).containsAll(entrySet)); + + Object[] valuesToArray1 = valueCollection.toArray(); + assertEquals(map.size(), valuesToArray1.length); + assertTrue(Arrays.asList(valuesToArray1).containsAll(valueCollection)); + + Object[] valuesToArray2 = new Object[map.size() + 2]; + valuesToArray2[map.size()] = "foo"; + assertSame(valuesToArray2, valueCollection.toArray(valuesToArray2)); + assertNull(valuesToArray2[map.size()]); + assertTrue(Arrays.asList(valuesToArray2).containsAll(valueCollection)); + + if (supportsValuesHashCode) { + int expectedHash = 0; + for (Entry entry : entrySet) { + expectedHash += entry.hashCode(); + } + assertEquals(expectedHash, map.hashCode()); + } + + assertMoreInvariants(map); + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/T2For.java b/testes/forloop/ForLoopToFunctional/T2For.java new file mode 100644 index 0000000..75117b3 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/T2For.java @@ -0,0 +1,10 @@ +for (K key : keySet) { + V value = map.get(key); + expectedKeySetHash += key != null ? key.hashCode() : 0; + assertTrue(map.containsKey(key)); + assertTrue(map.containsValue(value)); + assertTrue(valueCollection.contains(value)); + assertTrue(valueCollection.containsAll(Collections.singleton(value))); + assertTrue(entrySet.contains(mapEntry(key, value))); + assertTrue(allowsNullKeys || (key != null)); + } \ No newline at end of file diff --git a/testes/forloop/ForLoopToFunctional/T2For2.java b/testes/forloop/ForLoopToFunctional/T2For2.java new file mode 100644 index 0000000..5362a43 --- /dev/null +++ b/testes/forloop/ForLoopToFunctional/T2For2.java @@ -0,0 +1,9 @@ +for (Entry entry : entrySet) { + assertTrue(map.containsKey(entry.getKey())); + assertTrue(map.containsValue(entry.getValue())); + int expectedHash = + (entry.getKey() == null ? 0 : entry.getKey().hashCode()) + ^ (entry.getValue() == null ? 0 : entry.getValue().hashCode()); + assertEquals(expectedHash, entry.hashCode()); + expectedEntrySetHash += expectedHash; + } \ No newline at end of file diff --git a/testes/forloop/ProspectiveOperation/ContinueAndReturnEnhancedLoop b/testes/forloop/ProspectiveOperation/ContinueAndReturnEnhancedLoop new file mode 100644 index 0000000..697c33f --- /dev/null +++ b/testes/forloop/ProspectiveOperation/ContinueAndReturnEnhancedLoop @@ -0,0 +1,5 @@ +for(GrammarEngine e : importedEngines) { + if(e.getGrammarName() == null) continue; + if(e.getGrammarName().equals(grammarName)) + return true; + } \ No newline at end of file diff --git a/testes/forloop/ProspectiveOperation/FilterAndMergedForEach b/testes/forloop/ProspectiveOperation/FilterAndMergedForEach new file mode 100644 index 0000000..af3f52b --- /dev/null +++ b/testes/forloop/ProspectiveOperation/FilterAndMergedForEach @@ -0,0 +1,7 @@ +for (Map.Entry entry : + childClassLoaders.entrySet()) + if(isValid(entry)) { + ClassLoader cl = entry.getKey(); + if (!((WebappClassLoader)cl).isStart()) + result.add(entry.getValue()); + } \ No newline at end of file diff --git a/testes/forloop/ProspectiveOperation/FilterMapReduceEnhancedLoop b/testes/forloop/ProspectiveOperation/FilterMapReduceEnhancedLoop new file mode 100644 index 0000000..1546dfa --- /dev/null +++ b/testes/forloop/ProspectiveOperation/FilterMapReduceEnhancedLoop @@ -0,0 +1,4 @@ +for (ElementRule rule : getRules()) { + if(rule.hasErrors()) + count += rule.getErrors().size(); + } \ No newline at end of file diff --git a/testes/forloop/ProspectiveOperation/SimpleShortEnhancedLoop b/testes/forloop/ProspectiveOperation/SimpleShortEnhancedLoop new file mode 100644 index 0000000..317b394 --- /dev/null +++ b/testes/forloop/ProspectiveOperation/SimpleShortEnhancedLoop @@ -0,0 +1,3 @@ +for (String thing: things) { + writer.write(thing); +} \ No newline at end of file diff --git a/testes/forloop/Refactorer/LoopRefactorableInnerLoopButNotOuter b/testes/forloop/Refactorer/LoopRefactorableInnerLoopButNotOuter new file mode 100644 index 0000000..ab71835 --- /dev/null +++ b/testes/forloop/Refactorer/LoopRefactorableInnerLoopButNotOuter @@ -0,0 +1,9 @@ +for (BeanDefinition candidate : componentProvider + .findCandidateComponents(packageToScan)) { + if (candidate instanceof ScannedGenericBeanDefinition) { + for (ServletComponentHandler handler : HANDLERS) { + handler.handle(((ScannedGenericBeanDefinition) candidate), + (BeanDefinitionRegistry) this.applicationContext); + } + } + } \ No newline at end of file diff --git a/testes/forloop/Refactorer/ServletComponentRegisteringPostProcessor.java b/testes/forloop/Refactorer/ServletComponentRegisteringPostProcessor.java new file mode 100644 index 0000000..4d9b41f --- /dev/null +++ b/testes/forloop/Refactorer/ServletComponentRegisteringPostProcessor.java @@ -0,0 +1,116 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.servlet; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.context.annotation.ScannedGenericBeanDefinition; +import org.springframework.web.context.WebApplicationContext; + +/** + * {@link BeanFactoryPostProcessor} that registers beans for Servlet components found via + * package scanning. + * + * @author Andy Wilkinson + * @see ServletComponentScan + * @see ServletComponentScanRegistrar + */ +class ServletComponentRegisteringPostProcessor + implements BeanFactoryPostProcessor, ApplicationContextAware { + + private static final List HANDLERS; + + static { + List handlers = new ArrayList<>(); + handlers.add(new WebServletHandler()); + handlers.add(new WebFilterHandler()); + handlers.add(new WebListenerHandler()); + HANDLERS = Collections.unmodifiableList(handlers); + } + + private final Set packagesToScan; + + private ApplicationContext applicationContext; + + ServletComponentRegisteringPostProcessor(Set packagesToScan) { + this.packagesToScan = packagesToScan; + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) + throws BeansException { + if (isRunningInEmbeddedWebServer()) { + ClassPathScanningCandidateComponentProvider componentProvider = createComponentProvider(); + for (String packageToScan : this.packagesToScan) { + scanPackage(componentProvider, packageToScan); + } + } + } + + private void scanPackage( + ClassPathScanningCandidateComponentProvider componentProvider, + String packageToScan) { + for (BeanDefinition candidate : componentProvider + .findCandidateComponents(packageToScan)) { + if (candidate instanceof ScannedGenericBeanDefinition) { + for (ServletComponentHandler handler : HANDLERS) { + handler.handle(((ScannedGenericBeanDefinition) candidate), + (BeanDefinitionRegistry) this.applicationContext); + } + } + } + } + + private boolean isRunningInEmbeddedWebServer() { + return this.applicationContext instanceof WebApplicationContext + && ((WebApplicationContext) this.applicationContext) + .getServletContext() == null; + } + + private ClassPathScanningCandidateComponentProvider createComponentProvider() { + ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider( + false); + componentProvider.setEnvironment(this.applicationContext.getEnvironment()); + componentProvider.setResourceLoader(this.applicationContext); + for (ServletComponentHandler handler : HANDLERS) { + componentProvider.addIncludeFilter(handler.getTypeFilter()); + } + return componentProvider; + } + + Set getPackagesToScan() { + return Collections.unmodifiableSet(this.packagesToScan); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + +} diff --git a/testes/forloop/classFields/ForIteratingOnThisField b/testes/forloop/classFields/ForIteratingOnThisField new file mode 100644 index 0000000..6fff176 --- /dev/null +++ b/testes/forloop/classFields/ForIteratingOnThisField @@ -0,0 +1,3 @@ +for (Connector additionalConnector : this.additionalTomcatConnectors) { + tomcat.getService().addConnector(additionalConnector); + } \ No newline at end of file diff --git a/testes/forloop/classFields/ForIteratingOnThisField2 b/testes/forloop/classFields/ForIteratingOnThisField2 new file mode 100644 index 0000000..fadf1e2 --- /dev/null +++ b/testes/forloop/classFields/ForIteratingOnThisField2 @@ -0,0 +1,3 @@ +for (Valve valve : this.engineValves) { + engine.getPipeline().addValve(valve); + } \ No newline at end of file diff --git a/testes/forloop/classFields/MethodBodyIteratingOnThisField b/testes/forloop/classFields/MethodBodyIteratingOnThisField new file mode 100644 index 0000000..a5848b6 --- /dev/null +++ b/testes/forloop/classFields/MethodBodyIteratingOnThisField @@ -0,0 +1,17 @@ +{ + Tomcat tomcat = new Tomcat(); + File baseDir = (this.baseDirectory != null ? this.baseDirectory + : createTempDir("tomcat")); + tomcat.setBaseDir(baseDir.getAbsolutePath()); + Connector connector = new Connector(this.protocol); + tomcat.getService().addConnector(connector); + customizeConnector(connector); + tomcat.setConnector(connector); + tomcat.getHost().setAutoDeploy(false); + configureEngine(tomcat.getEngine()); + for (Connector additionalConnector : this.additionalTomcatConnectors) { + tomcat.getService().addConnector(additionalConnector); + } + prepareContext(tomcat.getHost(), initializers); + return getTomcatWebServer(tomcat); + } \ No newline at end of file diff --git a/testes/forloop/classFields/MethodBodyIteratingOnThisField2 b/testes/forloop/classFields/MethodBodyIteratingOnThisField2 new file mode 100644 index 0000000..11d16c0 --- /dev/null +++ b/testes/forloop/classFields/MethodBodyIteratingOnThisField2 @@ -0,0 +1,6 @@ +{ + engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay); + for (Valve valve : this.engineValves) { + engine.getPipeline().addValve(valve); + } + } \ No newline at end of file diff --git a/testes/forloop/classFields/TomcatServletWebServerFactory.java b/testes/forloop/classFields/TomcatServletWebServerFactory.java new file mode 100644 index 0000000..d57b959 --- /dev/null +++ b/testes/forloop/classFields/TomcatServletWebServerFactory.java @@ -0,0 +1,872 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.embedded.tomcat; + +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletContainerInitializer; + +import org.apache.catalina.Context; +import org.apache.catalina.Engine; +import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.Manager; +import org.apache.catalina.Valve; +import org.apache.catalina.WebResourceRoot.ResourceSetType; +import org.apache.catalina.Wrapper; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.loader.WebappLoader; +import org.apache.catalina.session.StandardManager; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.Tomcat.FixContextListener; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; +import org.apache.coyote.AbstractProtocol; +import org.apache.coyote.ProtocolHandler; +import org.apache.coyote.http11.AbstractHttp11JsseProtocol; +import org.apache.coyote.http11.AbstractHttp11Protocol; +import org.apache.coyote.http11.Http11NioProtocol; +import org.apache.tomcat.util.net.SSLHostConfig; + +import org.springframework.boot.web.server.Compression; +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.MimeMappings; +import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.Ssl.ClientAuth; +import org.springframework.boot.web.server.SslStoreProvider; +import org.springframework.boot.web.server.WebServer; +import org.springframework.boot.web.server.WebServerException; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ResourceUtils; +import org.springframework.util.StringUtils; + +/** + * {@link AbstractServletWebServerFactory} that can be used to create + * {@link TomcatWebServer}s. Can be initialized using Spring's + * {@link ServletContextInitializer}s or Tomcat {@link LifecycleListener}s. + *

+ * Unless explicitly configured otherwise this factory will created containers that + * listens for HTTP requests on port 8080. + * + * @author Phillip Webb + * @author Dave Syer + * @author Brock Mills + * @author Stephane Nicoll + * @author Andy Wilkinson + * @author EddĂș MelĂ©ndez + * @author Christoffer Sawicki + * @since 2.0.0 + * @see #setPort(int) + * @see #setContextLifecycleListeners(Collection) + * @see TomcatWebServer + */ +public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory + implements ResourceLoaderAware { + + private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private static final Set> NO_CLASSES = Collections.emptySet(); + + /** + * The class name of default protocol used. + */ + public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol"; + + private File baseDirectory; + + private List engineValves = new ArrayList<>(); + + private List contextValves = new ArrayList<>(); + + private List contextLifecycleListeners = new ArrayList<>(); + + private List tomcatContextCustomizers = new ArrayList<>(); + + private List tomcatConnectorCustomizers = new ArrayList<>(); + + private List additionalTomcatConnectors = new ArrayList<>(); + + private ResourceLoader resourceLoader; + + private String protocol = DEFAULT_PROTOCOL; + + private Set tldSkipPatterns = new LinkedHashSet<>(TldSkipPatterns.DEFAULT); + + private Charset uriEncoding = DEFAULT_CHARSET; + + private int backgroundProcessorDelay; + + /** + * Create a new {@link TomcatServletWebServerFactory} instance. + */ + public TomcatServletWebServerFactory() { + super(); + } + + /** + * Create a new {@link TomcatServletWebServerFactory} that listens for requests using + * the specified port. + * @param port the port to listen on + */ + public TomcatServletWebServerFactory(int port) { + super(port); + } + + /** + * Create a new {@link TomcatServletWebServerFactory} with the specified context path + * and port. + * @param contextPath the root context path + * @param port the port to listen on + */ + public TomcatServletWebServerFactory(String contextPath, int port) { + super(contextPath, port); + } + + @Override + public WebServer getWebServer(ServletContextInitializer... initializers) { + Tomcat tomcat = new Tomcat(); + File baseDir = (this.baseDirectory != null ? this.baseDirectory + : createTempDir("tomcat")); + tomcat.setBaseDir(baseDir.getAbsolutePath()); + Connector connector = new Connector(this.protocol); + tomcat.getService().addConnector(connector); + customizeConnector(connector); + tomcat.setConnector(connector); + tomcat.getHost().setAutoDeploy(false); + configureEngine(tomcat.getEngine()); + for (Connector additionalConnector : this.additionalTomcatConnectors) { + tomcat.getService().addConnector(additionalConnector); + } + prepareContext(tomcat.getHost(), initializers); + return getTomcatWebServer(tomcat); + } + + private void configureEngine(Engine engine) { + engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay); + for (Valve valve : this.engineValves) { + engine.getPipeline().addValve(valve); + } + } + + protected void prepareContext(Host host, ServletContextInitializer[] initializers) { + File docBase = getValidDocumentRoot(); + docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase")); + final TomcatEmbeddedContext context = new TomcatEmbeddedContext(); + context.setName(getContextPath()); + context.setDisplayName(getDisplayName()); + context.setPath(getContextPath()); + context.setDocBase(docBase.getAbsolutePath()); + context.addLifecycleListener(new FixContextListener()); + context.setParentClassLoader( + this.resourceLoader != null ? this.resourceLoader.getClassLoader() + : ClassUtils.getDefaultClassLoader()); + resetDefaultLocaleMapping(context); + addLocaleMappings(context); + try { + context.setUseRelativeRedirects(false); + } + catch (NoSuchMethodError ex) { + // Tomcat is < 8.0.30. Continue + } + SkipPatternJarScanner.apply(context, this.tldSkipPatterns); + WebappLoader loader = new WebappLoader(context.getParentClassLoader()); + loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName()); + loader.setDelegate(true); + context.setLoader(loader); + if (isRegisterDefaultServlet()) { + addDefaultServlet(context); + } + if (shouldRegisterJspServlet()) { + addJspServlet(context); + addJasperInitializer(context); + } + context.addLifecycleListener(new StaticResourceConfigurer(context)); + ServletContextInitializer[] initializersToUse = mergeInitializers(initializers); + configureContext(context, initializersToUse); + host.addChild(context); + postProcessContext(context); + } + + /** + * Override Tomcat's default locale mappings to align with other servers. See + * {@code org.apache.catalina.util.CharsetMapperDefault.properties}. + * @param context the context to reset + */ + private void resetDefaultLocaleMapping(TomcatEmbeddedContext context) { + context.addLocaleEncodingMappingParameter(Locale.ENGLISH.toString(), + DEFAULT_CHARSET.displayName()); + context.addLocaleEncodingMappingParameter(Locale.FRENCH.toString(), + DEFAULT_CHARSET.displayName()); + } + + private void addLocaleMappings(TomcatEmbeddedContext context) { + for (Map.Entry entry : getLocaleCharsetMappings().entrySet()) { + Locale locale = entry.getKey(); + Charset charset = entry.getValue(); + context.addLocaleEncodingMappingParameter(locale.toString(), + charset.toString()); + } + } + + private void addDefaultServlet(Context context) { + Wrapper defaultServlet = context.createWrapper(); + defaultServlet.setName("default"); + defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet"); + defaultServlet.addInitParameter("debug", "0"); + defaultServlet.addInitParameter("listings", "false"); + defaultServlet.setLoadOnStartup(1); + // Otherwise the default location of a Spring DispatcherServlet cannot be set + defaultServlet.setOverridable(true); + context.addChild(defaultServlet); + addServletMapping(context, "/", "default"); + } + + private void addJspServlet(Context context) { + Wrapper jspServlet = context.createWrapper(); + jspServlet.setName("jsp"); + jspServlet.setServletClass(getJsp().getClassName()); + jspServlet.addInitParameter("fork", "false"); + for (Entry initParameter : getJsp().getInitParameters() + .entrySet()) { + jspServlet.addInitParameter(initParameter.getKey(), initParameter.getValue()); + } + jspServlet.setLoadOnStartup(3); + context.addChild(jspServlet); + addServletMapping(context, "*.jsp", "jsp"); + addServletMapping(context, "*.jspx", "jsp"); + } + + @SuppressWarnings("deprecation") + private void addServletMapping(Context context, String pattern, String name) { + context.addServletMapping(pattern, name); + } + + private void addJasperInitializer(TomcatEmbeddedContext context) { + try { + ServletContainerInitializer initializer = (ServletContainerInitializer) ClassUtils + .forName("org.apache.jasper.servlet.JasperInitializer", null) + .newInstance(); + context.addServletContainerInitializer(initializer, null); + } + catch (Exception ex) { + // Probably not Tomcat 8 + } + } + + // Needs to be protected so it can be used by subclasses + protected void customizeConnector(Connector connector) { + int port = (getPort() >= 0 ? getPort() : 0); + connector.setPort(port); + if (StringUtils.hasText(this.getServerHeader())) { + connector.setAttribute("server", this.getServerHeader()); + } + if (connector.getProtocolHandler() instanceof AbstractProtocol) { + customizeProtocol((AbstractProtocol) connector.getProtocolHandler()); + } + if (getUriEncoding() != null) { + connector.setURIEncoding(getUriEncoding().name()); + } + + // If ApplicationContext is slow to start we want Tomcat not to bind to the socket + // prematurely... + connector.setProperty("bindOnInit", "false"); + + if (getSsl() != null && getSsl().isEnabled()) { + customizeSsl(connector); + } + if (getCompression() != null && getCompression().getEnabled()) { + customizeCompression(connector); + } + for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { + customizer.customize(connector); + } + } + + private void customizeProtocol(AbstractProtocol protocol) { + if (getAddress() != null) { + protocol.setAddress(getAddress()); + } + } + + private void customizeSsl(Connector connector) { + ProtocolHandler handler = connector.getProtocolHandler(); + Assert.state(handler instanceof AbstractHttp11JsseProtocol, + "To use SSL, the connector's protocol handler must be an " + + "AbstractHttp11JsseProtocol subclass"); + configureSsl((AbstractHttp11JsseProtocol) handler, getSsl()); + connector.setScheme("https"); + connector.setSecure(true); + } + + private void customizeCompression(Connector connector) { + ProtocolHandler handler = connector.getProtocolHandler(); + if (handler instanceof AbstractHttp11Protocol) { + AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler; + Compression compression = getCompression(); + protocol.setCompression("on"); + protocol.setCompressionMinSize(compression.getMinResponseSize()); + protocol.setCompressibleMimeType( + StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes())); + if (getCompression().getExcludedUserAgents() != null) { + protocol.setNoCompressionUserAgents( + StringUtils.arrayToCommaDelimitedString( + getCompression().getExcludedUserAgents())); + } + } + } + + /** + * Configure Tomcat's {@link AbstractHttp11JsseProtocol} for SSL. + * @param protocol the protocol + * @param ssl the ssl details + */ + protected void configureSsl(AbstractHttp11JsseProtocol protocol, Ssl ssl) { + protocol.setSSLEnabled(true); + protocol.setSslProtocol(ssl.getProtocol()); + configureSslClientAuth(protocol, ssl); + protocol.setKeystorePass(ssl.getKeyStorePassword()); + protocol.setKeyPass(ssl.getKeyPassword()); + protocol.setKeyAlias(ssl.getKeyAlias()); + String ciphers = StringUtils.arrayToCommaDelimitedString(ssl.getCiphers()); + protocol.setCiphers(StringUtils.hasText(ciphers) ? ciphers : null); + if (ssl.getEnabledProtocols() != null) { + try { + for (SSLHostConfig sslHostConfig : protocol.findSslHostConfigs()) { + sslHostConfig.setProtocols(StringUtils + .arrayToCommaDelimitedString(ssl.getEnabledProtocols())); + } + } + catch (NoSuchMethodError ex) { + // Tomcat 8.0.x or earlier + Assert.isTrue( + protocol.setProperty("sslEnabledProtocols", + StringUtils.arrayToCommaDelimitedString( + ssl.getEnabledProtocols())), + "Failed to set sslEnabledProtocols"); + } + } + if (getSslStoreProvider() != null) { + TomcatURLStreamHandlerFactory instance = TomcatURLStreamHandlerFactory + .getInstance(); + instance.addUserFactory( + new SslStoreProviderUrlStreamHandlerFactory(getSslStoreProvider())); + protocol.setKeystoreFile( + SslStoreProviderUrlStreamHandlerFactory.KEY_STORE_URL); + protocol.setTruststoreFile( + SslStoreProviderUrlStreamHandlerFactory.TRUST_STORE_URL); + } + else { + configureSslKeyStore(protocol, ssl); + configureSslTrustStore(protocol, ssl); + } + } + + private void configureSslClientAuth(AbstractHttp11JsseProtocol protocol, Ssl ssl) { + if (ssl.getClientAuth() == ClientAuth.NEED) { + protocol.setClientAuth(Boolean.TRUE.toString()); + } + else if (ssl.getClientAuth() == ClientAuth.WANT) { + protocol.setClientAuth("want"); + } + } + + protected void configureSslStoreProvider(AbstractHttp11JsseProtocol protocol, + SslStoreProvider sslStoreProvider) { + Assert.isInstanceOf(Http11NioProtocol.class, protocol, + "SslStoreProvider can only be used with Http11NioProtocol"); + } + + private void configureSslKeyStore(AbstractHttp11JsseProtocol protocol, Ssl ssl) { + try { + protocol.setKeystoreFile(ResourceUtils.getURL(ssl.getKeyStore()).toString()); + } + catch (FileNotFoundException ex) { + throw new WebServerException("Could not load key store: " + ex.getMessage(), + ex); + } + if (ssl.getKeyStoreType() != null) { + protocol.setKeystoreType(ssl.getKeyStoreType()); + } + if (ssl.getKeyStoreProvider() != null) { + protocol.setKeystoreProvider(ssl.getKeyStoreProvider()); + } + } + + private void configureSslTrustStore(AbstractHttp11JsseProtocol protocol, Ssl ssl) { + + if (ssl.getTrustStore() != null) { + try { + protocol.setTruststoreFile( + ResourceUtils.getURL(ssl.getTrustStore()).toString()); + } + catch (FileNotFoundException ex) { + throw new WebServerException( + "Could not load trust store: " + ex.getMessage(), ex); + } + } + protocol.setTruststorePass(ssl.getTrustStorePassword()); + if (ssl.getTrustStoreType() != null) { + protocol.setTruststoreType(ssl.getTrustStoreType()); + } + if (ssl.getTrustStoreProvider() != null) { + protocol.setTruststoreProvider(ssl.getTrustStoreProvider()); + } + } + + /** + * Configure the Tomcat {@link Context}. + * @param context the Tomcat context + * @param initializers initializers to apply + */ + protected void configureContext(Context context, + ServletContextInitializer[] initializers) { + TomcatStarter starter = new TomcatStarter(initializers); + if (context instanceof TomcatEmbeddedContext) { + // Should be true + ((TomcatEmbeddedContext) context).setStarter(starter); + } + context.addServletContainerInitializer(starter, NO_CLASSES); + for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) { + context.addLifecycleListener(lifecycleListener); + } + for (Valve valve : this.contextValves) { + context.getPipeline().addValve(valve); + } + for (ErrorPage errorPage : getErrorPages()) { + new TomcatErrorPage(errorPage).addToContext(context); + } + for (MimeMappings.Mapping mapping : getMimeMappings()) { + context.addMimeMapping(mapping.getExtension(), mapping.getMimeType()); + } + configureSession(context); + for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) { + customizer.customize(context); + } + } + + private void configureSession(Context context) { + long sessionTimeout = getSessionTimeoutInMinutes(); + context.setSessionTimeout((int) sessionTimeout); + if (isPersistSession()) { + Manager manager = context.getManager(); + if (manager == null) { + manager = new StandardManager(); + context.setManager(manager); + } + configurePersistSession(manager); + } + else { + context.addLifecycleListener(new DisablePersistSessionListener()); + } + } + + private void configurePersistSession(Manager manager) { + Assert.state(manager instanceof StandardManager, + "Unable to persist HTTP session state using manager type " + + manager.getClass().getName()); + File dir = getValidSessionStoreDir(); + File file = new File(dir, "SESSIONS.ser"); + ((StandardManager) manager).setPathname(file.getAbsolutePath()); + } + + private long getSessionTimeoutInMinutes() { + long sessionTimeout = getSessionTimeout(); + if (sessionTimeout > 0) { + sessionTimeout = Math.max(TimeUnit.SECONDS.toMinutes(sessionTimeout), 1L); + } + return sessionTimeout; + } + + /** + * Post process the Tomcat {@link Context} before it used with the Tomcat Server. + * Subclasses can override this method to apply additional processing to the + * {@link Context}. + * @param context the Tomcat {@link Context} + */ + protected void postProcessContext(Context context) { + } + + /** + * Factory method called to create the {@link TomcatWebServer}. Subclasses can + * override this method to return a different {@link TomcatWebServer} or apply + * additional processing to the Tomcat server. + * @param tomcat the Tomcat server. + * @return a new {@link TomcatWebServer} instance + */ + protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { + return new TomcatWebServer(tomcat, getPort() >= 0); + } + + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + /** + * Set the Tomcat base directory. If not specified a temporary directory will be used. + * @param baseDirectory the tomcat base directory + */ + public void setBaseDirectory(File baseDirectory) { + this.baseDirectory = baseDirectory; + } + + /** + * A comma-separated list of jars to ignore for TLD scanning. See Tomcat's + * catalina.properties for typical values. Defaults to a list drawn from that source. + * @param tldSkip the jars to skip when scanning for TLDs etc + * @deprecated as of 1.5 in favor of {@link #setTldSkipPatterns(Collection)} + */ + @Deprecated + public void setTldSkip(String tldSkip) { + Assert.notNull(tldSkip, "TldSkip must not be null"); + setTldSkipPatterns(StringUtils.commaDelimitedListToSet(tldSkip)); + } + + /** + * Returns a mutable set of the patterns that match jars to ignore for TLD scanning. + * @return the list of jars to ignore for TLD scanning + */ + public Set getTldSkipPatterns() { + return this.tldSkipPatterns; + } + + /** + * Set the patterns that match jars to ignore for TLD scanning. See Tomcat's + * catalina.properties for typical values. Defaults to a list drawn from that source. + * @param patterns the jar patterns to skip when scanning for TLDs etc + */ + public void setTldSkipPatterns(Collection patterns) { + Assert.notNull(patterns, "Patterns must not be null"); + this.tldSkipPatterns = new LinkedHashSet<>(patterns); + } + + /** + * Add patterns that match jars to ignore for TLD scanning. See Tomcat's + * catalina.properties for typical values. + * @param patterns the additional jar patterns to skip when scanning for TLDs etc + */ + public void addTldSkipPatterns(String... patterns) { + Assert.notNull(patterns, "Patterns must not be null"); + this.tldSkipPatterns.addAll(Arrays.asList(patterns)); + } + + /** + * The Tomcat protocol to use when create the {@link Connector}. + * @param protocol the protocol + * @see Connector#Connector(String) + */ + public void setProtocol(String protocol) { + Assert.hasLength(protocol, "Protocol must not be empty"); + this.protocol = protocol; + } + + /** + * Set {@link Valve}s that should be applied to the Tomcat {@link Engine}. Calling + * this method will replace any existing valves. + * @param engineValves the valves to set + */ + public void setEngineValves(Collection engineValves) { + Assert.notNull(engineValves, "Valves must not be null"); + this.engineValves = new ArrayList<>(engineValves); + } + + /** + * Returns a mutable collection of the {@link Valve}s that will be applied to the + * Tomcat {@link Engine}. + * @return the engineValves the valves that will be applied + */ + public Collection getEngineValves() { + return this.engineValves; + } + + /** + * Add {@link Valve}s that should be applied to the Tomcat {@link Engine}. + * @param engineValves the valves to add + */ + public void addEngineValves(Valve... engineValves) { + Assert.notNull(engineValves, "Valves must not be null"); + this.engineValves.addAll(Arrays.asList(engineValves)); + } + + /** + * Set {@link Valve}s that should be applied to the Tomcat {@link Context}. Calling + * this method will replace any existing valves. + * @param contextValves the valves to set + */ + public void setContextValves(Collection contextValves) { + Assert.notNull(contextValves, "Valves must not be null"); + this.contextValves = new ArrayList<>(contextValves); + } + + /** + * Returns a mutable collection of the {@link Valve}s that will be applied to the + * Tomcat {@link Context}. + * @return the contextValves the valves that will be applied + * @see #getEngineValves() + */ + public Collection getContextValves() { + return this.contextValves; + } + + /** + * Add {@link Valve}s that should be applied to the Tomcat {@link Context}. + * @param contextValves the valves to add + */ + public void addContextValves(Valve... contextValves) { + Assert.notNull(contextValves, "Valves must not be null"); + this.contextValves.addAll(Arrays.asList(contextValves)); + } + + /** + * Set {@link LifecycleListener}s that should be applied to the Tomcat {@link Context} + * . Calling this method will replace any existing listeners. + * @param contextLifecycleListeners the listeners to set + */ + public void setContextLifecycleListeners( + Collection contextLifecycleListeners) { + Assert.notNull(contextLifecycleListeners, + "ContextLifecycleListeners must not be null"); + this.contextLifecycleListeners = new ArrayList<>(contextLifecycleListeners); + } + + /** + * Returns a mutable collection of the {@link LifecycleListener}s that will be applied + * to the Tomcat {@link Context} . + * @return the contextLifecycleListeners the listeners that will be applied + */ + public Collection getContextLifecycleListeners() { + return this.contextLifecycleListeners; + } + + /** + * Add {@link LifecycleListener}s that should be added to the Tomcat {@link Context}. + * @param contextLifecycleListeners the listeners to add + */ + public void addContextLifecycleListeners( + LifecycleListener... contextLifecycleListeners) { + Assert.notNull(contextLifecycleListeners, + "ContextLifecycleListeners must not be null"); + this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners)); + } + + /** + * Set {@link TomcatContextCustomizer}s that should be applied to the Tomcat + * {@link Context} . Calling this method will replace any existing customizers. + * @param tomcatContextCustomizers the customizers to set + */ + public void setTomcatContextCustomizers( + Collection tomcatContextCustomizers) { + Assert.notNull(tomcatContextCustomizers, + "TomcatContextCustomizers must not be null"); + this.tomcatContextCustomizers = new ArrayList<>(tomcatContextCustomizers); + } + + /** + * Returns a mutable collection of the {@link TomcatContextCustomizer}s that will be + * applied to the Tomcat {@link Context} . + * @return the listeners that will be applied + */ + public Collection getTomcatContextCustomizers() { + return this.tomcatContextCustomizers; + } + + /** + * Add {@link TomcatContextCustomizer}s that should be added to the Tomcat + * {@link Context}. + * @param tomcatContextCustomizers the customizers to add + */ + public void addContextCustomizers( + TomcatContextCustomizer... tomcatContextCustomizers) { + Assert.notNull(tomcatContextCustomizers, + "TomcatContextCustomizers must not be null"); + this.tomcatContextCustomizers.addAll(Arrays.asList(tomcatContextCustomizers)); + } + + /** + * Set {@link TomcatConnectorCustomizer}s that should be applied to the Tomcat + * {@link Connector} . Calling this method will replace any existing customizers. + * @param tomcatConnectorCustomizers the customizers to set + */ + public void setTomcatConnectorCustomizers( + Collection tomcatConnectorCustomizers) { + Assert.notNull(tomcatConnectorCustomizers, + "TomcatConnectorCustomizers must not be null"); + this.tomcatConnectorCustomizers = new ArrayList<>(tomcatConnectorCustomizers); + } + + /** + * Add {@link TomcatContextCustomizer}s that should be added to the Tomcat + * {@link Connector}. + * @param tomcatConnectorCustomizers the customizers to add + */ + public void addConnectorCustomizers( + TomcatConnectorCustomizer... tomcatConnectorCustomizers) { + Assert.notNull(tomcatConnectorCustomizers, + "TomcatConnectorCustomizers must not be null"); + this.tomcatConnectorCustomizers.addAll(Arrays.asList(tomcatConnectorCustomizers)); + } + + /** + * Returns a mutable collection of the {@link TomcatConnectorCustomizer}s that will be + * applied to the Tomcat {@link Context} . + * @return the listeners that will be applied + */ + public Collection getTomcatConnectorCustomizers() { + return this.tomcatConnectorCustomizers; + } + + /** + * Add {@link Connector}s in addition to the default connector, e.g. for SSL or AJP + * @param connectors the connectors to add + */ + public void addAdditionalTomcatConnectors(Connector... connectors) { + Assert.notNull(connectors, "Connectors must not be null"); + this.additionalTomcatConnectors.addAll(Arrays.asList(connectors)); + } + + /** + * Returns a mutable collection of the {@link Connector}s that will be added to the + * Tomcat. + * @return the additionalTomcatConnectors + */ + public List getAdditionalTomcatConnectors() { + return this.additionalTomcatConnectors; + } + + /** + * Set the character encoding to use for URL decoding. If not specified 'UTF-8' will + * be used. + * @param uriEncoding the uri encoding to set + */ + public void setUriEncoding(Charset uriEncoding) { + this.uriEncoding = uriEncoding; + } + + /** + * Returns the character encoding to use for URL decoding. + * @return the URI encoding + */ + public Charset getUriEncoding() { + return this.uriEncoding; + } + + /** + * Sets the background processor delay in seconds. + * @param delay the delay in seconds + * @since 1.4.1 + */ + public void setBackgroundProcessorDelay(int delay) { + this.backgroundProcessorDelay = delay; + } + + /** + * {@link LifecycleListener} to disable persistence in the {@link StandardManager}. A + * {@link LifecycleListener} is used so not to interfere with Tomcat's default manager + * creation logic. + */ + private static class DisablePersistSessionListener implements LifecycleListener { + + @Override + public void lifecycleEvent(LifecycleEvent event) { + if (event.getType().equals(Lifecycle.START_EVENT)) { + Context context = (Context) event.getLifecycle(); + Manager manager = context.getManager(); + if (manager != null && manager instanceof StandardManager) { + ((StandardManager) manager).setPathname(null); + } + } + } + + } + + private final class StaticResourceConfigurer implements LifecycleListener { + + private final Context context; + + private StaticResourceConfigurer(Context context) { + this.context = context; + } + + @Override + public void lifecycleEvent(LifecycleEvent event) { + if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { + addResourceJars(getUrlsOfJarsWithMetaInfResources()); + } + } + + private void addResourceJars(List resourceJarUrls) { + for (URL url : resourceJarUrls) { + String file = url.getFile(); + if (file.endsWith(".jar") || file.endsWith(".jar!/")) { + String jar = url.toString(); + if (!jar.startsWith("jar:")) { + // A jar file in the file system. Convert to Jar URL. + jar = "jar:" + jar + "!/"; + } + addResourceSet(jar); + } + else { + addResourceSet(url.toString()); + } + } + } + + private void addResourceSet(String resource) { + try { + if (isInsideNestedJar(resource)) { + // It's a nested jar but we now don't want the suffix because Tomcat + // is going to try and locate it as a root URL (not the resource + // inside it) + resource = resource.substring(0, resource.length() - 2); + } + URL url = new URL(resource); + String path = "/META-INF/resources"; + this.context.getResources().createWebResourceSet( + ResourceSetType.RESOURCE_JAR, "/", url, path); + } + catch (Exception ex) { + // Ignore (probably not a directory) + } + } + + private boolean isInsideNestedJar(String dir) { + return dir.indexOf("!/") < dir.lastIndexOf("!/"); + } + + } + +} diff --git a/testes/forloop/localVariables/ClassWithFields.java b/testes/forloop/localVariables/ClassWithFields.java new file mode 100644 index 0000000..33bd7b7 --- /dev/null +++ b/testes/forloop/localVariables/ClassWithFields.java @@ -0,0 +1,18 @@ +import java.util.ArrayList; +import java.util.List; + +public class ClassWithFields { + + public static final String NAME = "Classe"; + + private static final int NUMBERS_SIZE = 10; + + private String name; + + private List numbers = new ArrayList<>(NUMBERS_SIZE); + + private Object[] objArray; + + private Object objArray2[]; + +} \ No newline at end of file diff --git a/testes/forloop/localVariables/EnhancedForLoopFinalVarDecl b/testes/forloop/localVariables/EnhancedForLoopFinalVarDecl new file mode 100644 index 0000000..eec540b --- /dev/null +++ b/testes/forloop/localVariables/EnhancedForLoopFinalVarDecl @@ -0,0 +1,44 @@ +{ + // Corner case: List is empty. + if (futures.isEmpty()) { + handleAllCompleted(); + return; + } + + // NOTE: If we ever want to use a custom executor here, have a look at CombinedFuture as we'll + // need to handle RejectedExecutionException + + if (allMustSucceed) { + // We need fail fast, so we have to keep track of which future failed so we can propagate + // the exception immediately + + // Register a listener on each Future in the list to update the state of this future. + // Note that if all the futures on the list are done prior to completing this loop, the last + // call to addListener() will callback to setOneValue(), transitively call our cleanup + // listener, and set this.futures to null. + // This is not actually a problem, since the foreach only needs this.futures to be non-null + // at the beginning of the loop. + int i = 0; + for (final ListenableFuture listenableFinal : futures) { + final int index = i++; + listenable.addListener( + new Runnable() { + @Override + public void run() { + try { + handleOneInputDone(index, listenable); + } finally { + decrementCountAndMaybeComplete(); + } + } + }, + directExecutor()); + } + } else { + // We'll only call the callback when all futures complete, regardless of whether some failed + // Hold off on calling setOneValue until all complete, so we can share the same listener + for (ListenableFuture listenableNonFinal : futures) { + listenable.addListener(this, directExecutor()); + } + } + } \ No newline at end of file diff --git a/testes/forloop/localVariables/EnhancedForLoopVarsWithinLoop b/testes/forloop/localVariables/EnhancedForLoopVarsWithinLoop new file mode 100644 index 0000000..34829b7 --- /dev/null +++ b/testes/forloop/localVariables/EnhancedForLoopVarsWithinLoop @@ -0,0 +1,9 @@ +{ + int localVarNotWithinLoop; + Object localVarNotWithinLoopAgain; + for (String insideDecl : strings) { + final int insideBody = 50; + String insideBodyStr = "insideBody"; + } + Object notWithinLoopAfterLoop = null; +} \ No newline at end of file diff --git a/testes/forloop/localVariables/EnhancedForLoopWithException b/testes/forloop/localVariables/EnhancedForLoopWithException new file mode 100644 index 0000000..d87dbb5 --- /dev/null +++ b/testes/forloop/localVariables/EnhancedForLoopWithException @@ -0,0 +1,24 @@ +{ + final Map map; + final Set> entrySet; + try { + map = makePopulatedMap(); + } catch (UnsupportedOperationException e) { + return; + } + assertInvariants(map); + + entrySet = map.entrySet(); + final K unmappedKey; + final V unmappedValue; + try { + unmappedKey = getKeyNotInPopulatedMap(); + unmappedValue = getValueNotInPopulatedMap(); + } catch (UnsupportedOperationException e) { + return; + } + for (Entry entry : entrySet) { + assertFalse(unmappedKey.equals(entry.getKey())); + assertFalse(unmappedValue.equals(entry.getValue())); + } + } \ No newline at end of file diff --git a/testes/forloop/localVariables/IterableParameterMethodBody b/testes/forloop/localVariables/IterableParameterMethodBody new file mode 100644 index 0000000..9701499 --- /dev/null +++ b/testes/forloop/localVariables/IterableParameterMethodBody @@ -0,0 +1,9 @@ +{ + ImmutableList.Builder builder = ImmutableList.builder(); + for (K type : types) { + if (!getRawType(type).isInterface()) { + builder.add(type); + } + } + return super.collectTypes(builder.build()); + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyAssignmentInsideLoop b/testes/forloop/localVariables/MethodBodyAssignmentInsideLoop new file mode 100644 index 0000000..8b30e1a --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyAssignmentInsideLoop @@ -0,0 +1,11 @@ +{ + Collection exceptions = null; + try { + for (Path entry : dir) { + exceptions = concat(exceptions, deleteRecursivelyInsecure(entry)); + } + return exceptions; + } catch (DirectoryIteratorException e) { + return addException(exceptions, e.getCause()); + } + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyPostDecrementedVar b/testes/forloop/localVariables/MethodBodyPostDecrementedVar new file mode 100644 index 0000000..3b9e45f --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyPostDecrementedVar @@ -0,0 +1,44 @@ +{ + int hits = 0; + int misses = 0; + + Map result = Maps.newLinkedHashMap(); + Set keysToLoad = Sets.newLinkedHashSet(); +// for (K key : keys) { +// V value = get(key); +// if (!result.containsKey(key)) { +// result.put(key, value); +// if (value == null) { +// misses++; +// keysToLoad.add(key); +// } else { +// hits++; +// } +// } +// } + + try { + if (!keysToLoad.isEmpty()) { + try { + Map newEntries = loadAll(keysToLoad, defaultLoader); + for (K key : keysToLoad) { + V value = newEntries.get(key); + if (value == null) { + throw new InvalidCacheLoadException("loadAll failed to return a value for " + key); + } + result.put(key, value); + } + } catch (UnsupportedLoadingOperationException e) { + // loadAll not implemented, fallback to load + for (K key : keysToLoad) { + misses--; // get will count this miss + result.put(key, get(key, defaultLoader)); + } + } + } + return ImmutableMap.copyOf(result); + } finally { + globalStatsCounter.recordHits(hits); + globalStatsCounter.recordMisses(misses); + } + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyPostIncrementedVar b/testes/forloop/localVariables/MethodBodyPostIncrementedVar new file mode 100644 index 0000000..73e9fc8 --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyPostIncrementedVar @@ -0,0 +1,11 @@ +{ + // Collect the values if (a) our output requires collecting them and (b) we haven't been + // collecting them as we go. (We've collected them as we go only if we needed to fail fast) + if (collectsValues & !allMustSucceed) { + int i = 0; + for (ListenableFuture listenable : futures) { + handleOneInputDone(i++, listenable); + } + } + handleAllCompleted(); + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyPostIncrementedVar2 b/testes/forloop/localVariables/MethodBodyPostIncrementedVar2 new file mode 100644 index 0000000..569c8b9 --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyPostIncrementedVar2 @@ -0,0 +1,43 @@ +{ + // Corner case: List is empty. + if (futures.isEmpty()) { + handleAllCompleted(); + return; + } + + // NOTE: If we ever want to use a custom executor here, have a look at CombinedFuture as we'll + // need to handle RejectedExecutionException + if (allMustSucceed) { + // We need fail fast, so we have to keep track of which future failed so we can propagate + // the exception immediately + + // Register a listener on each Future in the list to update the state of this future. + // Note that if all the futures on the list are done prior to completing this loop, the last + // call to addListener() will callback to setOneValue(), transitively call our cleanup + // listener, and set this.futures to null. + // This is not actually a problem, since the foreach only needs this.futures to be non-null + // at the beginning of the loop. + int i = 0; + for (final ListenableFuture listenable : futures) { + final int index = i++; + listenable.addListener( + new Runnable() { + @Override + public void run() { + try { + handleOneInputDone(index, listenable); + } finally { + decrementCountAndMaybeComplete(); + } + } + }, + directExecutor()); + } + } else { + // We'll only call the callback when all futures complete, regardless of whether some failed + // Hold off on calling setOneValue until all complete, so we can share the same listener + for (ListenableFuture listenable : futures) { + listenable.addListener(this, directExecutor()); + } + } + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyPostIncrementedVar3 b/testes/forloop/localVariables/MethodBodyPostIncrementedVar3 new file mode 100644 index 0000000..0253d77 --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyPostIncrementedVar3 @@ -0,0 +1,8 @@ +{ + ImmutableMap.Builder builder = new ImmutableMap.Builder(list.size()); + int i = 0; + for (E e : list) { + builder.put(e, i++); + } + return builder.build(); + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyReduceWithPostIncrement b/testes/forloop/localVariables/MethodBodyReduceWithPostIncrement new file mode 100644 index 0000000..f200967 --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyReduceWithPostIncrement @@ -0,0 +1,18 @@ +{ + if (entries.isEmpty()) { + return emptyMultiset(comparator); + } + ImmutableList.Builder elementsBuilder = new ImmutableList.Builder(entries.size()); + long[] cumulativeCounts = new long[entries.size() + 1]; + int i = 0; + for (Entry entry : entries) { + elementsBuilder.add(entry.getElement()); + // cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount(); + i++; + } + return new RegularImmutableSortedMultiset( + new RegularImmutableSortedSet(elementsBuilder.build(), comparator), + cumulativeCounts, + 0, + entries.size()); + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyWithAnonnymousInnerClass b/testes/forloop/localVariables/MethodBodyWithAnonnymousInnerClass new file mode 100644 index 0000000..52cc226 --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyWithAnonnymousInnerClass @@ -0,0 +1,19 @@ +{ + return new ForwardingTypeCollector(this) { + @Override + Iterable getInterfaces(K type) { + return ImmutableSet.of(); + } + + @Override + ImmutableList collectTypes(Iterable types) { + ImmutableList.Builder builder = ImmutableList.builder(); + for (K type : types) { + if (!getRawType(type).isInterface()) { + builder.add(type); + } + } + return super.collectTypes(builder.build()); + } + }; + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MethodBodyWithTwoReferencesToOutsideNonEffectiveVars b/testes/forloop/localVariables/MethodBodyWithTwoReferencesToOutsideNonEffectiveVars new file mode 100644 index 0000000..74ecedd --- /dev/null +++ b/testes/forloop/localVariables/MethodBodyWithTwoReferencesToOutsideNonEffectiveVars @@ -0,0 +1,18 @@ +{ + if (entries.isEmpty()) { + return emptyMultiset(comparator); + } + ImmutableList.Builder elementsBuilder = new ImmutableList.Builder(entries.size()); + long[] cumulativeCounts = new long[entries.size() + 1]; + int i = 0; + for (Entry entry : entries) { + elementsBuilder.add(entry.getElement()); + cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount(); + i++; + } + return new RegularImmutableSortedMultiset( + new RegularImmutableSortedSet(elementsBuilder.build(), comparator), + cumulativeCounts, + 0, + entries.size()); + } \ No newline at end of file diff --git a/testes/forloop/localVariables/MultiplePlainArrayDeclarations b/testes/forloop/localVariables/MultiplePlainArrayDeclarations new file mode 100644 index 0000000..0e65cea --- /dev/null +++ b/testes/forloop/localVariables/MultiplePlainArrayDeclarations @@ -0,0 +1,15 @@ +{ + int[] intArray; + intArray = new int[5]; + String[] strArray = {"a", "b", "c"}; + Object[] objArray; + final Object[] finalObjArray; + final String[] finalStrArray = {"c", "b", "a"}; + + // discouraged syntax + Object objDiscouraged[]; + String strDiscouraged[] = {"a", "b", "c"}; + final Object finalObjDiscouraged[]; + final int finalIntDiscouraged[]; + finalIntDiscouraged = new int[5]; +} \ No newline at end of file diff --git a/testes/forloop/localVariables/NonEffectiveFinalUsedInEnhancedFor b/testes/forloop/localVariables/NonEffectiveFinalUsedInEnhancedFor new file mode 100644 index 0000000..387209d --- /dev/null +++ b/testes/forloop/localVariables/NonEffectiveFinalUsedInEnhancedFor @@ -0,0 +1,14 @@ +{ + String prefix = group; + if (!prefix.endsWith(".")) { + prefix = prefix + "."; + } + for (Metric metric : values) { + if (!metric.getName().startsWith(prefix)) { + metric = new Metric(prefix + metric.getName(), metric.getValue(), + metric.getTimestamp()); + } + this.repository.set(metric); + } + this.groups.add(group); + } \ No newline at end of file