package gr.uom.java.jdeodorant.refactoring.manipulators;

import gr.uom.java.ast.ASTReader;
import gr.uom.java.ast.ClassObject;
import gr.uom.java.ast.CompilationUnitCache;
import gr.uom.java.ast.decomposition.cfg.MethodCallAnalyzer;
import gr.uom.java.ast.util.ExpressionExtractor;
import gr.uom.java.ast.util.MethodDeclarationUtility;
import gr.uom.java.ast.util.StatementExtractor;
import gr.uom.java.ast.util.TypeVisitor;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CastExpression;
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.EnumDeclaration;
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.IBinding;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.Javadoc;
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.PackageDeclaration;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
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.TagElement;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
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.CompilationUnitChange;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;

import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;

public class MoveMethodRefactoring extends Refactoring {
	
	private CompilationUnit sourceCompilationUnit;
	private CompilationUnit targetCompilationUnit;
	private TypeDeclaration sourceTypeDeclaration;
	private TypeDeclaration targetTypeDeclaration;
	private MethodDeclaration sourceMethod;
	private String targetClassVariableName;
	private Set<String> additionalArgumentsAddedToMovedMethod;
	private Set<ITypeBinding> additionalTypeBindingsToBeImportedInTargetClass;
	private Map<MethodInvocation, MethodDeclaration> additionalMethodsToBeMoved;
	private Set<FieldDeclaration> fieldDeclarationsChangedWithPublicModifier;
	private Set<BodyDeclaration> memberTypeDeclarationsChangedWithPublicModifier;
	private boolean leaveDelegate;
	private String movedMethodName;
	private boolean isTargetClassVariableParameter;
	private int targetClassVariableParameterIndex;
	private Map<ICompilationUnit, CompilationUnitChange> fChanges;
	private MultiTextEdit sourceMultiTextEdit;
	private MultiTextEdit targetMultiTextEdit;
	private CompilationUnitChange sourceCompilationUnitChange;
	private CompilationUnitChange targetCompilationUnitChange;
	
	public MoveMethodRefactoring(CompilationUnit sourceCompilationUnit, CompilationUnit targetCompilationUnit, 
			TypeDeclaration sourceTypeDeclaration, TypeDeclaration targetTypeDeclaration, MethodDeclaration sourceMethod,
			Map<MethodInvocation, MethodDeclaration> additionalMethodsToBeMoved, boolean leaveDelegate, String movedMethodName) {
		this.sourceCompilationUnit = sourceCompilationUnit;
		this.targetCompilationUnit = targetCompilationUnit;
		this.sourceTypeDeclaration = sourceTypeDeclaration;
		this.targetTypeDeclaration = targetTypeDeclaration;
		this.sourceMethod = sourceMethod;
		this.targetClassVariableName = null;
		this.additionalArgumentsAddedToMovedMethod = new LinkedHashSet<String>();
		this.additionalTypeBindingsToBeImportedInTargetClass = new LinkedHashSet<ITypeBinding>();
		this.additionalMethodsToBeMoved = additionalMethodsToBeMoved;
		this.fieldDeclarationsChangedWithPublicModifier = new LinkedHashSet<FieldDeclaration>();
		this.memberTypeDeclarationsChangedWithPublicModifier = new LinkedHashSet<BodyDeclaration>();
		this.leaveDelegate = leaveDelegate;
		this.movedMethodName = movedMethodName;
		this.isTargetClassVariableParameter = false;
		this.targetClassVariableParameterIndex = -1;
		this.fChanges = new LinkedHashMap<ICompilationUnit, CompilationUnitChange>();
		
		ICompilationUnit sourceICompilationUnit = (ICompilationUnit)sourceCompilationUnit.getJavaElement();
		this.sourceMultiTextEdit = new MultiTextEdit();
		this.sourceCompilationUnitChange = new CompilationUnitChange("", sourceICompilationUnit);
		sourceCompilationUnitChange.setEdit(sourceMultiTextEdit);
		fChanges.put(sourceICompilationUnit, sourceCompilationUnitChange);
		
		ICompilationUnit targetICompilationUnit = (ICompilationUnit)targetCompilationUnit.getJavaElement();
		if(sourceICompilationUnit.equals(targetICompilationUnit)) {
			this.targetMultiTextEdit = sourceMultiTextEdit;
			this.targetCompilationUnitChange = sourceCompilationUnitChange;
		}
		else {
			this.targetMultiTextEdit = new MultiTextEdit();
			this.targetCompilationUnitChange = new CompilationUnitChange("", targetICompilationUnit);
			targetCompilationUnitChange.setEdit(targetMultiTextEdit);
			fChanges.put(targetICompilationUnit, targetCompilationUnitChange);
		}
	}

	public Map<ICompilationUnit, CompilationUnitChange> getChanges() {
		return fChanges;
	}

	public String getMovedMethodName() {
		return movedMethodName;
	}

	public void setMovedMethodName(String movedMethodName) {
		this.movedMethodName = movedMethodName;
	}

	public void setLeaveDelegate(boolean leaveDelegate) {
		this.leaveDelegate = leaveDelegate;
	}

	public void apply() {
		createMovedMethod();
		addAdditionalMethodsToTargetClass();
		if(!sourceCompilationUnit.equals(targetCompilationUnit))
			addRequiredTargetImportDeclarations();
		
		modifyMovedMethodInvocationInSourceClass();
		if(leaveDelegate) {
			addDelegationInSourceMethod();
			removeAdditionalMethodsFromSourceClass();
		}
		else {
			//removes also additional methods used only by the moved method
			removeSourceMethod();
		}
	}

	private void addRequiredTargetImportDeclarations() {
		TypeVisitor typeVisitor = new TypeVisitor();
		sourceMethod.accept(typeVisitor);
		ImportRewrite targetImportRewrite = ImportRewrite.create(targetCompilationUnit, true);
		
		for(ITypeBinding typeBinding : typeVisitor.getTypeBindings()) {
			if(!typeBinding.isNested() || (typeBinding.isNested() && sourceTypeDeclaration.resolveBinding().isEqualTo(typeBinding.getDeclaringClass())))
				targetImportRewrite.addImport(typeBinding);
		}
		for(ITypeBinding typeBinding : additionalTypeBindingsToBeImportedInTargetClass) {
			if(!typeBinding.isNested() || (typeBinding.isNested() && sourceTypeDeclaration.resolveBinding().isEqualTo(typeBinding.getDeclaringClass())))
				targetImportRewrite.addImport(typeBinding);
		}
		
		try {
			TextEdit targetImportEdit = targetImportRewrite.rewriteImports(null);
			if(targetImportRewrite.getCreatedImports().length > 0) {
				targetMultiTextEdit.addChild(targetImportEdit);
				targetCompilationUnitChange.addTextEditGroup(new TextEditGroup("Add required import declarations", new TextEdit[] {targetImportEdit}));
			}
		}
		catch(CoreException coreException) {
			coreException.printStackTrace();
		}
	}

