package mit.edu.concurrencyrefactorings.refactorings; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import mit.edu.concurrencyrefactorings.util.ChecksAndResources; import mit.edu.concurrencyrefactorings.util.CompilationUnitSourceContext; import mit.edu.concurrencyrefactorings.util.MessageUtil; import mit.edu.concurrencyrefactorings.util.ModifierRewrite; import mit.edu.concurrencyrefactorings.util.ResourceUtil; import mit.edu.concurrencyrefactorings.util.TextChangeCompatibility; import mit.edu.concurrencyrefactorings.util.TextChangeManager; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.jdt.core.dom.ClassInstanceCreation; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.FieldAccess; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.IfStatement; import org.eclipse.jdt.core.dom.InfixExpression; import org.eclipse.jdt.core.dom.Message; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Modifier; import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.jdt.core.refactoring.IJavaRefactorings; import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.ui.CodeStyleConfiguration; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.ltk.core.refactoring.TextChange; import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.TextEdit; import org.eclipse.text.edits.TextEditGroup; public class ConvertToFJTaskRefactoring extends Refactoring { private static final String NO_NAME= ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string; private IMethod fMethod; private CompilationUnit fRoot; private MethodDeclaration fMethodDeclaration; private ASTRewrite fRewriter; private TextChangeManager fChangeManager; private ImportRewrite fImportRewrite; private String nameForFJTaskSubtype= ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string; private String sequentialThreshold; private boolean fInfixExpressionFlag= false; private boolean fMethodInvocationFlag= false; public ConvertToFJTaskRefactoring(IMethod method){ fChangeManager= new TextChangeManager(); this.fMethod= method; nameForFJTaskSubtype= suggestTaskName(); } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { RefactoringStatus result= new RefactoringStatus(); fChangeManager.clear(); pm.beginTask(ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string, 12); pm.setTaskName(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_check_preconditions); pm.worked(1); pm.setTaskName(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_name); List<TextEditGroup> ownerDescriptions= new ArrayList<TextEditGroup>(); ICompilationUnit owner= fMethod.getCompilationUnit(); fImportRewrite= CodeStyleConfiguration.createImportRewrite(fRoot, true); checkCompileErrors(result, fRoot, owner); ownerDescriptions.addAll(addCreateTaskClass(fRoot, result)); if (result.hasFatalError()) return result; ownerDescriptions.addAll(reimplementOriginalRecursiveFunction()); addImports(fImportRewrite); createEdits(owner, fRewriter, ownerDescriptions, fImportRewrite); IFile[] filesToBeModified= ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); result.merge(ChecksAndResources.validateModifiesFiles(filesToBeModified, getValidationContext())); if (result.hasFatalError()) return result; ResourceChangeChecker.checkFilesToBeChanged(filesToBeModified, new SubProgressMonitor(pm, 1)); return result; } private Collection<TextEditGroup> reimplementOriginalRecursiveFunction() { TextEditGroup gd= new TextEditGroup(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_recursive_method); AST ast= fRoot.getAST(); Block originalBody= fMethodDeclaration.getBody(); Block newMethodBody= ast.newBlock(); List<ASTNode> newStatements= newMethodBody.statements(); String declareNumOfAvailableResource= "int processorCount = Runtime.getRuntime().availableProcessors();"; //$NON-NLS-1$ ASTNode declNumOfAvailableResources= fRewriter.createStringPlaceholder(declareNumOfAvailableResource, ASTNode.EXPRESSION_STATEMENT); newStatements.add(declNumOfAvailableResources); String pool= new String("ForkJoinPool pool = new ForkJoinPool(processorCount);"); //$NON-NLS-1$ ASTNode poolNode= fRewriter.createStringPlaceholder(pool, ASTNode.EXPRESSION_STATEMENT); newStatements.add(poolNode); VariableDeclarationFragment newTaskDeclFragment= ast.newVariableDeclarationFragment(); String taskInstanceName= "a" + nameForFJTaskSubtype; //$NON-NLS-1$ newTaskDeclFragment.setName(ast.newSimpleName(taskInstanceName)); ClassInstanceCreation createTaskInstance= ast.newClassInstanceCreation(); newTaskDeclFragment.setInitializer(createTaskInstance); createTaskInstance.setType(ast.newSimpleType(ast.newSimpleName(nameForFJTaskSubtype))); List<SimpleName> argumentsForTaskCreation= createTaskInstance.arguments(); List<ASTNode> recursiveMethodParameters= fMethodDeclaration.parameters(); for (Object par : recursiveMethodParameters) { SingleVariableDeclaration parameter= (SingleVariableDeclaration) par; argumentsForTaskCreation.add(ast.newSimpleName(parameter.getName().getIdentifier())); } VariableDeclarationStatement declTask= ast.newVariableDeclarationStatement(newTaskDeclFragment); declTask.setType(ast.newSimpleType(ast.newSimpleName(nameForFJTaskSubtype))); newStatements.add(declTask); String poolInvoke= "pool.invoke(" + taskInstanceName +");"; //$NON-NLS-1$ //$NON-NLS-2$ ASTNode poolInvokeNode= fRewriter.createStringPlaceholder(poolInvoke, ASTNode.EXPRESSION_STATEMENT); newStatements.add(poolInvokeNode); if (! recursiveMethodReturnsVoid()) { String returnSt= "return " + taskInstanceName + ".result;"; //$NON-NLS-1$ //$NON-NLS-2$ ASTNode returnNode= fRewriter.createStringPlaceholder(returnSt, ASTNode.EXPRESSION_STATEMENT); newStatements.add(returnNode); } fRewriter.replace(originalBody, newMethodBody, gd); ArrayList<TextEditGroup> group= new ArrayList<TextEditGroup>(); group.add(gd); return group; } private void addImports(ImportRewrite importRewrite) { importRewrite.addImport("java.util.concurrent.ForkJoinPool"); //$NON-NLS-1$ importRewrite.addImport("java.util.concurrent.RecursiveAction"); //$NON-NLS-1$ } private Collection<TextEditGroup> addCreateTaskClass(CompilationUnit root, RefactoringStatus result) { TextEditGroup gd= new TextEditGroup(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_recursive_action); AST ast= root.getAST(); TypeDeclaration recursiveActionSubtype= ast.newTypeDeclaration(); recursiveActionSubtype.setName(ast.newSimpleName(nameForFJTaskSubtype)); recursiveActionSubtype.setSuperclassType(ast.newSimpleType(ast.newSimpleName("RecursiveAction"))); //$NON-NLS-1$ ModifierRewrite.create(fRewriter, recursiveActionSubtype).copyAllModifiers(fMethodDeclaration, gd); createFields(recursiveActionSubtype, ast); createContructor(recursiveActionSubtype, ast); createComputeMethod(recursiveActionSubtype,ast, result); copyRecursiveMethod(recursiveActionSubtype, ast); ChildListPropertyDescriptor descriptor= getBodyDeclarationsProperty(fMethodDeclaration.getParent()); fRewriter.getListRewrite(fMethodDeclaration.getParent(), descriptor).insertAfter(recursiveActionSubtype, fMethodDeclaration, gd); ArrayList<TextEditGroup> group= new ArrayList<TextEditGroup>(); group.add(gd); return group; } private void copyRecursiveMethod(TypeDeclaration recursiveActionSubtype, AST ast) { ASTNode copyRecursiveMethod= ASTNode.copySubtree(ast, fMethodDeclaration); recursiveActionSubtype.bodyDeclarations().add(copyRecursiveMethod); } private void createComputeMethod(TypeDeclaration recursiveActionSubtype, AST ast, RefactoringStatus result) { MethodDeclaration computeMethod= ast.newMethodDeclaration(); computeMethod.setName(ast.newSimpleName("compute")); //$NON-NLS-1$ computeMethod.modifiers().add(ast.newModifier(ModifierKeyword.PROTECTED_KEYWORD)); final TextEditGroup editGroup= new TextEditGroup(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_generate_compute); Statement recursionBaseCaseBranch= identifyRecursionBaseCaseBranch(fMethodDeclaration.getBody()); if (recursionBaseCaseBranch== null) { RefactoringStatus fatalError= new RefactoringStatus(); fatalError.addFatalError(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_recursion_error_1 + fMethod.getElementName() + ConcurrencyRefactorings.ConvertToFJTaskRefactoring_recursion_error_2); result.merge(fatalError); return; } final ASTRewrite scratchRewriter= ASTRewrite.create(fRoot.getAST()); ASTNode sequentialThresholdCheck= scratchRewriter.createStringPlaceholder("(" + sequentialThreshold + ")", ASTNode.PARENTHESIZED_EXPRESSION); //$NON-NLS-1$ //$NON-NLS-2$ IfStatement enclosingIf= (IfStatement) recursionBaseCaseBranch.getParent(); scratchRewriter.replace(enclosingIf.getExpression(), (Expression) sequentialThresholdCheck, editGroup); if (recursionBaseCaseBranch instanceof Block) { Block baseCaseBlock= (Block) recursionBaseCaseBranch; List<ASTNode> statementsInBaseCase= baseCaseBlock.statements(); ASTNode lastStatementInBaseCase= statementsInBaseCase.get(statementsInBaseCase.size() - 1 ); if (recursiveMethodReturnsVoid()) { ExpressionStatement sequentialMethodInvocation= ast.newExpressionStatement(createSequentialMethodInvocation(ast)); ListRewrite listRewriteForBaseBlock= scratchRewriter.getListRewrite(baseCaseBlock, Block.STATEMENTS_PROPERTY); listRewriteForBaseBlock.insertBefore(sequentialMethodInvocation, lastStatementInBaseCase, editGroup); } else { Assignment assignmentToResult= ast.newAssignment(); assignmentToResult.setLeftHandSide(ast.newSimpleName("result")); //$NON-NLS-1$ assignmentToResult.setRightHandSide(createSequentialMethodInvocation(ast)); ExpressionStatement newExpressionStatement= ast.newExpressionStatement(assignmentToResult); ListRewrite listRewriteForBaseBlock= scratchRewriter.getListRewrite(baseCaseBlock, Block.STATEMENTS_PROPERTY); listRewriteForBaseBlock.insertBefore(newExpressionStatement, lastStatementInBaseCase, editGroup); ReturnStatement newReturnResult= ast.newReturnStatement(); scratchRewriter.replace(lastStatementInBaseCase, newReturnResult, editGroup); } } else if (recursionBaseCaseBranch instanceof ReturnStatement) { Block basecaseBlock= ast.newBlock(); List<ASTNode> basecaseStatements= basecaseBlock.statements(); if (recursiveMethodReturnsVoid()) { ExpressionStatement sequentialMethodInvocation= ast.newExpressionStatement(createSequentialMethodInvocation(ast)); basecaseStatements.add(sequentialMethodInvocation); } else { Assignment assignmentToResult= ast.newAssignment(); assignmentToResult.setLeftHandSide(ast.newSimpleName("result")); //$NON-NLS-1$ assignmentToResult.setRightHandSide(createSequentialMethodInvocation(ast)); ExpressionStatement newExpressionStatement= ast.newExpressionStatement(assignmentToResult); basecaseStatements.add(newExpressionStatement); } basecaseStatements.add(ast.newReturnStatement()); scratchRewriter.replace(recursionBaseCaseBranch, basecaseBlock, editGroup); } final List<Statement> lastStatementWithRecursiveMethodInvocation= new ArrayList<Statement>(); final int[] taskNumber= new int[] {0}; final List<String> partialComputationsNames= new ArrayList<String>(); final List<String> typesOfComputations= new ArrayList<String>(); fMethodDeclaration.accept(new ASTVisitor() { public boolean visit(MethodInvocation methodCall) { IMethodBinding bindingForMethodCall= methodCall.resolveMethodBinding(); IMethodBinding bindingForMethodDeclaration= fMethodDeclaration.resolveBinding(); if (bindingForMethodCall.isEqualTo(bindingForMethodDeclaration)) { String codeForTaskDecl= nameForFJTaskSubtype + " task" + ++taskNumber[0] + //$NON-NLS-1$ " = new " + nameForFJTaskSubtype + "("; //$NON-NLS-1$ //$NON-NLS-2$ String methodArguments= ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string; List<Expression> arguments= methodCall.arguments(); for (Iterator<Expression> iterator= arguments.iterator(); iterator .hasNext();) { ASTNode argument= iterator.next(); methodArguments += argument.toString(); if (iterator.hasNext()) { methodArguments += ", "; //$NON-NLS-1$ } } codeForTaskDecl += methodArguments + ");"; //$NON-NLS-1$ VariableDeclarationStatement taskDeclStatement= (VariableDeclarationStatement) scratchRewriter.createStringPlaceholder(codeForTaskDecl , ASTNode.VARIABLE_DECLARATION_STATEMENT); Statement parentOfMethodCall= findParentStatement(methodCall); if (recursiveMethodReturnsVoid()) { scratchRewriter.replace(parentOfMethodCall, taskDeclStatement, editGroup); } else { if (parentOfMethodCall instanceof VariableDeclarationStatement){ VariableDeclarationStatement varDeclaration= (VariableDeclarationStatement) parentOfMethodCall; VariableDeclarationFragment varFragment= (VariableDeclarationFragment) varDeclaration.fragments().get(0); partialComputationsNames.add(varFragment.getName().getIdentifier()); typesOfComputations.add(varDeclaration.getType().toString()); scratchRewriter.replace(parentOfMethodCall, taskDeclStatement, editGroup); } else if (parentOfMethodCall instanceof ExpressionStatement) { ExpressionStatement exprStatement= (ExpressionStatement) parentOfMethodCall; Expression expressionContainer= exprStatement.getExpression(); if (expressionContainer instanceof Assignment) { Assignment assignment= (Assignment) expressionContainer; Expression leftHandSide= assignment.getLeftHandSide(); partialComputationsNames.add(leftHandSide.toString()); typesOfComputations.add(leftHandSide.resolveTypeBinding().getName()); scratchRewriter.replace(parentOfMethodCall, taskDeclStatement, editGroup); } else System.err.println(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_scenario_error + parentOfMethodCall.toString() ); } else if (parentOfMethodCall instanceof ReturnStatement) { ASTNode tempNode= parentOfMethodCall.getParent(); if (tempNode instanceof Block) { Block blockWithReturn= (Block) tempNode; ListRewrite listRewriteForBlock= scratchRewriter.getListRewrite(blockWithReturn, Block.STATEMENTS_PROPERTY); List<ASTNode> statementsInBlockWithReturn= blockWithReturn.statements(); Statement lastStatementInBlock= (Statement) statementsInBlockWithReturn.get(statementsInBlockWithReturn.size() - 1); if (lastStatementInBlock instanceof ReturnStatement) { //TODO Do I need this check? listRewriteForBlock.insertBefore(taskDeclStatement, lastStatementInBlock, editGroup); } } Expression exprTemp= ((ReturnStatement) parentOfMethodCall).getExpression(); if (exprTemp instanceof InfixExpression) { fInfixExpressionFlag= true; } else if (exprTemp instanceof MethodInvocation) { fMethodInvocationFlag= true; } else System.err.println(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_scenario_error + parentOfMethodCall.toString() ); } else System.err.println(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_scenario_error + parentOfMethodCall.toString() ); } lastStatementWithRecursiveMethodInvocation.add(parentOfMethodCall); } return true; } }); try { Block blockContainingTaskDecl; ASTNode tempNode= lastStatementWithRecursiveMethodInvocation.get(0); do { tempNode= tempNode.getParent(); } while (tempNode != null && !Block.class.isInstance(tempNode)); blockContainingTaskDecl = (Block) tempNode; ListRewrite listRewriteForBlock= scratchRewriter.getListRewrite(blockContainingTaskDecl, Block.STATEMENTS_PROPERTY); MethodInvocation forkJoinInvocation= ast.newMethodInvocation(); //TODO the code below assumes that there are just two tasks passed to the // invokeAll. Need to implement the scenario with more than two tasks - COMPLETED (I think, seemed like was too easy...) forkJoinInvocation.setName(ast.newSimpleName("invokeAll")); //$NON-NLS-1$ List<Expression> argumentsForkJoin= forkJoinInvocation.arguments(); for (int i= 1; i <= taskNumber[0]; i++) { argumentsForkJoin.add(ast.newSimpleName("task" + i)); //$NON-NLS-1$ } Statement lastStatementWithRecursiveCall= lastStatementWithRecursiveMethodInvocation.get(lastStatementWithRecursiveMethodInvocation.size() - 1); if (!recursiveMethodReturnsVoid()) { if (partialComputationsNames.size() >= 1) if (lastStatementWithRecursiveCall instanceof VariableDeclarationStatement) { for (int i= partialComputationsNames.size() - 1; i >= 0 ; ) { String varStatement= typesOfComputations.get(i) + " " + partialComputationsNames.get(i) + " = task" + (i + 1) + ".result;"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ ASTNode variableStatement= scratchRewriter.createStringPlaceholder(varStatement, ASTNode.VARIABLE_DECLARATION_STATEMENT); listRewriteForBlock.insertAfter(variableStatement, lastStatementWithRecursiveCall, editGroup); i--; } } else if (lastStatementWithRecursiveCall instanceof ExpressionStatement) { for (int i= partialComputationsNames.size() - 1; i >= 0 ; ) { String varStatement= partialComputationsNames.get(i) + " = task" + (i + 1) + ".result;"; //$NON-NLS-1$ //$NON-NLS-2$ ASTNode exprStatement= scratchRewriter.createStringPlaceholder(varStatement, ASTNode.EXPRESSION_STATEMENT); listRewriteForBlock.insertAfter(exprStatement, lastStatementWithRecursiveCall, editGroup); i--; } } List<ASTNode> statementsInBlockWithTaskDecl= blockContainingTaskDecl.statements(); Statement lastStatementInBlock= (Statement) statementsInBlockWithTaskDecl.get(statementsInBlockWithTaskDecl.size() - 1); if (lastStatementInBlock instanceof ReturnStatement) { if (fInfixExpressionFlag) { Assignment assignToResult= ast.newAssignment(); assignToResult.setLeftHandSide(ast.newSimpleName("result")); //$NON-NLS-1$ InfixExpression infixExpression= ((InfixExpression)(ASTNode.copySubtree(ast, ((ReturnStatement)lastStatementInBlock).getExpression()))); int taskNum= 1; infixExpression.setLeftOperand((Expression) ast.newQualifiedName(ast.newSimpleName("task" + taskNum++), ast.newSimpleName("result"))); //$NON-NLS-1$ //$NON-NLS-2$ infixExpression.setRightOperand((Expression) ast.newQualifiedName(ast.newSimpleName("task" + taskNum++), ast.newSimpleName("result"))); //$NON-NLS-1$ //$NON-NLS-2$ List<Expression> extendedOperands = infixExpression.extendedOperands(); for (int i= 0; i < extendedOperands.size(); ) { extendedOperands.set(i, (Expression) ast.newQualifiedName(ast.newSimpleName("task" + taskNum++), ast.newSimpleName("result"))); //$NON-NLS-1$ //$NON-NLS-2$ i++; } assignToResult.setRightHandSide(infixExpression); scratchRewriter.replace(lastStatementInBlock, ast.newExpressionStatement(assignToResult), editGroup); } else if (fMethodInvocationFlag) { Assignment assignToResult= ast.newAssignment(); assignToResult.setLeftHandSide(ast.newSimpleName("result")); //$NON-NLS-1$ MethodInvocation methodInvocation= ((MethodInvocation)(ASTNode.copySubtree(ast, ((ReturnStatement)lastStatementInBlock).getExpression()))); int taskNum= 1; List<Expression> methodArguments= methodInvocation.arguments(); for (int index= 0; index < methodArguments.size(); ) { methodArguments.set(index++, (Expression) ast.newQualifiedName(ast.newSimpleName("task" + taskNum++), ast.newSimpleName("result"))); //$NON-NLS-1$ //$NON-NLS-2$ } assignToResult.setRightHandSide(methodInvocation); scratchRewriter.replace(lastStatementInBlock, ast.newExpressionStatement(assignToResult), editGroup); } else { Assignment assignToResult= ast.newAssignment(); assignToResult.setLeftHandSide(ast.newSimpleName("result")); //$NON-NLS-1$ assignToResult.setRightHandSide((Expression) ASTNode.copySubtree(ast, ((ReturnStatement) lastStatementInBlock).getExpression())); scratchRewriter.replace(lastStatementInBlock, ast.newExpressionStatement(assignToResult), editGroup); } } } if (fInfixExpressionFlag || fMethodInvocationFlag ) { listRewriteForBlock.insertBefore(ast.newExpressionStatement(forkJoinInvocation), lastStatementWithRecursiveCall, editGroup); } else { listRewriteForBlock.insertAfter(ast.newExpressionStatement(forkJoinInvocation), lastStatementWithRecursiveCall, editGroup); } TextEdit edits= scratchRewriter.rewriteAST(); IDocument scratchDocument= new Document(((ICompilationUnit)fRoot.getJavaElement()).getSource()); try { edits.apply(scratchDocument); ASTParser parser= ASTParser.newParser(AST.JLS4); parser.setSource(scratchDocument.get().toCharArray()); CompilationUnit scratchCU= (CompilationUnit)parser.createAST(null); final TypeDeclaration[] declaringClass= new TypeDeclaration[1]; scratchCU.accept(new ASTVisitor() { public boolean visit(TypeDeclaration typedecl){ if (typedecl.getName().getIdentifier().equals(fMethod.getDeclaringType().getElementName())) { declaringClass[0]= typedecl; } return true; } }); MethodDeclaration[] methodsInRefactoredClass= declaringClass[0].getMethods(); for (MethodDeclaration methodDeclaration : methodsInRefactoredClass) { if (methodDeclaration.getName().getIdentifier().equals(fMethodDeclaration.getName().getIdentifier()) && methodsHaveSameSignature(methodDeclaration,fMethodDeclaration)) { Block block= methodDeclaration.getBody(); Block copySubtree= (Block) ASTNode.copySubtree(ast, block); computeMethod.setBody(copySubtree); break; } } } catch (MalformedTreeException e) { e.printStackTrace(); } catch (BadLocationException e) { e.printStackTrace(); } } catch (JavaModelException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } recursiveActionSubtype.bodyDeclarations().add(computeMethod); } Statement findParentStatement(MethodInvocation methodCall) { Statement parentOfMethodCall= null; ASTNode tempNode= methodCall; do { tempNode= tempNode.getParent(); } while (tempNode != null && !VariableDeclarationStatement.class.isInstance(tempNode)); if (tempNode != null) { parentOfMethodCall= (VariableDeclarationStatement) tempNode; } else { tempNode= methodCall; do { tempNode= tempNode.getParent(); } while (tempNode != null && !ExpressionStatement.class.isInstance(tempNode)); if (tempNode != null) { parentOfMethodCall= (ExpressionStatement) tempNode; } else { tempNode= methodCall; do { tempNode= tempNode.getParent(); } while (tempNode != null && !ReturnStatement.class.isInstance(tempNode)); if (tempNode != null) parentOfMethodCall= (ReturnStatement) tempNode; } } return parentOfMethodCall; } private boolean methodsHaveSameSignature( MethodDeclaration methodDeclaration, MethodDeclaration methodDeclaration2) { String methodArguments= ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string; List<ASTNode> arguments= methodDeclaration.parameters(); for (Iterator<ASTNode> iterator= arguments.iterator(); iterator .hasNext();) { ASTNode argument= iterator.next(); methodArguments += argument.toString(); if (iterator.hasNext()) { methodArguments += ", "; //$NON-NLS-1$ } } String methodArguments2= ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string; arguments= methodDeclaration2.parameters(); for (Iterator<ASTNode> iterator= arguments.iterator(); iterator .hasNext();) { ASTNode argument= iterator.next(); methodArguments2 += argument.toString(); if (iterator.hasNext()) { methodArguments2 += ", "; //$NON-NLS-1$ } } return methodArguments.equals(methodArguments2); } private MethodInvocation createSequentialMethodInvocation(AST ast) { MethodInvocation invokeSequentialMethod= ast.newMethodInvocation(); invokeSequentialMethod.setName(ast.newSimpleName(fMethod.getElementName())); List<Expression> argumentsForInvokingSeqMethod= invokeSequentialMethod.arguments(); List<ASTNode> recursiveMethodParameters= fMethodDeclaration.parameters(); for (Object par : recursiveMethodParameters) { SingleVariableDeclaration parameter= (SingleVariableDeclaration) par; argumentsForInvokingSeqMethod.add(ast.newSimpleName(parameter.getName().getIdentifier())); } return invokeSequentialMethod; } private Statement identifyRecursionBaseCaseBranch(Block computeBodyBlock) { final Statement[] baseCase= new Statement[] {null}; computeBodyBlock.accept(new ASTVisitor() { public boolean visit(IfStatement ifStatement) { Statement thenStatement= ifStatement.getThenStatement(); Statement elseStatement= ifStatement.getElseStatement(); if (statementIsBaseCase(thenStatement)) baseCase[0]= thenStatement; else if ((elseStatement!= null) && (statementIsBaseCase(elseStatement))) baseCase[0]= elseStatement; return false; } private boolean statementIsBaseCase(Statement statement) { return statementEndsWithReturn(statement) && !statementContainsRecursiveCall(statement); } private boolean statementEndsWithReturn(Statement statement) { if (statement instanceof Block) { Block blockStatement= (Block) statement; List<ASTNode> statements= blockStatement.statements(); ASTNode lastStatement= statements.get(statements.size() - 1); if ( (lastStatement instanceof ReturnStatement)) { return true; } } else if (statement instanceof ReturnStatement) return true; return false; } }); return baseCase[0]; } private boolean statementContainsRecursiveCall(Statement statement) { final boolean[] result= new boolean[] {false}; statement.accept(new ASTVisitor() { public boolean visit(MethodInvocation methodCall) { IMethodBinding bindingForMethodCall= methodCall.resolveMethodBinding(); IMethodBinding bindingForMethodDeclaration= fMethodDeclaration.resolveBinding(); if (bindingForMethodCall.isEqualTo(bindingForMethodDeclaration)) { result[0]= true; } return true; } }); return result[0]; } private void createContructor(TypeDeclaration recursiveActionSubtype, AST ast) { MethodDeclaration newConstructor= ast.newMethodDeclaration(); newConstructor.setConstructor(true); newConstructor.setName(ast.newSimpleName(nameForFJTaskSubtype)); List<ASTNode> constructorParameters= newConstructor.parameters(); List<ASTNode> recursiveMethodParameters= fMethodDeclaration.parameters(); for (Object par : recursiveMethodParameters) { SingleVariableDeclaration parameter= (SingleVariableDeclaration) par; constructorParameters.add(ASTNode.copySubtree(ast, parameter)); } newConstructor.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)); Block newConstructorBlock= ast.newBlock(); newConstructor.setBody(newConstructorBlock); List<ASTNode> newConstructorStatements= newConstructorBlock.statements(); for (Object par : recursiveMethodParameters) { SingleVariableDeclaration parameter= (SingleVariableDeclaration) par; Assignment newAssignment= ast.newAssignment(); FieldAccess newFieldAccess= ast.newFieldAccess(); newFieldAccess.setExpression(ast.newThisExpression()); newFieldAccess.setName(ast.newSimpleName(parameter.getName().getIdentifier())); newAssignment.setLeftHandSide(newFieldAccess); newAssignment.setRightHandSide(ast.newSimpleName(parameter.getName().getIdentifier())); ExpressionStatement newExpressionStatement= ast.newExpressionStatement(newAssignment); newConstructorStatements.add(newExpressionStatement); } recursiveActionSubtype.bodyDeclarations().add(newConstructor); } private void createFields(TypeDeclaration recursiveActionSubtype, AST ast) { List<ASTNode> recursiveMethodParameters= fMethodDeclaration.parameters(); for (Object par : recursiveMethodParameters) { SingleVariableDeclaration parameter= (SingleVariableDeclaration) par; VariableDeclarationFragment newDeclarationFragment= ast.newVariableDeclarationFragment(); newDeclarationFragment.setName(ast.newSimpleName(parameter.getName().getIdentifier())); FieldDeclaration newFieldDeclaration= ast.newFieldDeclaration(newDeclarationFragment); newFieldDeclaration.setType((Type) ASTNode.copySubtree(ast, parameter.getType())); List<Modifier> modifiers= newFieldDeclaration.modifiers(); modifiers.add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)); recursiveActionSubtype.bodyDeclarations().add(newFieldDeclaration); } if (!recursiveMethodReturnsVoid()) { VariableDeclarationFragment newDeclarationFragment= ast.newVariableDeclarationFragment(); newDeclarationFragment.setName(ast.newSimpleName("result")); //$NON-NLS-1$ FieldDeclaration newFieldDeclaration= ast.newFieldDeclaration(newDeclarationFragment); newFieldDeclaration.setType((Type) ASTNode.copySubtree(ast, fMethodDeclaration.getReturnType2())); List<Modifier> modifiers= newFieldDeclaration.modifiers(); modifiers.add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD)); recursiveActionSubtype.bodyDeclarations().add(newFieldDeclaration); } } boolean recursiveMethodReturnsVoid() { Type returnType= fMethodDeclaration.getReturnType2(); return (returnType.isPrimitiveType() && ((PrimitiveType)returnType).getPrimitiveTypeCode().equals(PrimitiveType.VOID)); } private ChildListPropertyDescriptor getBodyDeclarationsProperty(ASTNode declaration) { if (declaration instanceof AnonymousClassDeclaration) return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY; else if (declaration instanceof AbstractTypeDeclaration) return ((AbstractTypeDeclaration) declaration).getBodyDeclarationsProperty(); Assert.isTrue(false); return null; } private void createEdits(ICompilationUnit unit, ASTRewrite rewriter, List<TextEditGroup> groups, ImportRewrite importRewrite) throws CoreException { TextChange change= fChangeManager.get(unit); MultiTextEdit root= new MultiTextEdit(); change.setEdit(root); TextEdit importEdit= importRewrite.rewriteImports(null); TextChangeCompatibility.addTextEdit(fChangeManager.get(unit), ConcurrencyRefactorings.ConvertToFJTaskRefactoring_update_imports, importEdit); root.addChild(rewriter.rewriteAST()); for (Iterator<TextEditGroup> iter= groups.iterator(); iter.hasNext();) { change.addTextEditGroup(iter.next()); } } public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { RefactoringStatus result= new RefactoringStatus(); result.merge(ChecksAndResources.checkAvailability(fMethod)); if (result.hasFatalError()) return result; ASTParser parser = ASTParser.newParser(AST.JLS4); parser.setResolveBindings(true); parser.setStatementsRecovery(false); parser.setBindingsRecovery(false); parser.setSource(fMethod.getCompilationUnit()); parser.setCompilerOptions(getCompilerOptions(fMethod.getCompilationUnit())); fRoot= (CompilationUnit) parser.createAST(pm); ISourceRange sourceRange= fMethod.getNameRange(); ASTNode node= NodeFinder.perform(fRoot, sourceRange.getOffset(), sourceRange.getLength()); if (node== null) { return mappingErrorFound(result, node); } do { node= node.getParent(); } while (node != null && !MethodDeclaration.class.isInstance(node)); fMethodDeclaration= (MethodDeclaration) node; if (fMethodDeclaration== null) { return mappingErrorFound(result, node); } if (fMethodDeclaration.resolveBinding()== null) { if (!processCompilerError(result, node)) result.addFatalError(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_type_error); return result; } fRewriter= ASTRewrite.create(fRoot.getAST()); return result; } private RefactoringStatus mappingErrorFound(RefactoringStatus result, ASTNode node) { if (node != null && (node.getFlags() & ASTNode.MALFORMED) != 0 && processCompilerError(result, node)) return result; result.addFatalError(getMappingErrorMessage()); return result; } private String getMappingErrorMessage() { return MessageFormat.format(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_analyze_error, ((Object[]) new String[] {fMethod.getElementName()})); } private boolean processCompilerError(RefactoringStatus result, ASTNode node) { Message[] messages= MessageUtil.getMessages(node, MessageUtil.INCLUDE_ALL_PARENTS); if (messages.length== 0) return false; result.addFatalError(MessageFormat.format(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_compile_error, ((Object[]) new String[] { fMethod.getElementName(), messages[0].getMessage()}))); return true; } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { String project= null; IJavaProject javaProject= fMethod.getJavaProject(); if (javaProject != null) project= javaProject.getElementName(); int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE; final IType declaring= fMethod.getDeclaringType(); try { if (declaring.isAnonymous() || declaring.isLocal()) flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; } catch (JavaModelException exception) { JavaPlugin.log(exception); } //TODO need to properly initialize the arguments so that this refactoring becomes recordable final Map<String, String> arguments= new HashMap<String, String>(); String description= ConcurrencyRefactorings.ConvertToFJTaskRefactoring_name_user; String comment= ConcurrencyRefactorings.ConvertToFJTaskRefactoring_name_user; final JavaRefactoringDescriptor descriptor= new JavaRefactoringDescriptor(IJavaRefactorings.ENCAPSULATE_FIELD, project, description, comment, arguments, flags) {}; //TODO See below as well arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fMethod)); //TODO Use JavaRefactoringDescriptor instead but it is protected arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fMethod.getElementName()); //arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1, JavaRefactoringDescriptorUtil.elementToHandle(project, nameForFJTaskSubtype)); final DynamicValidationRefactoringChange result= new DynamicValidationRefactoringChange(descriptor, getName()); TextChange[] changes= fChangeManager.getAllChanges(); pm.beginTask(NO_NAME, changes.length); pm.setTaskName(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_create_changes); for (int i= 0; i < changes.length; i++) { result.add(changes[i]); pm.worked(1); } pm.done(); return result; } @Override public String getName() { return ConcurrencyRefactorings.ConvertToFJTaskRefactoring_name_official; } public void setMethod(IMethod method) { this.fMethod= method; } public RefactoringStatus setFieldName(String text) { // TODO Auto-generated method stub return null; } public IMethod getMethod() { return fMethod; } public String getMethodName() { return fMethod.getElementName(); } public String getNameForFJTaskSubtype() { return nameForFJTaskSubtype; } public RefactoringStatus setNameForFJTaskSubtype(String nameForFJTaskSubtype) { this.nameForFJTaskSubtype= nameForFJTaskSubtype; return new RefactoringStatus(); } private boolean isIgnorableProblem(IProblem problem) { if (problem.getID()== IProblem.NotVisibleField) return true; return false; } private void checkCompileErrors(RefactoringStatus result, CompilationUnit root, ICompilationUnit element) { IProblem[] messages= root.getProblems(); for (int i= 0; i < messages.length; i++) { IProblem problem= messages[i]; if (!isIgnorableProblem(problem)) { result.addError(MessageFormat.format(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_compile_error_update, (new Object[] { element.getElementName()})), new CompilationUnitSourceContext(element, null)); return; } } } public String suggestTaskName() { String methodName= fMethod.getElementName(); return methodName.substring(0, 1).toUpperCase() + methodName.substring(1, methodName.length()) + "Impl"; //$NON-NLS-1$ } public RefactoringStatus setSequentialThreshold(String text) { if (text== null || ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string.equals(text)) return RefactoringStatus.createErrorStatus(ConcurrencyRefactorings.ConvertToFJTaskRefactoring_sequential_req); sequentialThreshold= text; return new RefactoringStatus(); } public String getMethodNameAndSignature() { String nameAndSignature= fMethodDeclaration.getName().getIdentifier() + "("; //$NON-NLS-1$ List<SingleVariableDeclaration> recursiveMethodParameters= castList(SingleVariableDeclaration.class, fMethodDeclaration.parameters()); for (Iterator<SingleVariableDeclaration> iterator= recursiveMethodParameters.iterator(); iterator .hasNext();) { SingleVariableDeclaration parameter= iterator.next(); nameAndSignature += parameter.getType() + " " + parameter.getName().getIdentifier(); //$NON-NLS-1$ if (iterator.hasNext()) nameAndSignature +=", "; //$NON-NLS-1$ } nameAndSignature += ")"; //$NON-NLS-1$ return nameAndSignature; } private <T> List<T> castList(Class<? extends T> toCastTo, List<?> c) { if (c == null || c.size() == 0) { return (List<T>) c; } else { List<T> tempList = new ArrayList<T>(c.size()); for (Object objCast: c) { tempList.add(toCastTo.cast(objCast)); } return tempList; } } private Map<String, String> getCompilerOptions(IJavaElement element) { IJavaProject project= element.getJavaProject(); Map<String, String> options= project.getOptions(true); for (Iterator<String> iter= options.keySet().iterator(); iter.hasNext();) { String key= iter.next(); String value= options.get(key); if (JavaCore.ERROR.equals(value) || JavaCore.WARNING.equals(value)) { // System.out.println("Ignoring - " + key); options.put(key, JavaCore.IGNORE); } } options.put(JavaCore.COMPILER_PB_MAX_PER_UNIT, "0"); //$NON-NLS-1$ options.put(JavaCore.COMPILER_TASK_TAGS, ConcurrencyRefactorings.ConcurrencyRefactorings_empty_string); return options; } } //TODO fix warnings