	private void createMovedMethod() {
		ASTRewrite targetRewriter = ASTRewrite.create(targetCompilationUnit.getAST());
		AST ast = targetTypeDeclaration.getAST();
		MethodDeclaration newMethodDeclaration = (MethodDeclaration)ASTNode.copySubtree(ast, sourceMethod);
		
		targetRewriter.set(newMethodDeclaration, MethodDeclaration.NAME_PROPERTY, ast.newSimpleName(movedMethodName), null);
		ListRewrite modifierRewrite = targetRewriter.getListRewrite(newMethodDeclaration, MethodDeclaration.MODIFIERS2_PROPERTY);
		Modifier publicModifier = newMethodDeclaration.getAST().newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
		boolean modifierFound = false;
		List<IExtendedModifier> modifiers = newMethodDeclaration.modifiers();
		for(IExtendedModifier extendedModifier : modifiers) {
			if(extendedModifier.isModifier()) {
				Modifier modifier = (Modifier)extendedModifier;
				if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD)) {
					modifierFound = true;
				}
				else if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD) ||
						modifier.getKeyword().equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD)) {
					modifierFound = true;
					modifierRewrite.replace(modifier, publicModifier, null);
				}
			}
		}
		if(!modifierFound) {
			modifierRewrite.insertFirst(publicModifier, null);
		}
		
		ListRewrite parametersRewrite = targetRewriter.getListRewrite(newMethodDeclaration, MethodDeclaration.PARAMETERS_PROPERTY);
		List<SingleVariableDeclaration> sourceMethodParameters = sourceMethod.parameters();
		List<SingleVariableDeclaration> newMethodParameters = newMethodDeclaration.parameters();
		ExpressionExtractor expressionExtractor = new ExpressionExtractor();
		List<Expression> newVariableInstructions = expressionExtractor.getVariableInstructions(newMethodDeclaration.getBody());
		
		int i = 0;
		for(SingleVariableDeclaration parameter : sourceMethodParameters) {
			ITypeBinding parameterTypeBinding = parameter.getType().resolveBinding();
			if(parameterTypeBinding.isEqualTo(targetTypeDeclaration.resolveBinding())){
				for(Expression expression : newVariableInstructions) {
					SimpleName simpleName = (SimpleName)expression;
					if(parameter.getName().getIdentifier().equals(simpleName.getIdentifier())) {
						targetClassVariableName = parameter.getName().getIdentifier();
						parametersRewrite.remove(newMethodParameters.get(i), null);
						removeParamTagElementFromJavadoc(newMethodDeclaration, targetRewriter, targetClassVariableName);
						isTargetClassVariableParameter = true;
						targetClassVariableParameterIndex = i;
						break;
					}
				}
				if(targetClassVariableName != null)
    				break;
			}
			i++;
		}
		
		FieldDeclaration[] fieldDeclarations = sourceTypeDeclaration.getFields();
		
		if(targetClassVariableName == null) {
        	for(FieldDeclaration fieldDeclaration : fieldDeclarations) {
        		List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments();
        		for(VariableDeclarationFragment fragment : fragments) {
	        		if(fieldDeclaration.getType().resolveBinding().isEqualTo(targetTypeDeclaration.resolveBinding())) {
	        			for(Expression expression : newVariableInstructions) {
	        				SimpleName simpleName = (SimpleName)expression;
	        				if(fragment.getName().getIdentifier().equals(simpleName.getIdentifier())) {
	        					targetClassVariableName = fragment.getName().getIdentifier();
	        					break;
	        				}
	        			}
	        			if(targetClassVariableName != null)
	        				break;
	        		}
        		}
        		if(targetClassVariableName != null)
    				break;
        	}
		}
		
		List<Expression> oldMethodInvocations = expressionExtractor.getMethodInvocations(sourceMethod.getBody());
		MethodDeclaration[] methodDeclarations = sourceTypeDeclaration.getMethods();
		
		if(targetClassVariableName == null) {
			for(Expression oldMethodInvocation : oldMethodInvocations) {
				if(oldMethodInvocation instanceof MethodInvocation) {
					MethodInvocation methodInvocation = (MethodInvocation)oldMethodInvocation;
					if(methodInvocation.resolveMethodBinding().getDeclaringClass().isEqualTo(sourceTypeDeclaration.resolveBinding())) {
						for(MethodDeclaration methodDeclaration : methodDeclarations) {
							if(methodInvocation.resolveMethodBinding().isEqualTo(methodDeclaration.resolveBinding())) {
								SimpleName fieldInstruction = MethodDeclarationUtility.isGetter(methodDeclaration);
								if(fieldInstruction != null && fieldInstruction.resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding())) {
									targetClassVariableName = fieldInstruction.getIdentifier();
									break;
								}
							}
						}
						if(targetClassVariableName != null)
	        				break;
					}
				}
			}
		}
		
		if(targetClassVariableName == null) {
			for(Expression oldMethodInvocation : oldMethodInvocations) {
				if(oldMethodInvocation instanceof MethodInvocation) {
					MethodInvocation methodInvocation = (MethodInvocation)oldMethodInvocation;
					if(methodInvocation.resolveMethodBinding().getDeclaringClass().isEqualTo(sourceTypeDeclaration.resolveBinding())) {
						for(MethodDeclaration methodDeclaration : methodDeclarations) {
							if(methodInvocation.resolveMethodBinding().isEqualTo(methodDeclaration.resolveBinding())) {
								MethodInvocation delegation = MethodDeclarationUtility.isDelegate(methodDeclaration);
								if(delegation != null && delegation.resolveMethodBinding().getDeclaringClass().isEqualTo(targetTypeDeclaration.resolveBinding())) {
									List<Expression> delegateMethodVariableInstructions = expressionExtractor.getVariableInstructions(methodDeclaration.getBody());
									for(Expression expression : delegateMethodVariableInstructions) {
										SimpleName fieldInstruction = (SimpleName)expression;
										IBinding fieldInstructionBinding = fieldInstruction.resolveBinding();
										if(fieldInstructionBinding != null && fieldInstructionBinding.getKind() == IBinding.VARIABLE) {
											IVariableBinding fieldInstructionVariableBinding = (IVariableBinding)fieldInstructionBinding;
											if(fieldInstructionVariableBinding.isField() && fieldInstructionVariableBinding.getType().isEqualTo(targetTypeDeclaration.resolveBinding())) {
												targetClassVariableName = fieldInstruction.getIdentifier();
												break;
											}
										}
									}
									if(targetClassVariableName != null)
				        				break;
								}
							}
						}
						if(targetClassVariableName != null)
	        				break;
					}
				}
			}
		}
		
		if(targetClassVariableName == null) {
        	for(FieldDeclaration fieldDeclaration : fieldDeclarations) {
        		List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments();
        		for(VariableDeclarationFragment fragment : fragments) {
	        		if(targetTypeDeclaration.resolveBinding().isEqualTo(fieldDeclaration.getType().resolveBinding().getSuperclass())) {
	        			for(Expression expression : newVariableInstructions) {
	        				SimpleName simpleName = (SimpleName)expression;
	        				if(fragment.getName().getIdentifier().equals(simpleName.getIdentifier())) {
	        					targetClassVariableName = fragment.getName().getIdentifier();
	        					break;
	        				}
	        			}
	        			if(targetClassVariableName != null)
	        				break;
	        		}
        		}
        		if(targetClassVariableName != null)
    				break;
        	}
		}
		
		modifySourceMemberAccessesInTargetClass(newMethodDeclaration, targetRewriter);
		if(targetClassVariableName != null) {
			modifyTargetMethodInvocations(newMethodDeclaration, targetRewriter);
			modifyTargetPublicFieldInstructions(newMethodDeclaration, targetRewriter);
		}
		modifySourceStaticFieldInstructionsInTargetClass(newMethodDeclaration, targetRewriter);
		modifySourceStaticMethodInvocationsInTargetClass(newMethodDeclaration, targetRewriter);
		modifyRecursiveMethodInvocationsOfTheMovedMethod(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInMethodInvocationArguments(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInClassInstanceCreationArguments(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInVariableDeclarationInitializers(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInInfixExpressions(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInCastExpressions(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInInstanceofExpressions(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInAssignments(newMethodDeclaration, targetRewriter);
		replaceTargetClassVariableNameWithThisExpressionInReturnStatements(newMethodDeclaration, targetRewriter);
		replaceThisExpressionWithSourceClassParameterInMethodInvocationArguments(newMethodDeclaration, targetRewriter);
		replaceThisExpressionWithSourceClassParameterInClassInstanceCreationArguments(newMethodDeclaration, targetRewriter);
		replaceThisExpressionWithSourceClassParameterInVariableDeclarationInitializers(newMethodDeclaration, targetRewriter);

		ListRewrite targetClassBodyRewrite = targetRewriter.getListRewrite(targetTypeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
		targetClassBodyRewrite.insertLast(newMethodDeclaration, null);
		
		try {
			TextEdit targetEdit = targetRewriter.rewriteAST();
			targetMultiTextEdit.addChild(targetEdit);
			targetCompilationUnitChange.addTextEditGroup(new TextEditGroup("Add moved method", new TextEdit[] {targetEdit}));
		}
		catch(JavaModelException javaModelException) {
			javaModelException.printStackTrace();
		}
	}

	private void addAdditionalMethodsToTargetClass() {
		AST ast = targetTypeDeclaration.getAST();
		Set<MethodDeclaration> methodsToBeMoved = new LinkedHashSet<MethodDeclaration>(additionalMethodsToBeMoved.values());
		for(MethodDeclaration methodDeclaration : methodsToBeMoved) {
			TypeVisitor typeVisitor = new TypeVisitor();
			methodDeclaration.accept(typeVisitor);
			for(ITypeBinding typeBinding : typeVisitor.getTypeBindings()) {
				this.additionalTypeBindingsToBeImportedInTargetClass.add(typeBinding);
			}
			MethodDeclaration newMethodDeclaration = (MethodDeclaration)ASTNode.copySubtree(ast, methodDeclaration);
			ASTRewrite targetRewriter = ASTRewrite.create(targetCompilationUnit.getAST());
			ListRewrite targetClassBodyRewrite = targetRewriter.getListRewrite(targetTypeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
			targetClassBodyRewrite.insertLast(newMethodDeclaration, null);
			try {
				TextEdit targetEdit = targetRewriter.rewriteAST();
				targetMultiTextEdit.addChild(targetEdit);
				targetCompilationUnitChange.addTextEditGroup(new TextEditGroup("Add additional moved method", new TextEdit[] {targetEdit}));
			}
			catch(JavaModelException javaModelException) {
				javaModelException.printStackTrace();
			}
		}
	}

	private void removeAdditionalMethodsFromSourceClass() {
		Set<MethodDeclaration> methodsToBeMoved = new LinkedHashSet<MethodDeclaration>(additionalMethodsToBeMoved.values());
		for(MethodDeclaration methodDeclaration : methodsToBeMoved) {
			ASTRewrite sourceRewriter = ASTRewrite.create(sourceCompilationUnit.getAST());
			ListRewrite sourceClassBodyRewrite = sourceRewriter.getListRewrite(sourceTypeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
			sourceClassBodyRewrite.remove(methodDeclaration, null);
			try {
				TextEdit sourceEdit = sourceRewriter.rewriteAST();
				sourceMultiTextEdit.addChild(sourceEdit);
				sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Remove additional moved method", new TextEdit[] {sourceEdit}));
			}
			catch(JavaModelException javaModelException) {
				javaModelException.printStackTrace();
			}
		}
	}

	private void removeSourceMethod() {
		ASTRewrite sourceRewriter = ASTRewrite.create(sourceCompilationUnit.getAST());
		ListRewrite classBodyRewrite = sourceRewriter.getListRewrite(sourceTypeDeclaration, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
		classBodyRewrite.remove(sourceMethod, null);
		Set<MethodDeclaration> methodsToBeMoved = new LinkedHashSet<MethodDeclaration>(additionalMethodsToBeMoved.values());
		for(MethodDeclaration methodDeclaration : methodsToBeMoved) {
			classBodyRewrite.remove(methodDeclaration, null);
		}
		try {
			TextEdit sourceEdit = sourceRewriter.rewriteAST();
			sourceMultiTextEdit.addChild(sourceEdit);
			sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Remove moved method", new TextEdit[] {sourceEdit}));
		}
		catch(JavaModelException javaModelException) {
			javaModelException.printStackTrace();
		}
	}

	private void addDelegationInSourceMethod() {
		List<SingleVariableDeclaration> sourceMethodParameters = sourceMethod.parameters();
		String targetClassVariableName = null;
		for(SingleVariableDeclaration parameter : sourceMethodParameters) {
			ITypeBinding parameterTypeBinding = parameter.getType().resolveBinding();
			if(parameterTypeBinding.isEqualTo(targetTypeDeclaration.resolveBinding()) &&
					parameter.getName().getIdentifier().equals(this.targetClassVariableName)) {
				targetClassVariableName = parameter.getName().getIdentifier();
				break;
			}
		}
		FieldDeclaration[] fieldDeclarations = sourceTypeDeclaration.getFields();
		if(targetClassVariableName == null) {
        	for(FieldDeclaration fieldDeclaration : fieldDeclarations) {
        		List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments();
        		for(VariableDeclarationFragment fragment : fragments) {
	        		if(fieldDeclaration.getType().resolveBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) &&
	        				fragment.getName().getIdentifier().equals(this.targetClassVariableName)) {
	        			targetClassVariableName = fragment.getName().getIdentifier();
	        			break;
	        		}
        		}
        	}
		}
		if(targetClassVariableName == null) {
        	for(FieldDeclaration fieldDeclaration : fieldDeclarations) {
        		List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments();
        		for(VariableDeclarationFragment fragment : fragments) {
	        		if(targetTypeDeclaration.resolveBinding().isEqualTo(fieldDeclaration.getType().resolveBinding().getSuperclass()) &&
	        				fragment.getName().getIdentifier().equals(this.targetClassVariableName)) {
	        			targetClassVariableName = fragment.getName().getIdentifier();
	        			break;
	        		}
        		}
        	}
		}
		
		ASTRewrite sourceRewriter = ASTRewrite.create(sourceCompilationUnit.getAST());
		ListRewrite methodBodyRewrite = sourceRewriter.getListRewrite(sourceMethod.getBody(), Block.STATEMENTS_PROPERTY);
		List<Statement> sourceMethodStatements = sourceMethod.getBody().statements();
		for(Statement statement : sourceMethodStatements) {
			methodBodyRewrite.remove(statement, null);
		}
		
		Type sourceMethodReturnType = sourceMethod.getReturnType2();
		ITypeBinding sourceMethodReturnTypeBinding = sourceMethodReturnType.resolveBinding();
		AST ast = sourceMethod.getBody().getAST();
		MethodInvocation delegation = ast.newMethodInvocation();
		sourceRewriter.set(delegation, MethodInvocation.NAME_PROPERTY, ast.newSimpleName(movedMethodName), null);
		SimpleName expressionName = ast.newSimpleName(targetClassVariableName);
		sourceRewriter.set(delegation, MethodInvocation.EXPRESSION_PROPERTY, expressionName, null);
		
		ListRewrite argumentRewrite = sourceRewriter.getListRewrite(delegation, MethodInvocation.ARGUMENTS_PROPERTY);
		for(SingleVariableDeclaration parameter : sourceMethodParameters) {
			if(!targetClassVariableName.equals(parameter.getName().getIdentifier())) {
				SimpleName argumentName = ast.newSimpleName(parameter.getName().getIdentifier());
				argumentRewrite.insertLast(argumentName, null);
			}
		}
		for(String argument : additionalArgumentsAddedToMovedMethod) {
			if(argument.equals("this"))
				argumentRewrite.insertLast(ast.newThisExpression(), null);
			else
				argumentRewrite.insertLast(ast.newSimpleName(argument), null);
		}
		if(sourceMethodReturnTypeBinding.getName().equals("void")) {
			ExpressionStatement expressionStatement = ast.newExpressionStatement(delegation);
			methodBodyRewrite.insertLast(expressionStatement, null);
		}
		else {
			ReturnStatement returnStatement = ast.newReturnStatement();
			sourceRewriter.set(returnStatement, ReturnStatement.EXPRESSION_PROPERTY, delegation, null);
			methodBodyRewrite.insertLast(returnStatement, null);
		}
		
		try {
			TextEdit sourceEdit = sourceRewriter.rewriteAST();
			sourceMultiTextEdit.addChild(sourceEdit);
			sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Leave delegate to moved method", new TextEdit[] {sourceEdit}));
		}
		catch(JavaModelException javaModelException) {
			javaModelException.printStackTrace();
		}
	}

	private void modifyMovedMethodInvocationInSourceClass() {
		ExpressionExtractor expressionExtractor = new ExpressionExtractor();
		MethodDeclaration[] methodDeclarations = sourceTypeDeclaration.getMethods();
		for(MethodDeclaration methodDeclaration : methodDeclarations) {
			if(!sourceMethod.equals(methodDeclaration)) {
				Block methodBody = methodDeclaration.getBody();
				if(methodBody != null) {
					List<Statement> statements = methodBody.statements();
					for(Statement statement : statements) {
						List<Expression> methodInvocations = expressionExtractor.getMethodInvocations(statement);
						for(Expression expression : methodInvocations) {
							if(expression instanceof MethodInvocation) {
								MethodInvocation methodInvocation = (MethodInvocation)expression;
								if(sourceMethod.resolveBinding().isEqualTo(methodInvocation.resolveMethodBinding())) {
									ASTRewrite sourceRewriter = ASTRewrite.create(sourceCompilationUnit.getAST());
									AST ast = methodInvocation.getAST();
									sourceRewriter.set(methodInvocation, MethodInvocation.NAME_PROPERTY, ast.newSimpleName(movedMethodName), null);
									List<Expression> arguments = methodInvocation.arguments();
									boolean foundInArguments = false;
									for(Expression argument : arguments) {
										if(argument.resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding())) {
											foundInArguments = true;
											ListRewrite argumentRewrite = sourceRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
											argumentRewrite.remove(argument, null);
											if(argument instanceof CastExpression) {
												ParenthesizedExpression parenthesizedExpression = ast.newParenthesizedExpression();
												sourceRewriter.set(parenthesizedExpression, ParenthesizedExpression.EXPRESSION_PROPERTY, argument, null);
												sourceRewriter.set(methodInvocation, MethodInvocation.EXPRESSION_PROPERTY, parenthesizedExpression, null);
											}
											else {
												sourceRewriter.set(methodInvocation, MethodInvocation.EXPRESSION_PROPERTY, argument, null);
											}
											break;
										}
									}
									if(!foundInArguments && isTargetClassVariableParameter) {
										for(Expression argument : arguments) {
											if(targetTypeDeclaration.resolveBinding().isEqualTo(argument.resolveTypeBinding().getSuperclass())) {
												foundInArguments = true;
												ListRewrite argumentRewrite = sourceRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
												argumentRewrite.remove(argument, null);
												sourceRewriter.set(methodInvocation, MethodInvocation.EXPRESSION_PROPERTY, argument, null);
												break;
											}
										}
									}
									boolean foundInFields = false;
									if(!foundInArguments) {
										FieldDeclaration[] fieldDeclarations = sourceTypeDeclaration.getFields();
										for(FieldDeclaration fieldDeclaration : fieldDeclarations) {
											List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments();
											for(VariableDeclarationFragment fragment : fragments) {
												if(fieldDeclaration.getType().resolveBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) &&
														fragment.getName().getIdentifier().equals(targetClassVariableName)) {
													foundInFields = true;
													sourceRewriter.set(methodInvocation, MethodInvocation.EXPRESSION_PROPERTY, fragment.getName(), null);
													break;
												}
											}
										}
									}
									if(!foundInArguments && !foundInFields) {
										FieldDeclaration[] fieldDeclarations = sourceTypeDeclaration.getFields();
										for(FieldDeclaration fieldDeclaration : fieldDeclarations) {
											List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments();
											for(VariableDeclarationFragment fragment : fragments) {
												if(targetTypeDeclaration.resolveBinding().isEqualTo(fieldDeclaration.getType().resolveBinding().getSuperclass()) &&
														fragment.getName().getIdentifier().equals(targetClassVariableName)) {
													sourceRewriter.set(methodInvocation, MethodInvocation.EXPRESSION_PROPERTY, fragment.getName(), null);
													break;
												}
											}
										}
									}
									ListRewrite argumentRewrite = sourceRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
									for(String argument : additionalArgumentsAddedToMovedMethod) {
										if(argument.equals("this"))
											argumentRewrite.insertLast(ast.newThisExpression(), null);
										else
											argumentRewrite.insertLast(ast.newSimpleName(argument), null);
									}

									try {
										TextEdit sourceEdit = sourceRewriter.rewriteAST();
										sourceMultiTextEdit.addChild(sourceEdit);
										sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Change invocation of moved method", new TextEdit[] {sourceEdit}));
									}
									catch(JavaModelException javaModelException) {
										javaModelException.printStackTrace();
									}
								}
							}
						}
					}
				}
			}
		}
	}

	private void modifyTargetMethodInvocations(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> sourceMethodInvocations = extractor.getMethodInvocations(sourceMethod.getBody());
		List<Expression> newMethodInvocations = extractor.getMethodInvocations(newMethodDeclaration.getBody());
		int i = 0;
		for(Expression expression : sourceMethodInvocations) {
			if(expression instanceof MethodInvocation) {
				MethodInvocation methodInvocation = (MethodInvocation)expression;
				Expression methodInvocationExpression = methodInvocation.getExpression();
				if(methodInvocationExpression instanceof SimpleName) {
					SimpleName methodInvocationExpressionSimpleName = (SimpleName)methodInvocationExpression;
					if( (methodInvocationExpressionSimpleName.resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) ||
							targetTypeDeclaration.resolveBinding().isEqualTo(methodInvocationExpressionSimpleName.resolveTypeBinding().getSuperclass()) ) &&
							methodInvocationExpressionSimpleName.getIdentifier().equals(targetClassVariableName)) {
						MethodInvocation newMethodInvocation = (MethodInvocation)newMethodInvocations.get(i);
						targetRewriter.remove(newMethodInvocation.getExpression(), null);
					}
				}
				else if(methodInvocationExpression instanceof FieldAccess) {
					FieldAccess methodInvocationExpressionFieldAccess = (FieldAccess)methodInvocationExpression;
					if( (methodInvocationExpressionFieldAccess.getName().resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) ||
							targetTypeDeclaration.resolveBinding().isEqualTo(methodInvocationExpressionFieldAccess.getName().resolveTypeBinding().getSuperclass()) ) &&
							methodInvocationExpressionFieldAccess.getName().getIdentifier().equals(targetClassVariableName)) {
						MethodInvocation newMethodInvocation = (MethodInvocation)newMethodInvocations.get(i);
						targetRewriter.remove(newMethodInvocation.getExpression(), null);
					}
				}
			}
			i++;
		}
	}

	private void modifyTargetPublicFieldInstructions(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> sourceFieldInstructions = extractor.getVariableInstructions(sourceMethod.getBody());
		List<Expression> newFieldInstructions = extractor.getVariableInstructions(newMethodDeclaration.getBody());
		FieldDeclaration[] fields = targetTypeDeclaration.getFields();
		for(FieldDeclaration field : fields) {
			List<VariableDeclarationFragment> fragments = field.fragments();
			for(VariableDeclarationFragment fragment : fragments) {
				SimpleName fragmentName = fragment.getName();
				int i = 0;
				for(Expression expression : sourceFieldInstructions) {
					SimpleName simpleName = (SimpleName)expression;
					if(simpleName.getParent() instanceof QualifiedName && fragmentName.getIdentifier().equals(simpleName.getIdentifier())) {
						QualifiedName qualifiedName = (QualifiedName)simpleName.getParent();
						if( (qualifiedName.getQualifier().resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) ||
								targetTypeDeclaration.resolveBinding().isEqualTo(qualifiedName.getQualifier().resolveTypeBinding().getSuperclass()) ) &&
								qualifiedName.getQualifier().getFullyQualifiedName().equals(targetClassVariableName)) {
							SimpleName newSimpleName = (SimpleName)newFieldInstructions.get(i);
							FieldAccess fieldAccess = newMethodDeclaration.getAST().newFieldAccess();
							targetRewriter.set(fieldAccess, FieldAccess.NAME_PROPERTY, simpleName, null);
							targetRewriter.set(fieldAccess, FieldAccess.EXPRESSION_PROPERTY, newMethodDeclaration.getAST().newThisExpression(), null);
							targetRewriter.replace(newSimpleName.getParent(), fieldAccess, null);
						}
					}
					else if(simpleName.getParent() instanceof FieldAccess && fragmentName.getIdentifier().equals(simpleName.getIdentifier())) {
						FieldAccess fieldAccess = (FieldAccess)simpleName.getParent();
						Expression fieldAccessExpression = fieldAccess.getExpression();
						if(fieldAccessExpression instanceof FieldAccess) {
							FieldAccess invokerFieldAccess = (FieldAccess)fieldAccessExpression;
							if( (invokerFieldAccess.resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) ||
									targetTypeDeclaration.resolveBinding().isEqualTo(invokerFieldAccess.resolveTypeBinding().getSuperclass())) &&
									invokerFieldAccess.getName().getIdentifier().equals(targetClassVariableName) && invokerFieldAccess.getExpression() instanceof ThisExpression) {
								SimpleName newSimpleName = (SimpleName)newFieldInstructions.get(i);
								FieldAccess newFieldAccess = (FieldAccess)newSimpleName.getParent();
								targetRewriter.replace(newFieldAccess.getExpression(), newMethodDeclaration.getAST().newThisExpression(), null);
							}
						}
					}
					i++;
				}
			}
		}
		if(!targetTypeDeclaration.resolveBinding().getSuperclass().getQualifiedName().equals("java.lang.Object")) {
			IVariableBinding[] superclassFields = targetTypeDeclaration.resolveBinding().getSuperclass().getDeclaredFields();
			for(IVariableBinding superclassField : superclassFields) {
				int i = 0;
				for(Expression expression : sourceFieldInstructions) {
					SimpleName simpleName = (SimpleName)expression;
					if(simpleName.getParent() instanceof QualifiedName && superclassField.isEqualTo(simpleName.resolveBinding())) {
						QualifiedName qualifiedName = (QualifiedName)simpleName.getParent();
						if(qualifiedName.getQualifier().resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding().getSuperclass()) &&
								qualifiedName.getQualifier().getFullyQualifiedName().equals(targetClassVariableName)) {
							SimpleName newSimpleName = (SimpleName)newFieldInstructions.get(i);
							FieldAccess fieldAccess = newMethodDeclaration.getAST().newFieldAccess();
							targetRewriter.set(fieldAccess, FieldAccess.NAME_PROPERTY, simpleName, null);
							targetRewriter.set(fieldAccess, FieldAccess.EXPRESSION_PROPERTY, newMethodDeclaration.getAST().newThisExpression(), null);
							targetRewriter.replace(newSimpleName.getParent(), fieldAccess, null);
						}
					}
					else if(simpleName.getParent() instanceof FieldAccess && superclassField.isEqualTo(simpleName.resolveBinding())) {
						FieldAccess fieldAccess = (FieldAccess)simpleName.getParent();
						Expression fieldAccessExpression = fieldAccess.getExpression();
						if(fieldAccessExpression instanceof FieldAccess) {
							FieldAccess invokerFieldAccess = (FieldAccess)fieldAccessExpression;
							if(invokerFieldAccess.resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) &&
									invokerFieldAccess.getName().getIdentifier().equals(targetClassVariableName) && invokerFieldAccess.getExpression() instanceof ThisExpression) {
								SimpleName newSimpleName = (SimpleName)newFieldInstructions.get(i);
								FieldAccess newFieldAccess = (FieldAccess)newSimpleName.getParent();
								targetRewriter.replace(newFieldAccess.getExpression(), newMethodDeclaration.getAST().newThisExpression(), null);
							}
						}
					}
					i++;
				}
			}
		}
	}
	
	private void modifySourceStaticFieldInstructionsInTargetClass(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> sourceVariableInstructions = extractor.getVariableInstructions(sourceMethod.getBody());
		List<Expression> newVariableInstructions = extractor.getVariableInstructions(newMethodDeclaration.getBody());
		int i = 0;
		for(Expression expression : sourceVariableInstructions) {
			SimpleName simpleName = (SimpleName)expression;
			IBinding binding = simpleName.resolveBinding();
			if(binding != null && binding.getKind() == IBinding.VARIABLE) {
				IVariableBinding variableBinding = (IVariableBinding)binding;
				if(variableBinding.isField() && (variableBinding.getModifiers() & Modifier.STATIC) != 0) {
					if(sourceTypeDeclaration.resolveBinding().isEqualTo(variableBinding.getDeclaringClass())) {
						AST ast = newMethodDeclaration.getAST();
						SimpleName qualifier = ast.newSimpleName(sourceTypeDeclaration.getName().getIdentifier());
						if(simpleName.getParent() instanceof FieldAccess) {
							FieldAccess fieldAccess = (FieldAccess)newVariableInstructions.get(i).getParent();
							targetRewriter.set(fieldAccess, FieldAccess.EXPRESSION_PROPERTY, qualifier, null);
						}
						else if(RefactoringUtility.needsQualifier(simpleName)) {
							SimpleName newSimpleName = ast.newSimpleName(simpleName.getIdentifier());
							QualifiedName newQualifiedName = ast.newQualifiedName(qualifier, newSimpleName);
							targetRewriter.replace(newVariableInstructions.get(i), newQualifiedName, null);
						}
						this.additionalTypeBindingsToBeImportedInTargetClass.add(sourceTypeDeclaration.resolveBinding());
						setPublicModifierToSourceField(variableBinding);
					}
					else {
						AST ast = newMethodDeclaration.getAST();
						SimpleName qualifier = null;
						if((variableBinding.getModifiers() & Modifier.PUBLIC) != 0) {
							qualifier = ast.newSimpleName(variableBinding.getDeclaringClass().getName());
							this.additionalTypeBindingsToBeImportedInTargetClass.add(variableBinding.getDeclaringClass());
						}
						else {
							qualifier = ast.newSimpleName(sourceTypeDeclaration.getName().getIdentifier());
							this.additionalTypeBindingsToBeImportedInTargetClass.add(sourceTypeDeclaration.resolveBinding());
						}
						if(simpleName.getParent() instanceof FieldAccess) {
							FieldAccess fieldAccess = (FieldAccess)newVariableInstructions.get(i).getParent();
							targetRewriter.set(fieldAccess, FieldAccess.EXPRESSION_PROPERTY, qualifier, null);
						}
						else if(RefactoringUtility.needsQualifier(simpleName)) {
							SimpleName newSimpleName = ast.newSimpleName(simpleName.getIdentifier());
							QualifiedName newQualifiedName = ast.newQualifiedName(qualifier, newSimpleName);
							targetRewriter.replace(newVariableInstructions.get(i), newQualifiedName, null);
						}
						ITypeBinding fieldDeclaringClass = variableBinding.getDeclaringClass();
						if(fieldDeclaringClass != null && fieldDeclaringClass.isEnum() && sourceTypeDeclaration.resolveBinding().isEqualTo(fieldDeclaringClass.getDeclaringClass())) {
							setPublicModifierToSourceMemberType(fieldDeclaringClass);
						}
					}
				}
			}
			i++;
		}
	}

	private void setPublicModifierToSourceMemberType(ITypeBinding typeBinding) {
		List<BodyDeclaration> bodyDeclarations = sourceTypeDeclaration.bodyDeclarations();
		for(BodyDeclaration bodyDeclaration : bodyDeclarations) {
			if(bodyDeclaration instanceof TypeDeclaration) {
				TypeDeclaration memberType = (TypeDeclaration)bodyDeclaration;
				ITypeBinding memberTypeBinding = memberType.resolveBinding();
				if(typeBinding.isEqualTo(memberTypeBinding)) {
					updateBodyDeclarationAccessModifier(memberType, TypeDeclaration.MODIFIERS2_PROPERTY);
				}
			}
			else if(bodyDeclaration instanceof EnumDeclaration) {
				EnumDeclaration memberEnum = (EnumDeclaration)bodyDeclaration;
				ITypeBinding memberTypeBinding = memberEnum.resolveBinding();
				if(typeBinding.isEqualTo(memberTypeBinding)) {
					updateBodyDeclarationAccessModifier(memberEnum, EnumDeclaration.MODIFIERS2_PROPERTY);
				}
			}
		}
	}

	private void updateBodyDeclarationAccessModifier(BodyDeclaration memberType, ChildListPropertyDescriptor childListPropertyDescriptor) {
		ASTRewrite sourceRewriter = ASTRewrite.create(sourceTypeDeclaration.getAST());
		ListRewrite modifierRewrite = sourceRewriter.getListRewrite(memberType, childListPropertyDescriptor);
		Modifier publicModifier = memberType.getAST().newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
		boolean modifierFound = false;
		List<IExtendedModifier> modifiers = memberType.modifiers();
		for(IExtendedModifier extendedModifier : modifiers) {
			if(extendedModifier.isModifier()) {
				Modifier modifier = (Modifier)extendedModifier;
				if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD)) {
					modifierFound = true;
				}
				else if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD)) {
					if(!memberTypeDeclarationsChangedWithPublicModifier.contains(memberType)) {
						memberTypeDeclarationsChangedWithPublicModifier.add(memberType);
						modifierFound = true;
						modifierRewrite.replace(modifier, publicModifier, null);
						try {
							TextEdit sourceEdit = sourceRewriter.rewriteAST();
							sourceMultiTextEdit.addChild(sourceEdit);
							sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Change access level to public", new TextEdit[] {sourceEdit}));
						} catch (JavaModelException e) {
							e.printStackTrace();
						}
					}
				}
				else if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD)) {
					modifierFound = true;
				}
			}
		}
		if(!modifierFound) {
			if(!memberTypeDeclarationsChangedWithPublicModifier.contains(memberType)) {
				memberTypeDeclarationsChangedWithPublicModifier.add(memberType);
				modifierRewrite.insertFirst(publicModifier, null);
				try {
					TextEdit sourceEdit = sourceRewriter.rewriteAST();
					sourceMultiTextEdit.addChild(sourceEdit);
					sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Set access level to public", new TextEdit[] {sourceEdit}));
				} catch (JavaModelException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void setPublicModifierToSourceField(IVariableBinding variableBinding) {
		FieldDeclaration[] fieldDeclarations = sourceTypeDeclaration.getFields();
		for(FieldDeclaration fieldDeclaration : fieldDeclarations) {
			List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments();
			for(VariableDeclarationFragment fragment : fragments) {
				boolean modifierIsReplaced = false;
				if(variableBinding.isEqualTo(fragment.resolveBinding())) {
					ASTRewrite sourceRewriter = ASTRewrite.create(sourceTypeDeclaration.getAST());
					ListRewrite modifierRewrite = sourceRewriter.getListRewrite(fieldDeclaration, FieldDeclaration.MODIFIERS2_PROPERTY);
					Modifier publicModifier = fieldDeclaration.getAST().newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
					boolean modifierFound = false;
					List<IExtendedModifier> modifiers = fieldDeclaration.modifiers();
					for(IExtendedModifier extendedModifier : modifiers) {
						if(extendedModifier.isModifier()) {
							Modifier modifier = (Modifier)extendedModifier;
							if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD)) {
								modifierFound = true;
							}
							else if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD) ||
									modifier.getKeyword().equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD)) {
								if(!fieldDeclarationsChangedWithPublicModifier.contains(fieldDeclaration)) {
									fieldDeclarationsChangedWithPublicModifier.add(fieldDeclaration);
									modifierFound = true;
									modifierRewrite.replace(modifier, publicModifier, null);
									modifierIsReplaced = true;
									try {
										TextEdit sourceEdit = sourceRewriter.rewriteAST();
										sourceMultiTextEdit.addChild(sourceEdit);
										sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Change access level to public", new TextEdit[] {sourceEdit}));
									} catch (JavaModelException e) {
										e.printStackTrace();
									}
								}
							}
						}
					}
					if(!modifierFound) {
						if(!fieldDeclarationsChangedWithPublicModifier.contains(fieldDeclaration)) {
							fieldDeclarationsChangedWithPublicModifier.add(fieldDeclaration);
							modifierRewrite.insertFirst(publicModifier, null);
							modifierIsReplaced = true;
							try {
								TextEdit sourceEdit = sourceRewriter.rewriteAST();
								sourceMultiTextEdit.addChild(sourceEdit);
								sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Set access level to public", new TextEdit[] {sourceEdit}));
							} catch (JavaModelException e) {
								e.printStackTrace();
							}
						}
					}
				}
				if(modifierIsReplaced)
					break;
			}
		}
	}

	private void modifySourceStaticMethodInvocationsInTargetClass(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();	
		List<Expression> sourceMethodInvocations = extractor.getMethodInvocations(sourceMethod.getBody());
		List<Expression> newMethodInvocations = extractor.getMethodInvocations(newMethodDeclaration.getBody());
		int i = 0;
		for(Expression expression : sourceMethodInvocations) {
			if(expression instanceof MethodInvocation) {
				MethodInvocation methodInvocation = (MethodInvocation)expression;
				IMethodBinding methodBinding = methodInvocation.resolveMethodBinding();
				if((methodBinding.getModifiers() & Modifier.STATIC) != 0 &&
						methodBinding.getDeclaringClass().isEqualTo(sourceTypeDeclaration.resolveBinding()) &&
						!additionalMethodsToBeMoved.containsKey(methodInvocation)) {
					AST ast = newMethodDeclaration.getAST();
					SimpleName qualifier = ast.newSimpleName(sourceTypeDeclaration.getName().getIdentifier());
					targetRewriter.set(newMethodInvocations.get(i), MethodInvocation.EXPRESSION_PROPERTY, qualifier, null);
					this.additionalTypeBindingsToBeImportedInTargetClass.add(sourceTypeDeclaration.resolveBinding());
				}
			}
			i++;
		}
	}

	private void modifySourceMemberAccessesInTargetClass(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();	
		List<Expression> sourceMethodInvocations = extractor.getMethodInvocations(sourceMethod.getBody());
		List<Expression> newMethodInvocations = extractor.getMethodInvocations(newMethodDeclaration.getBody());
		List<Expression> expressionsToBeRemoved = new ArrayList<Expression>();
		for(MethodInvocation methodInvocation : additionalMethodsToBeMoved.keySet()) {
			for(Expression expression : sourceMethodInvocations) {
				if(expression instanceof MethodInvocation) {
					MethodInvocation sourceMethodInvocation = (MethodInvocation)expression;
					if(methodInvocation.equals(sourceMethodInvocation)) {
						expressionsToBeRemoved.add(methodInvocation);
					}
				}
			}
		}
		for(Expression expression : expressionsToBeRemoved) {
			int index = sourceMethodInvocations.indexOf(expression);
			sourceMethodInvocations.remove(index);
			newMethodInvocations.remove(index);
		}
		expressionsToBeRemoved.clear();
		int k = 0;
		for(Expression expression : sourceMethodInvocations) {
			if(expression instanceof MethodInvocation) {
				MethodInvocation methodInvocation = (MethodInvocation)expression;
				ITypeBinding methodInvocationDeclaringClassTypeBinding = methodInvocation.resolveMethodBinding().getDeclaringClass();
				if(methodInvocationDeclaringClassTypeBinding.isEqualTo(sourceTypeDeclaration.resolveBinding()) &&
						(methodInvocation.getExpression() == null || methodInvocation.getExpression() instanceof ThisExpression)) {
					MethodDeclaration[] sourceMethodDeclarations = sourceTypeDeclaration.getMethods();
					for(MethodDeclaration sourceMethodDeclaration : sourceMethodDeclarations) {
						if(sourceMethodDeclaration.resolveBinding().isEqualTo(methodInvocation.resolveMethodBinding())) {
							MethodInvocation delegation = MethodDeclarationUtility.isDelegate(sourceMethodDeclaration);
							if(delegation != null) {
								ITypeBinding delegationDeclaringClassTypeBinding = delegation.resolveMethodBinding().getDeclaringClass();
								if(delegationDeclaringClassTypeBinding.isEqualTo(targetTypeDeclaration.resolveBinding())) {
									if(delegation.getExpression() != null) {
										MethodInvocation newMethodInvocation = (MethodInvocation)ASTNode.copySubtree(newMethodDeclaration.getAST(), delegation);
										targetRewriter.remove(newMethodInvocation.getExpression(), null);
										targetRewriter.replace(newMethodInvocations.get(k), newMethodInvocation, null);
									}
									expressionsToBeRemoved.add(methodInvocation);
								}
							}
						}
					}
				}
				else if(methodInvocationDeclaringClassTypeBinding.isEqualTo(targetTypeDeclaration.resolveBinding()) && methodInvocation.getExpression() != null) {
					Expression methodInvocationExpression = methodInvocation.getExpression();
					if(methodInvocationExpression instanceof MethodInvocation) {
						MethodInvocation invoker = (MethodInvocation)methodInvocationExpression;
						if(invoker.getExpression() == null || invoker.getExpression() instanceof ThisExpression) {
							MethodDeclaration[] sourceMethodDeclarations = sourceTypeDeclaration.getMethods();
							for(MethodDeclaration sourceMethodDeclaration : sourceMethodDeclarations) {
								if(sourceMethodDeclaration.resolveBinding().isEqualTo(invoker.resolveMethodBinding())) {
									SimpleName fieldInstruction = MethodDeclarationUtility.isGetter(sourceMethodDeclaration);
									if(fieldInstruction != null && fieldInstruction.resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding())) {
										int index = sourceMethodInvocations.indexOf(invoker);
										targetRewriter.remove(newMethodInvocations.get(index), null);
										expressionsToBeRemoved.add(invoker);
										expressionsToBeRemoved.add(methodInvocation);
									}
								}
							}
						}
					}
				}
			}
			k++;
		}
		for(Expression expression : expressionsToBeRemoved) {
			int index = sourceMethodInvocations.indexOf(expression);
			sourceMethodInvocations.remove(index);
			newMethodInvocations.remove(index);
		}
		
		List<Expression> sourceFieldInstructions = extractor.getVariableInstructions(sourceMethod.getBody());
		List<Expression> newFieldInstructions = extractor.getVariableInstructions(newMethodDeclaration.getBody());
		
		int i = 0;
		for(Expression expression : sourceFieldInstructions) {
			SimpleName simpleName = (SimpleName)expression;
			IBinding binding = simpleName.resolveBinding();
			if(binding != null && binding.getKind() == IBinding.VARIABLE) {
				IVariableBinding variableBinding = (IVariableBinding)binding;
				if(variableBinding.isField() && (variableBinding.getModifiers() & Modifier.STATIC) == 0) {
					if(sourceTypeDeclaration.resolveBinding().isEqualTo(variableBinding.getDeclaringClass())) {
						SimpleName expressionName = (SimpleName)newFieldInstructions.get(i);
						if(expressionName.getParent() instanceof FieldAccess) {
							FieldAccess fieldAccess = (FieldAccess)expressionName.getParent();
							if(fieldAccess.getExpression() instanceof ThisExpression && !expressionName.getIdentifier().equals(targetClassVariableName)) {
								targetRewriter.replace(expressionName.getParent(), expressionName, null);
								if(!additionalArgumentsAddedToMovedMethod.contains(expressionName.getIdentifier()))
									addParameterToMovedMethod(newMethodDeclaration, expressionName, targetRewriter);
							}
						}
						else if(!expressionName.getIdentifier().equals(targetClassVariableName) && !additionalArgumentsAddedToMovedMethod.contains(expressionName.getIdentifier()))
							addParameterToMovedMethod(newMethodDeclaration, expressionName, targetRewriter);
					}
					else {
						Type superclassType = sourceTypeDeclaration.getSuperclassType();
						ITypeBinding superclassTypeBinding = null;
						if(superclassType != null)
							superclassTypeBinding = superclassType.resolveBinding();
						while(superclassTypeBinding != null && !superclassTypeBinding.isEqualTo(variableBinding.getDeclaringClass())) {
							superclassTypeBinding = superclassTypeBinding.getSuperclass();
						}
						if(superclassTypeBinding != null) {
							IVariableBinding[] superclassFieldBindings = superclassTypeBinding.getDeclaredFields();
							for(IVariableBinding superclassFieldBinding : superclassFieldBindings) {
								if(superclassFieldBinding.isEqualTo(variableBinding)) {
									SimpleName expressionName = (SimpleName)newFieldInstructions.get(i);
									if(!expressionName.getIdentifier().equals(targetClassVariableName) && !additionalArgumentsAddedToMovedMethod.contains(expressionName.getIdentifier()))
										addParameterToMovedMethod(newMethodDeclaration, variableBinding, targetRewriter);
								}
							}
						}
					}
				}
			}
			i++;
		}
		SimpleName parameterName = null;
		Set<String> sourceMethodsWithPublicModifier = new LinkedHashSet<String>();
		int j = 0;
		for(Expression expression : sourceMethodInvocations) {
			if(expression instanceof MethodInvocation) {
				MethodInvocation methodInvocation = (MethodInvocation)expression;
				if(methodInvocation.getExpression() == null || methodInvocation.getExpression() instanceof ThisExpression) {
					IMethodBinding methodBinding = methodInvocation.resolveMethodBinding();
					if(methodBinding.getDeclaringClass().isEqualTo(sourceTypeDeclaration.resolveBinding())) {
						MethodDeclaration[] sourceMethodDeclarations = sourceTypeDeclaration.getMethods();
						for(MethodDeclaration sourceMethodDeclaration : sourceMethodDeclarations) {
							if(sourceMethodDeclaration.resolveBinding().isEqualTo(methodInvocation.resolveMethodBinding()) &&
									!sourceMethod.resolveBinding().isEqualTo(methodInvocation.resolveMethodBinding())) {
								SimpleName fieldName = MethodDeclarationUtility.isGetter(sourceMethodDeclaration);
								int modifiers = sourceMethodDeclaration.getModifiers();
								MethodInvocation newMethodInvocation = (MethodInvocation)newMethodInvocations.get(j);
								if((modifiers & Modifier.STATIC) != 0) {
									AST ast = newMethodDeclaration.getAST();
									targetRewriter.set(newMethodInvocation, MethodInvocation.EXPRESSION_PROPERTY, ast.newSimpleName(sourceTypeDeclaration.getName().getIdentifier()), null);
									if(!sourceMethodsWithPublicModifier.contains(methodInvocation.resolveMethodBinding().getKey())) {
										setPublicModifierToSourceMethod(methodInvocation.resolveMethodBinding(), sourceTypeDeclaration);
										sourceMethodsWithPublicModifier.add(methodInvocation.resolveMethodBinding().getKey());
										Map<IMethodBinding, TypeDeclaration> subclassTypeDeclarationMap = findSubclassesOverridingMethod(sourceTypeDeclaration, methodInvocation.resolveMethodBinding());
										for(IMethodBinding methodBindingKey : subclassTypeDeclarationMap.keySet()) {
											setPublicModifierToSourceMethod(methodBindingKey, subclassTypeDeclarationMap.get(methodBindingKey));
										}
									}
								}
								else if(fieldName != null) {
									AST ast = newMethodDeclaration.getAST();
									targetRewriter.replace(newMethodInvocation, ast.newSimpleName(fieldName.getIdentifier()), null);
									if(!fieldName.getIdentifier().equals(targetClassVariableName) && !additionalArgumentsAddedToMovedMethod.contains(fieldName.getIdentifier()))
										addParameterToMovedMethod(newMethodDeclaration, fieldName, targetRewriter);
								}
								else {
									if(!additionalArgumentsAddedToMovedMethod.contains("this")) {
										parameterName = addSourceClassParameterToMovedMethod(newMethodDeclaration, targetRewriter);
									}
									targetRewriter.set(newMethodInvocation, MethodInvocation.EXPRESSION_PROPERTY, parameterName, null);
									if(!sourceMethodsWithPublicModifier.contains(methodInvocation.resolveMethodBinding().getKey())) {
										setPublicModifierToSourceMethod(methodInvocation.resolveMethodBinding(), sourceTypeDeclaration);
										sourceMethodsWithPublicModifier.add(methodInvocation.resolveMethodBinding().getKey());
										Map<IMethodBinding, TypeDeclaration> subclassTypeDeclarationMap = findSubclassesOverridingMethod(sourceTypeDeclaration, methodInvocation.resolveMethodBinding());
										for(IMethodBinding methodBindingKey : subclassTypeDeclarationMap.keySet()) {
											setPublicModifierToSourceMethod(methodBindingKey, subclassTypeDeclarationMap.get(methodBindingKey));
										}
									}
								}
							}
						}
					}
					else {
						Type superclassType = sourceTypeDeclaration.getSuperclassType();
						ITypeBinding superclassTypeBinding = null;
						if(superclassType != null)
							superclassTypeBinding = superclassType.resolveBinding();
						while(superclassTypeBinding != null && !methodBinding.getDeclaringClass().isEqualTo(superclassTypeBinding)) {
							superclassTypeBinding = superclassTypeBinding.getSuperclass();
						}
						if(superclassTypeBinding != null) {
							IMethodBinding[] superclassMethodBindings = superclassTypeBinding.getDeclaredMethods();
							for(IMethodBinding superclassMethodBinding : superclassMethodBindings) {
								if(superclassMethodBinding.isEqualTo(methodBinding)) {
									MethodInvocation newMethodInvocation = (MethodInvocation)newMethodInvocations.get(j);
									if((superclassMethodBinding.getModifiers() & Modifier.STATIC) != 0) {
										AST ast = newMethodDeclaration.getAST();
										SimpleName qualifier = ast.newSimpleName(sourceTypeDeclaration.getName().getIdentifier());
										targetRewriter.set(newMethodInvocation, MethodInvocation.EXPRESSION_PROPERTY, qualifier, null);
									}
									else {
										if(!additionalArgumentsAddedToMovedMethod.contains("this")) {
											parameterName = addSourceClassParameterToMovedMethod(newMethodDeclaration, targetRewriter);
										}
										targetRewriter.set(newMethodInvocation, MethodInvocation.EXPRESSION_PROPERTY, parameterName, null);
										if(!sourceMethodsWithPublicModifier.contains(methodBinding.getKey())) {
											TypeDeclaration superclassTypeDeclaration = RefactoringUtility.findDeclaringTypeDeclaration(superclassMethodBinding, sourceTypeDeclaration);
											if(superclassTypeDeclaration != null) {
												setPublicModifierToSourceMethod(methodInvocation.resolveMethodBinding(), superclassTypeDeclaration);
											}
											sourceMethodsWithPublicModifier.add(methodBinding.getKey());
										}
									}
								}
							}
						}
					}
				}
			}
			j++;
		}
	}

	private SimpleName addSourceClassParameterToMovedMethod(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		AST ast = newMethodDeclaration.getAST();
		SingleVariableDeclaration parameter = ast.newSingleVariableDeclaration();
		SimpleName typeName = ast.newSimpleName(sourceTypeDeclaration.getName().getIdentifier());
		Type parameterType = ast.newSimpleType(typeName);
		targetRewriter.set(parameter, SingleVariableDeclaration.TYPE_PROPERTY, parameterType, null);
		String sourceTypeName = sourceTypeDeclaration.getName().getIdentifier();
		SimpleName parameterName = ast.newSimpleName(sourceTypeName.replaceFirst(Character.toString(sourceTypeName.charAt(0)), Character.toString(Character.toLowerCase(sourceTypeName.charAt(0)))));
		targetRewriter.set(parameter, SingleVariableDeclaration.NAME_PROPERTY, parameterName, null);
		ListRewrite parametersRewrite = targetRewriter.getListRewrite(newMethodDeclaration, MethodDeclaration.PARAMETERS_PROPERTY);
		parametersRewrite.insertLast(parameter, null);
		this.additionalArgumentsAddedToMovedMethod.add("this");
		this.additionalTypeBindingsToBeImportedInTargetClass.add(sourceTypeDeclaration.resolveBinding());
		addParamTagElementToJavadoc(newMethodDeclaration, targetRewriter, parameterName.getIdentifier());
		setPublicModifierToSourceTypeDeclaration();
		return parameterName;
	}

	private Map<IMethodBinding, TypeDeclaration> findSubclassesOverridingMethod(TypeDeclaration typeDeclaration, IMethodBinding methodBinding) {
		Map<IMethodBinding, TypeDeclaration> subclassTypeDeclarationMap = new LinkedHashMap<IMethodBinding, TypeDeclaration>();
		CompilationUnitCache cache = CompilationUnitCache.getInstance();
		Set<IType> subTypes = cache.getSubTypes((IType)typeDeclaration.resolveBinding().getJavaElement());
		for(IType iType : subTypes) {
			String fullyQualifiedTypeName = iType.getFullyQualifiedName();
			ClassObject classObject = ASTReader.getSystemObject().getClassObject(fullyQualifiedTypeName);
			if(classObject != null) {
				AbstractTypeDeclaration abstractTypeDeclaration = classObject.getAbstractTypeDeclaration();
				if(abstractTypeDeclaration instanceof TypeDeclaration) {
					TypeDeclaration subclassTypeDeclaration = (TypeDeclaration)abstractTypeDeclaration;
					for(MethodDeclaration subclassMethodDeclaration : subclassTypeDeclaration.getMethods()) {
						if(MethodCallAnalyzer.equalSignature(subclassMethodDeclaration.resolveBinding(), methodBinding)) {
							subclassTypeDeclarationMap.put(subclassMethodDeclaration.resolveBinding(), subclassTypeDeclaration);
						}
					}
				}
			}
		}
		return subclassTypeDeclarationMap;
	}

	private void setPublicModifierToSourceTypeDeclaration() {
		PackageDeclaration sourcePackageDeclaration = sourceCompilationUnit.getPackage();
		PackageDeclaration targetPackageDeclaration = targetCompilationUnit.getPackage();
		if(sourcePackageDeclaration != null && targetPackageDeclaration != null) {
			String sourcePackageName = sourcePackageDeclaration.getName().getFullyQualifiedName();
			String targetPackageName = targetPackageDeclaration.getName().getFullyQualifiedName();
			if(!sourcePackageName.equals(targetPackageName)) {
				ASTRewrite sourceRewriter = ASTRewrite.create(sourceCompilationUnit.getAST());
				ListRewrite modifierRewrite = sourceRewriter.getListRewrite(sourceTypeDeclaration, TypeDeclaration.MODIFIERS2_PROPERTY);
				Modifier publicModifier = sourceTypeDeclaration.getAST().newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
				boolean modifierFound = false;
				List<IExtendedModifier> modifiers = sourceTypeDeclaration.modifiers();
				for(IExtendedModifier extendedModifier : modifiers) {
					if(extendedModifier.isModifier()) {
						Modifier modifier = (Modifier)extendedModifier;
						if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD)) {
							modifierFound = true;
						}
						else if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD) ||
								modifier.getKeyword().equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD)) {
							modifierFound = true;
							modifierRewrite.replace(modifier, publicModifier, null);
							try {
								TextEdit sourceEdit = sourceRewriter.rewriteAST();
								sourceMultiTextEdit.addChild(sourceEdit);
								sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Change access level to public", new TextEdit[] {sourceEdit}));
							}
							catch(JavaModelException javaModelException) {
								javaModelException.printStackTrace();
							}
						}
					}
				}
				if(!modifierFound) {
					modifierRewrite.insertFirst(publicModifier, null);
					try {
						TextEdit sourceEdit = sourceRewriter.rewriteAST();
						sourceMultiTextEdit.addChild(sourceEdit);
						sourceCompilationUnitChange.addTextEditGroup(new TextEditGroup("Set access level to public", new TextEdit[] {sourceEdit}));
					}
					catch(JavaModelException javaModelException) {
						javaModelException.printStackTrace();
					}
				}
			}
		}
	}

	private void addParameterToMovedMethod(MethodDeclaration newMethodDeclaration, SimpleName fieldName, ASTRewrite targetRewriter) {
		AST ast = newMethodDeclaration.getAST();
		SingleVariableDeclaration parameter = ast.newSingleVariableDeclaration();
		Type fieldType = null;
		FieldDeclaration[] fields = sourceTypeDeclaration.getFields();
		for(FieldDeclaration field : fields) {
			List<VariableDeclarationFragment> fragments = field.fragments();
			for(VariableDeclarationFragment fragment : fragments) {
				if(fragment.getName().getIdentifier().equals(fieldName.getIdentifier())) {
					fieldType = field.getType();
					break;
				}
			}
		}
		targetRewriter.set(parameter, SingleVariableDeclaration.TYPE_PROPERTY, fieldType, null);
		targetRewriter.set(parameter, SingleVariableDeclaration.NAME_PROPERTY, ast.newSimpleName(fieldName.getIdentifier()), null);
		ListRewrite parametersRewrite = targetRewriter.getListRewrite(newMethodDeclaration, MethodDeclaration.PARAMETERS_PROPERTY);
		parametersRewrite.insertLast(parameter, null);
		this.additionalArgumentsAddedToMovedMethod.add(fieldName.getIdentifier());
		this.additionalTypeBindingsToBeImportedInTargetClass.add(fieldType.resolveBinding());
		addParamTagElementToJavadoc(newMethodDeclaration, targetRewriter, fieldName.getIdentifier());
	}

	private void addParameterToMovedMethod(MethodDeclaration newMethodDeclaration, IVariableBinding variableBinding, ASTRewrite targetRewriter) {
		AST ast = newMethodDeclaration.getAST();
		SingleVariableDeclaration parameter = ast.newSingleVariableDeclaration();
		ITypeBinding typeBinding = variableBinding.getType();
		Type fieldType = RefactoringUtility.generateTypeFromTypeBinding(typeBinding, ast, targetRewriter);
		targetRewriter.set(parameter, SingleVariableDeclaration.TYPE_PROPERTY, fieldType, null);
		targetRewriter.set(parameter, SingleVariableDeclaration.NAME_PROPERTY, ast.newSimpleName(variableBinding.getName()), null);
		ListRewrite parametersRewrite = targetRewriter.getListRewrite(newMethodDeclaration, MethodDeclaration.PARAMETERS_PROPERTY);
		parametersRewrite.insertLast(parameter, null);
		this.additionalArgumentsAddedToMovedMethod.add(variableBinding.getName());
		this.additionalTypeBindingsToBeImportedInTargetClass.add(variableBinding.getType());
		addParamTagElementToJavadoc(newMethodDeclaration, targetRewriter, variableBinding.getName());
	}

	private void setPublicModifierToSourceMethod(IMethodBinding methodBinding, TypeDeclaration sourceTypeDeclaration) {
		MethodDeclaration[] methodDeclarations = sourceTypeDeclaration.getMethods();
		for(MethodDeclaration methodDeclaration : methodDeclarations) {
			if(methodDeclaration.resolveBinding().isEqualTo(methodBinding)) {
				CompilationUnit sourceCompilationUnit = RefactoringUtility.findCompilationUnit(methodDeclaration);
				ASTRewrite sourceRewriter = ASTRewrite.create(sourceCompilationUnit.getAST());
				ListRewrite modifierRewrite = sourceRewriter.getListRewrite(methodDeclaration, MethodDeclaration.MODIFIERS2_PROPERTY);
				Modifier publicModifier = methodDeclaration.getAST().newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD);
				boolean modifierFound = false;
				List<IExtendedModifier> modifiers = methodDeclaration.modifiers();
				for(IExtendedModifier extendedModifier : modifiers) {
					if(extendedModifier.isModifier()) {
						Modifier modifier = (Modifier)extendedModifier;
						if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PUBLIC_KEYWORD)) {
							modifierFound = true;
						}
						else if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PRIVATE_KEYWORD)) {
							modifierFound = true;
							modifierRewrite.replace(modifier, publicModifier, null);
							updateAccessModifier(sourceRewriter, sourceCompilationUnit);
						}
						else if(modifier.getKeyword().equals(Modifier.ModifierKeyword.PROTECTED_KEYWORD)) {
							modifierFound = true;
							IPackageBinding targetTypeDeclarationPackageBinding = this.targetTypeDeclaration.resolveBinding().getPackage();
							IPackageBinding typeDeclarationPackageBinding = sourceTypeDeclaration.resolveBinding().getPackage();
							if(targetTypeDeclarationPackageBinding != null && typeDeclarationPackageBinding != null &&
									!targetTypeDeclarationPackageBinding.isEqualTo(typeDeclarationPackageBinding)) {
								modifierRewrite.replace(modifier, publicModifier, null);
								updateAccessModifier(sourceRewriter, sourceCompilationUnit);
							}
						}
					}
				}
				if(!modifierFound) {
					modifierRewrite.insertFirst(publicModifier, null);
					updateAccessModifier(sourceRewriter, sourceCompilationUnit);
				}
			}
		}
	}

	private void updateAccessModifier(ASTRewrite sourceRewriter, CompilationUnit sourceCompilationUnit) {
		try {
			TextEdit sourceEdit = sourceRewriter.rewriteAST();
			ICompilationUnit sourceICompilationUnit = (ICompilationUnit)sourceCompilationUnit.getJavaElement();
			CompilationUnitChange change = fChanges.get(sourceICompilationUnit);
			if(change == null) {
				MultiTextEdit sourceMultiTextEdit = new MultiTextEdit();
				change = new CompilationUnitChange("", sourceICompilationUnit);
				change.setEdit(sourceMultiTextEdit);
				fChanges.put(sourceICompilationUnit, change);
			}
			change.getEdit().addChild(sourceEdit);
			change.addTextEditGroup(new TextEditGroup("Update access modifier to public", new TextEdit[] {sourceEdit}));
		}
		catch(JavaModelException javaModelException) {
			javaModelException.printStackTrace();
		}
	}
	
	private void modifyRecursiveMethodInvocationsOfTheMovedMethod(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> sourceMethodInvocations = extractor.getMethodInvocations(sourceMethod.getBody());
		List<Expression> newMethodInvocations = extractor.getMethodInvocations(newMethodDeclaration.getBody());
		int i = 0;
		for(Expression invocation : newMethodInvocations) {
			if(invocation instanceof MethodInvocation) {
				MethodInvocation methodInvocation = (MethodInvocation)invocation;
				MethodInvocation sourceMethodInvocation = (MethodInvocation)sourceMethodInvocations.get(i);
				AST ast = newMethodDeclaration.getAST();
				if(sourceMethod.resolveBinding().isEqualTo(sourceMethodInvocation.resolveMethodBinding())) {
					targetRewriter.set(methodInvocation, MethodInvocation.NAME_PROPERTY, ast.newSimpleName(movedMethodName), null);
					List<Expression> arguments = methodInvocation.arguments();
					boolean argumentFound = false;
					for(Expression argument : arguments) {
						SimpleName argumentSimpleName = null;
						if(argument instanceof SimpleName) {
							argumentSimpleName = (SimpleName)argument;
						}
						else if(argument instanceof FieldAccess) {
							FieldAccess fieldAccess = (FieldAccess)argument;
							argumentSimpleName = fieldAccess.getName();
						}
						if(argumentSimpleName != null) {
							ListRewrite argumentRewrite = targetRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
							if(argumentSimpleName.getIdentifier().equals(targetClassVariableName)) {
								argumentRewrite.remove(argument, null);
								argumentFound = true;
								break;
							}
						}
					}
					if(!argumentFound && isTargetClassVariableParameter) {
						List<Expression> sourceMethodInvocationArguments = sourceMethodInvocation.arguments();
						int j = 0;
						for(Expression argument : sourceMethodInvocationArguments) {
							SimpleName argumentSimpleName = null;
							if(argument instanceof SimpleName) {
								argumentSimpleName = (SimpleName)argument;
							}
							else if(argument instanceof FieldAccess) {
								FieldAccess fieldAccess = (FieldAccess)argument;
								argumentSimpleName = fieldAccess.getName();
							}
							if(argumentSimpleName != null) {
								ListRewrite argumentRewrite = targetRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
								if(( argumentSimpleName.resolveTypeBinding().isEqualTo(targetTypeDeclaration.resolveBinding()) ||
										targetTypeDeclaration.resolveBinding().isEqualTo(argumentSimpleName.resolveTypeBinding().getSuperclass()) ) &&
										targetClassVariableParameterIndex == j) {
									argumentRewrite.remove(arguments.get(j), null);
									targetRewriter.set(methodInvocation, MethodInvocation.EXPRESSION_PROPERTY, arguments.get(j), null);
									argumentFound = true;
									break;
								}
							}
							j++;
						}
					}
					if(!argumentFound && isTargetClassVariableParameter) {
						List<Expression> sourceMethodInvocationArguments = sourceMethodInvocation.arguments();
						int j = 0;
						for(Expression argument : sourceMethodInvocationArguments) {
							if(argument instanceof MethodInvocation) {
								MethodInvocation argumentMethodInvocation = (MethodInvocation)argument;
								ITypeBinding returnTypeBinding = argumentMethodInvocation.resolveMethodBinding().getReturnType();
								ListRewrite argumentRewrite = targetRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
								if(( returnTypeBinding.isEqualTo(targetTypeDeclaration.resolveBinding()) ||
										targetTypeDeclaration.resolveBinding().isEqualTo(returnTypeBinding.getSuperclass()) ) &&
										targetClassVariableParameterIndex == j) {
									argumentRewrite.remove(arguments.get(j), null);
									targetRewriter.set(methodInvocation, MethodInvocation.EXPRESSION_PROPERTY, arguments.get(j), null);
									break;
								}
							}
							j++;
						}
					}
				}
			}
			i++;
		}
	}
	
	private void replaceTargetClassVariableNameWithThisExpressionInMethodInvocationArguments(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> sourceMethodInvocations = extractor.getMethodInvocations(sourceMethod.getBody());
		List<Expression> newMethodInvocations = extractor.getMethodInvocations(newMethodDeclaration.getBody());
		int i = 0;
		for(Expression invocation : newMethodInvocations) {
			if(invocation instanceof MethodInvocation) {
				MethodInvocation methodInvocation = (MethodInvocation)invocation;
				List<Expression> arguments = methodInvocation.arguments();
				for(Expression argument : arguments) {
					if(argument instanceof SimpleName) {
						SimpleName simpleNameArgument = (SimpleName)argument;
						if(simpleNameArgument.getIdentifier().equals(targetClassVariableName)) {
							ListRewrite argumentRewrite = targetRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
							MethodInvocation sourceMethodInvocation = (MethodInvocation)sourceMethodInvocations.get(i);
							AST ast = newMethodDeclaration.getAST();
							if(!sourceMethod.resolveBinding().isEqualTo(sourceMethodInvocation.resolveMethodBinding())) {
								argumentRewrite.replace(argument, ast.newThisExpression(), null);
							}
						}
					}
					else if(argument instanceof FieldAccess) {
						FieldAccess fieldAccess = (FieldAccess)argument;
						SimpleName simpleNameArgument = fieldAccess.getName();
						if(simpleNameArgument.getIdentifier().equals(targetClassVariableName)) {
							ListRewrite argumentRewrite = targetRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
							MethodInvocation sourceMethodInvocation = (MethodInvocation)sourceMethodInvocations.get(i);
							AST ast = newMethodDeclaration.getAST();
							if(!sourceMethod.resolveBinding().isEqualTo(sourceMethodInvocation.resolveMethodBinding())) {
								argumentRewrite.replace(argument, ast.newThisExpression(), null);
							}
						}
					}
				}
			}
			i++;
		}
	}
	
	private void replaceTargetClassVariableNameWithThisExpressionInClassInstanceCreationArguments(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> classInstanceCreations = extractor.getClassInstanceCreations(newMethodDeclaration.getBody());
		for(Expression creation : classInstanceCreations) {
			ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation)creation;
			List<Expression> arguments = classInstanceCreation.arguments();
			for(Expression argument : arguments) {
				if(argument instanceof SimpleName) {
					SimpleName simpleNameArgument = (SimpleName)argument;
					if(simpleNameArgument.getIdentifier().equals(targetClassVariableName)) {
						ListRewrite argumentRewrite = targetRewriter.getListRewrite(classInstanceCreation, ClassInstanceCreation.ARGUMENTS_PROPERTY);
						AST ast = newMethodDeclaration.getAST();
						argumentRewrite.replace(argument, ast.newThisExpression(), null);
					}
				}
				else if(argument instanceof FieldAccess) {
					FieldAccess fieldAccess = (FieldAccess)argument;
					SimpleName simpleNameArgument = fieldAccess.getName();
					if(simpleNameArgument.getIdentifier().equals(targetClassVariableName)) {
						ListRewrite argumentRewrite = targetRewriter.getListRewrite(classInstanceCreation, ClassInstanceCreation.ARGUMENTS_PROPERTY);
						AST ast = newMethodDeclaration.getAST();
						argumentRewrite.replace(argument, ast.newThisExpression(), null);
					}
				}
			}
		}
	}
	
	private void replaceTargetClassVariableNameWithThisExpressionInVariableDeclarationInitializers(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		StatementExtractor statementExtractor = new StatementExtractor();
		ExpressionExtractor expressionExtractor = new ExpressionExtractor();
		List<VariableDeclarationFragment> variableDeclarationFragments = new ArrayList<VariableDeclarationFragment>();
		List<Statement> variableDeclarationStatements = statementExtractor.getVariableDeclarationStatements(newMethodDeclaration.getBody());
		for(Statement statement : variableDeclarationStatements) {
			VariableDeclarationStatement variableDeclarationStatement = (VariableDeclarationStatement)statement;
			List<VariableDeclarationFragment> fragments = variableDeclarationStatement.fragments();
			variableDeclarationFragments.addAll(fragments);
		}
		List<Expression> variableDeclarationExpressions = expressionExtractor.getVariableDeclarationExpressions(newMethodDeclaration.getBody());
		for(Expression expression : variableDeclarationExpressions) {
			VariableDeclarationExpression variableDeclarationExpression = (VariableDeclarationExpression)expression;
			List<VariableDeclarationFragment> fragments = variableDeclarationExpression.fragments();
			variableDeclarationFragments.addAll(fragments);
		}
		for(VariableDeclarationFragment fragment : variableDeclarationFragments) {
			Expression initializer = fragment.getInitializer();
			if(initializer instanceof SimpleName) {
				SimpleName simpleNameInitializer = (SimpleName)initializer;
				if(simpleNameInitializer.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(fragment, VariableDeclarationFragment.INITIALIZER_PROPERTY, ast.newThisExpression(), null);
				}
			}
			else if(initializer instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)initializer;
				SimpleName simpleNameInitializer = fieldAccess.getName();
				if(simpleNameInitializer.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(fragment, VariableDeclarationFragment.INITIALIZER_PROPERTY, ast.newThisExpression(), null);
				}
			}
		}
	}
	
	private void replaceTargetClassVariableNameWithThisExpressionInInfixExpressions(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> infixExpressions = extractor.getInfixExpressions(newMethodDeclaration.getBody());
		for(Expression expression : infixExpressions) {
			InfixExpression infixExpression = (InfixExpression)expression;
			if(infixExpression.getLeftOperand() instanceof SimpleName) {
				SimpleName leftOperand = (SimpleName)infixExpression.getLeftOperand();
				if(leftOperand.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(infixExpression, InfixExpression.LEFT_OPERAND_PROPERTY, ast.newThisExpression(), null);
				}	
			}
			else if(infixExpression.getLeftOperand() instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)infixExpression.getLeftOperand();
				SimpleName leftOperand = fieldAccess.getName();
				if(leftOperand.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(infixExpression, InfixExpression.LEFT_OPERAND_PROPERTY, ast.newThisExpression(), null);
				}
			}
			if(infixExpression.getRightOperand() instanceof SimpleName) {
				SimpleName rightOperand = (SimpleName)infixExpression.getRightOperand();
				if(rightOperand.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(infixExpression, InfixExpression.RIGHT_OPERAND_PROPERTY, ast.newThisExpression(), null);
				}	
			}
			else if(infixExpression.getRightOperand() instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)infixExpression.getRightOperand();
				SimpleName rightOperand = fieldAccess.getName();
				if(rightOperand.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(infixExpression, InfixExpression.RIGHT_OPERAND_PROPERTY, ast.newThisExpression(), null);
				}
			}
		}
	}
	
	private void replaceTargetClassVariableNameWithThisExpressionInCastExpressions(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> castExpressions = extractor.getCastExpressions(newMethodDeclaration.getBody());
		for(Expression expression : castExpressions) {
			CastExpression castExpression = (CastExpression)expression;
			if(castExpression.getExpression() instanceof SimpleName) {
				SimpleName simpleName = (SimpleName)castExpression.getExpression();
				if(simpleName.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(castExpression, CastExpression.EXPRESSION_PROPERTY, ast.newThisExpression(), null);
				}
			}
			else if(castExpression.getExpression() instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)castExpression.getExpression();
				SimpleName simpleName = fieldAccess.getName();
				if(simpleName.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(castExpression, CastExpression.EXPRESSION_PROPERTY, ast.newThisExpression(), null);
				}
			}
		}
	}
	
	private void replaceTargetClassVariableNameWithThisExpressionInInstanceofExpressions(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> instanceofExpressions = extractor.getInstanceofExpressions(newMethodDeclaration.getBody());
		for(Expression expression : instanceofExpressions) {
			InstanceofExpression instanceofExpression = (InstanceofExpression)expression;
			if(instanceofExpression.getLeftOperand() instanceof SimpleName) {
				SimpleName simpleName = (SimpleName)instanceofExpression.getLeftOperand();
				if(simpleName.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(instanceofExpression, InstanceofExpression.LEFT_OPERAND_PROPERTY, ast.newThisExpression(), null);
				}
			}
			else if(instanceofExpression.getLeftOperand() instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)instanceofExpression.getLeftOperand();
				SimpleName simpleName = fieldAccess.getName();
				if(simpleName.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(instanceofExpression, InstanceofExpression.LEFT_OPERAND_PROPERTY, ast.newThisExpression(), null);
				}
			}
		}
	}
	
	private void replaceTargetClassVariableNameWithThisExpressionInAssignments(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> assignments = extractor.getAssignments(newMethodDeclaration.getBody());
		for(Expression expression : assignments) {
			Assignment assignment = (Assignment)expression;
			if(assignment.getLeftHandSide() instanceof SimpleName) {
				SimpleName leftHandSide = (SimpleName)assignment.getLeftHandSide();
				if(leftHandSide.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(assignment, Assignment.LEFT_HAND_SIDE_PROPERTY, ast.newThisExpression(), null);
				}	
			}
			else if(assignment.getLeftHandSide() instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)assignment.getLeftHandSide();
				SimpleName leftHandSide = fieldAccess.getName();
				if(leftHandSide.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(assignment, Assignment.LEFT_HAND_SIDE_PROPERTY, ast.newThisExpression(), null);
				}
			}
			if(assignment.getRightHandSide() instanceof SimpleName) {
				SimpleName rightHandSide = (SimpleName)assignment.getRightHandSide();
				if(rightHandSide.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(assignment, Assignment.RIGHT_HAND_SIDE_PROPERTY, ast.newThisExpression(), null);
				}	
			}
			else if(assignment.getRightHandSide() instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)assignment.getRightHandSide();
				SimpleName rightHandSide = fieldAccess.getName();
				if(rightHandSide.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(assignment, Assignment.RIGHT_HAND_SIDE_PROPERTY, ast.newThisExpression(), null);
				}
			}
		}
	}

	private void replaceTargetClassVariableNameWithThisExpressionInReturnStatements(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		StatementExtractor extractor = new StatementExtractor();
		List<Statement> returnStatements = extractor.getReturnStatements(newMethodDeclaration.getBody());
		for(Statement statement : returnStatements) {
			ReturnStatement returnStatement = (ReturnStatement)statement;
			if(returnStatement.getExpression() instanceof SimpleName) {
				SimpleName simpleName = (SimpleName)returnStatement.getExpression();
				if(simpleName.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(returnStatement, ReturnStatement.EXPRESSION_PROPERTY, ast.newThisExpression(), null);
				}
			}
			else if(returnStatement.getExpression() instanceof FieldAccess) {
				FieldAccess fieldAccess = (FieldAccess)returnStatement.getExpression();
				SimpleName simpleName = fieldAccess.getName();
				if(simpleName.getIdentifier().equals(targetClassVariableName)) {
					AST ast = newMethodDeclaration.getAST();
					targetRewriter.set(returnStatement, ReturnStatement.EXPRESSION_PROPERTY, ast.newThisExpression(), null);
				}
			}
		}
	}

	private void replaceThisExpressionWithSourceClassParameterInMethodInvocationArguments(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> methodInvocations = extractor.getMethodInvocations(newMethodDeclaration.getBody());
		for(Expression invocation : methodInvocations) {
			if(invocation instanceof MethodInvocation) {
				MethodInvocation methodInvocation = (MethodInvocation)invocation;
				List<Expression> arguments = methodInvocation.arguments();
				for(Expression argument : arguments) {
					if(argument instanceof ThisExpression) {
						SimpleName parameterName = null;
						if(!additionalArgumentsAddedToMovedMethod.contains("this")) {
							parameterName = addSourceClassParameterToMovedMethod(newMethodDeclaration, targetRewriter);
						}
						else {
							AST ast = newMethodDeclaration.getAST();
							String sourceTypeName = sourceTypeDeclaration.getName().getIdentifier();
							parameterName = ast.newSimpleName(sourceTypeName.replaceFirst(Character.toString(sourceTypeName.charAt(0)), Character.toString(Character.toLowerCase(sourceTypeName.charAt(0)))));
						}
						ListRewrite argumentRewrite = targetRewriter.getListRewrite(methodInvocation, MethodInvocation.ARGUMENTS_PROPERTY);
						argumentRewrite.replace(argument, parameterName, null);
					}
				}
			}
		}
	}
	
	private void replaceThisExpressionWithSourceClassParameterInClassInstanceCreationArguments(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		ExpressionExtractor extractor = new ExpressionExtractor();
		List<Expression> classInstanceCreations = extractor.getClassInstanceCreations(newMethodDeclaration.getBody());
		for(Expression creation : classInstanceCreations) {
			ClassInstanceCreation classInstanceCreation = (ClassInstanceCreation)creation;
			List<Expression> arguments = classInstanceCreation.arguments();
			for(Expression argument : arguments) {
				if(argument instanceof ThisExpression) {
					SimpleName parameterName = null;
					if(!additionalArgumentsAddedToMovedMethod.contains("this")) {
						parameterName = addSourceClassParameterToMovedMethod(newMethodDeclaration, targetRewriter);
					}
					else {
						AST ast = newMethodDeclaration.getAST();
						String sourceTypeName = sourceTypeDeclaration.getName().getIdentifier();
						parameterName = ast.newSimpleName(sourceTypeName.replaceFirst(Character.toString(sourceTypeName.charAt(0)), Character.toString(Character.toLowerCase(sourceTypeName.charAt(0)))));
					}
					ListRewrite argumentRewrite = targetRewriter.getListRewrite(classInstanceCreation, ClassInstanceCreation.ARGUMENTS_PROPERTY);
					argumentRewrite.replace(argument, parameterName, null);
				}
			}
		}
	}
	
	private void replaceThisExpressionWithSourceClassParameterInVariableDeclarationInitializers(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter) {
		StatementExtractor statementExtractor = new StatementExtractor();
		ExpressionExtractor expressionExtractor = new ExpressionExtractor();
		List<VariableDeclarationFragment> variableDeclarationFragments = new ArrayList<VariableDeclarationFragment>();
		List<Statement> variableDeclarationStatements = statementExtractor.getVariableDeclarationStatements(newMethodDeclaration.getBody());
		for(Statement statement : variableDeclarationStatements) {
			VariableDeclarationStatement variableDeclarationStatement = (VariableDeclarationStatement)statement;
			List<VariableDeclarationFragment> fragments = variableDeclarationStatement.fragments();
			variableDeclarationFragments.addAll(fragments);
		}
		List<Expression> variableDeclarationExpressions = expressionExtractor.getVariableDeclarationExpressions(newMethodDeclaration.getBody());
		for(Expression expression : variableDeclarationExpressions) {
			VariableDeclarationExpression variableDeclarationExpression = (VariableDeclarationExpression)expression;
			List<VariableDeclarationFragment> fragments = variableDeclarationExpression.fragments();
			variableDeclarationFragments.addAll(fragments);
		}
		for(VariableDeclarationFragment fragment : variableDeclarationFragments) {
			Expression initializer = fragment.getInitializer();
			if(initializer instanceof ThisExpression) {
				SimpleName parameterName = null;
				if(!additionalArgumentsAddedToMovedMethod.contains("this")) {
					parameterName = addSourceClassParameterToMovedMethod(newMethodDeclaration, targetRewriter);
				}
				else {
					AST ast = newMethodDeclaration.getAST();
					String sourceTypeName = sourceTypeDeclaration.getName().getIdentifier();
					parameterName = ast.newSimpleName(sourceTypeName.replaceFirst(Character.toString(sourceTypeName.charAt(0)), Character.toString(Character.toLowerCase(sourceTypeName.charAt(0)))));
				}
				targetRewriter.set(fragment, VariableDeclarationFragment.INITIALIZER_PROPERTY, parameterName, null);
			}
		}
	}

	private void addParamTagElementToJavadoc(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter, String parameterToBeAdded) {
		if(newMethodDeclaration.getJavadoc() != null) {
			AST ast = newMethodDeclaration.getAST();
			Javadoc javadoc = newMethodDeclaration.getJavadoc();
			List<TagElement> tags = javadoc.tags();
			TagElement returnTagElement = null;
			for(TagElement tag : tags) {
				if(tag.getTagName() != null && tag.getTagName().equals(TagElement.TAG_RETURN)) {
					returnTagElement = tag;
					break;
				}
			}
			
			TagElement tagElement = ast.newTagElement();
			targetRewriter.set(tagElement, TagElement.TAG_NAME_PROPERTY, TagElement.TAG_PARAM, null);
			ListRewrite fragmentsRewrite = targetRewriter.getListRewrite(tagElement, TagElement.FRAGMENTS_PROPERTY);
			SimpleName paramName = ast.newSimpleName(parameterToBeAdded);
			fragmentsRewrite.insertLast(paramName, null);
			
			ListRewrite tagsRewrite = targetRewriter.getListRewrite(javadoc, Javadoc.TAGS_PROPERTY);
			if(returnTagElement != null)
				tagsRewrite.insertBefore(tagElement, returnTagElement, null);
			else
				tagsRewrite.insertLast(tagElement, null);
		}
	}

	private void removeParamTagElementFromJavadoc(MethodDeclaration newMethodDeclaration, ASTRewrite targetRewriter, String parameterToBeRemoved) {
		if(newMethodDeclaration.getJavadoc() != null) {
			Javadoc javadoc = newMethodDeclaration.getJavadoc();
			List<TagElement> tags = javadoc.tags();
			for(TagElement tag : tags) {
				if(tag.getTagName() != null && tag.getTagName().equals(TagElement.TAG_PARAM)) {
					List<ASTNode> tagFragments = tag.fragments();
					boolean paramFound = false;
					for(ASTNode node : tagFragments) {
						if(node instanceof SimpleName) {
							SimpleName simpleName = (SimpleName)node;
							if(simpleName.getIdentifier().equals(parameterToBeRemoved)) {
								paramFound = true;
								break;
							}
						}
					}
					if(paramFound) {
						ListRewrite tagsRewrite = targetRewriter.getListRewrite(javadoc, Javadoc.TAGS_PROPERTY);
						tagsRewrite.remove(tag, null);
						break;
					}
				}
			}
		}
	}

	@Override
	public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
			throws CoreException, OperationCanceledException {
		final RefactoringStatus status= new RefactoringStatus();
		try {
			pm.beginTask("Checking preconditions...", 2);
			apply();
		} finally {
			pm.done();
		}
		return status;
	}

	@Override
	public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
			throws CoreException, OperationCanceledException {
		RefactoringStatus status= new RefactoringStatus();
		try {
			pm.beginTask("Checking preconditions...", 1);
		} finally {
			pm.done();
		}
		return status;
	}

	@Override
	public Change createChange(IProgressMonitor pm) throws CoreException,
			OperationCanceledException {
		try {
			pm.beginTask("Creating change...", 1);
			final Collection<CompilationUnitChange> changes = fChanges.values();
			CompositeChange change = new CompositeChange(getName(), changes.toArray(new Change[changes.size()])) {
				@Override
				public ChangeDescriptor getDescriptor() {
					ICompilationUnit sourceICompilationUnit = (ICompilationUnit)sourceCompilationUnit.getJavaElement();
					String project = sourceICompilationUnit.getJavaProject().getElementName();
					String description = MessageFormat.format("Move method ''{0}''", new Object[] { sourceMethod.getName().getIdentifier()});
					String comment = MessageFormat.format("Move method ''{0}'' to ''{1}''", new Object[] { sourceMethod.getName().getIdentifier(), targetTypeDeclaration.getName().getIdentifier()});
					return new RefactoringChangeDescriptor(new MoveMethodRefactoringDescriptor(project, description, comment,
							sourceCompilationUnit, targetCompilationUnit,
							sourceTypeDeclaration, targetTypeDeclaration, sourceMethod,
							additionalMethodsToBeMoved, leaveDelegate, movedMethodName));
				}
			};
			return change;
		} finally {
			pm.done();
		}
	}

	@Override
	public String getName() {
		return "Move Method";
	}
}