package gr.uom.java.ast.decomposition.cfg.mapping;

import gr.uom.java.ast.ASTInformationGenerator;
import gr.uom.java.ast.ASTReader;
import gr.uom.java.ast.AbstractMethodDeclaration;
import gr.uom.java.ast.ClassObject;
import gr.uom.java.ast.CompilationUnitCache;
import gr.uom.java.ast.FieldInstructionObject;
import gr.uom.java.ast.FieldObject;
import gr.uom.java.ast.MethodInvocationObject;
import gr.uom.java.ast.MethodObject;
import gr.uom.java.ast.ParameterObject;
import gr.uom.java.ast.SystemObject;
import gr.uom.java.ast.VariableDeclarationObject;
import gr.uom.java.ast.decomposition.AbstractExpression;
import gr.uom.java.ast.decomposition.AbstractMethodFragment;
import gr.uom.java.ast.decomposition.AbstractStatement;
import gr.uom.java.ast.decomposition.CatchClauseObject;
import gr.uom.java.ast.decomposition.CompositeStatementObject;
import gr.uom.java.ast.decomposition.StatementObject;
import gr.uom.java.ast.decomposition.StatementType;
import gr.uom.java.ast.decomposition.TryStatementObject;
import gr.uom.java.ast.decomposition.cfg.AbstractVariable;
import gr.uom.java.ast.decomposition.cfg.CFGBranchIfNode;
import gr.uom.java.ast.decomposition.cfg.CFGBreakNode;
import gr.uom.java.ast.decomposition.cfg.CFGContinueNode;
import gr.uom.java.ast.decomposition.cfg.CFGExitNode;
import gr.uom.java.ast.decomposition.cfg.CFGNode;
import gr.uom.java.ast.decomposition.cfg.CFGThrowNode;
import gr.uom.java.ast.decomposition.cfg.CompositeVariable;
import gr.uom.java.ast.decomposition.cfg.GraphEdge;
import gr.uom.java.ast.decomposition.cfg.GraphNode;
import gr.uom.java.ast.decomposition.cfg.MethodCallAnalyzer;
import gr.uom.java.ast.decomposition.cfg.PDG;
import gr.uom.java.ast.decomposition.cfg.PDGAbstractDataDependence;
import gr.uom.java.ast.decomposition.cfg.PDGAntiDependence;
import gr.uom.java.ast.decomposition.cfg.PDGBlockNode;
import gr.uom.java.ast.decomposition.cfg.PDGControlPredicateNode;
import gr.uom.java.ast.decomposition.cfg.PDGDataDependence;
import gr.uom.java.ast.decomposition.cfg.PDGDependence;
import gr.uom.java.ast.decomposition.cfg.PDGExitNode;
import gr.uom.java.ast.decomposition.cfg.PDGExpression;
import gr.uom.java.ast.decomposition.cfg.PDGNode;
import gr.uom.java.ast.decomposition.cfg.PDGOutputDependence;
import gr.uom.java.ast.decomposition.cfg.PDGTryNode;
import gr.uom.java.ast.decomposition.cfg.PlainVariable;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.DualExpressionPreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.DualExpressionWithCommonSuperTypePreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.ExpressionPreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.NotAllPossibleExecutionFlowsEndInReturnPreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.PreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.PreconditionViolationType;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.ReturnedVariablePreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.StatementPreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.UncommonSuperclassPreconditionViolation;
import gr.uom.java.ast.decomposition.cfg.mapping.precondition.ZeroMatchedStatementsPreconditionViolation;
import gr.uom.java.ast.decomposition.matching.ASTNodeDifference;
import gr.uom.java.ast.decomposition.matching.ASTNodeMatcher;
import gr.uom.java.ast.decomposition.matching.BindingSignature;
import gr.uom.java.ast.decomposition.matching.BindingSignaturePair;
import gr.uom.java.ast.decomposition.matching.Difference;
import gr.uom.java.ast.decomposition.matching.DifferenceType;
import gr.uom.java.ast.decomposition.matching.FieldAssignmentReplacedWithSetterInvocationDifference;
import gr.uom.java.ast.util.ExpressionExtractor;
import gr.uom.java.ast.util.MethodDeclarationUtility;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
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.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
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.NullLiteral;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
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.Statement;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.VariableDeclaration;

public class PreconditionExaminer {
	private PDG pdg1;
	private PDG pdg2;
	private ICompilationUnit iCompilationUnit1;
	private ICompilationUnit iCompilationUnit2;
	private TreeSet<PDGNode> mappedNodesG1;
	private TreeSet<PDGNode> mappedNodesG2;
	private TreeSet<PDGNode> nonMappedNodesG1;
	private TreeSet<PDGNode> nonMappedNodesG2;
	private Map<VariableBindingKeyPair, ArrayList<AbstractVariable>> commonPassedParameters;
	private Map<VariableBindingKeyPair, ArrayList<AbstractVariable>> declaredLocalVariablesInMappedNodes;
	private Map<VariableBindingKeyPair, ArrayList<AbstractVariable>> declaredLocalVariablesInMappedNodesWithinAnonymousClass;
	//includes used and modified fields
	private Set<AbstractVariable> directlyAccessedLocalFieldsG1;
	private Set<AbstractVariable> directlyAccessedLocalFieldsG2;
	private Set<AbstractVariable> indirectlyAccessedLocalFieldsG1;
	private Set<AbstractVariable> indirectlyAccessedLocalFieldsG2;
	//includes only modified fields
	private Set<AbstractVariable> directlyModifiedLocalFieldsG1;
	private Set<AbstractVariable> directlyModifiedLocalFieldsG2;
	private Set<AbstractVariable> indirectlyModifiedLocalFieldsG1;
	private Set<AbstractVariable> indirectlyModifiedLocalFieldsG2;
	private Set<MethodObject> directlyAccessedLocalMethodsG1;
	private Set<MethodObject> directlyAccessedLocalMethodsG2;
	private Set<MethodObject> indirectlyAccessedLocalMethodsG1;
	private Set<MethodObject> indirectlyAccessedLocalMethodsG2;
	private List<PreconditionViolation> preconditionViolations;
	private Set<BindingSignaturePair> renamedVariables;
	private Set<BindingSignaturePair> renamedFields;
	private Set<PlainVariable> variablesToBeReturnedG1;
	private Set<PlainVariable> variablesToBeReturnedG2;
	private TreeSet<PDGNode> nonMappedPDGNodesG1MovableBefore;
	private TreeSet<PDGNode> nonMappedPDGNodesG1MovableAfter;
	private TreeSet<PDGNode> nonMappedPDGNodesG1MovableBeforeAndAfter;
	private TreeSet<PDGNode> nonMappedPDGNodesG2MovableBefore;
	private TreeSet<PDGNode> nonMappedPDGNodesG2MovableAfter;
	private TreeSet<PDGNode> nonMappedPDGNodesG2MovableBeforeAndAfter;
	private TreeSet<PDGNode> additionallyMatchedNodesG1;
	private TreeSet<PDGNode> additionallyMatchedNodesG2;
	private Set<AbstractVariable> declaredLocalVariablesInAdditionallyMatchedNodesG1;
	private Set<AbstractVariable> declaredLocalVariablesInAdditionallyMatchedNodesG2;
	private CloneStructureNode cloneStructureRoot;
	private MappingState finalState;
	private TreeSet<PDGNode> allNodesInSubTreePDG1;
	private TreeSet<PDGNode> allNodesInSubTreePDG2;
	private CloneRefactoringType cloneRefactoringType;
	private Set<PlainVariable> declaredVariablesInRemainingNodesDefinedByMappedNodesG1;
	private Set<PlainVariable> declaredVariablesInRemainingNodesDefinedByMappedNodesG2;
	
	public PreconditionExaminer(PDG pdg1, PDG pdg2,
			ICompilationUnit iCompilationUnit1, ICompilationUnit iCompilationUnit2,
			CloneStructureNode cloneStructureRoot, MappingState finalState,
			TreeSet<PDGNode> allNodesInSubTreePDG1, TreeSet<PDGNode> allNodesInSubTreePDG2) {
		this.pdg1 = pdg1;
		this.pdg2 = pdg2;
		this.iCompilationUnit1 = iCompilationUnit1;
		this.iCompilationUnit2 = iCompilationUnit2;
		this.cloneStructureRoot = cloneStructureRoot;
		this.finalState = finalState;
		this.allNodesInSubTreePDG1 = allNodesInSubTreePDG1;
		this.allNodesInSubTreePDG2 = allNodesInSubTreePDG2;
		this.nonMappedNodesG1 = new TreeSet<PDGNode>();
		this.nonMappedNodesG2 = new TreeSet<PDGNode>();
		this.commonPassedParameters = new LinkedHashMap<VariableBindingKeyPair, ArrayList<AbstractVariable>>();
		this.declaredLocalVariablesInMappedNodes = new LinkedHashMap<VariableBindingKeyPair, ArrayList<AbstractVariable>>();
		this.declaredLocalVariablesInMappedNodesWithinAnonymousClass = new LinkedHashMap<VariableBindingKeyPair, ArrayList<AbstractVariable>>();
		this.directlyAccessedLocalFieldsG1 = new LinkedHashSet<AbstractVariable>();
		this.directlyAccessedLocalFieldsG2 = new LinkedHashSet<AbstractVariable>();
		this.indirectlyAccessedLocalFieldsG1 = new LinkedHashSet<AbstractVariable>();
		this.indirectlyAccessedLocalFieldsG2 = new LinkedHashSet<AbstractVariable>();
		this.directlyModifiedLocalFieldsG1 = new LinkedHashSet<AbstractVariable>();
		this.directlyModifiedLocalFieldsG2 = new LinkedHashSet<AbstractVariable>();
		this.indirectlyModifiedLocalFieldsG1 = new LinkedHashSet<AbstractVariable>();
		this.indirectlyModifiedLocalFieldsG2 = new LinkedHashSet<AbstractVariable>();
		this.directlyAccessedLocalMethodsG1 = new LinkedHashSet<MethodObject>();
		this.directlyAccessedLocalMethodsG2 = new LinkedHashSet<MethodObject>();
		this.indirectlyAccessedLocalMethodsG1 = new LinkedHashSet<MethodObject>();
		this.indirectlyAccessedLocalMethodsG2 = new LinkedHashSet<MethodObject>();
		this.preconditionViolations = new ArrayList<PreconditionViolation>();
		this.renamedVariables = new LinkedHashSet<BindingSignaturePair>();
		this.renamedFields = new LinkedHashSet<BindingSignaturePair>();
		this.variablesToBeReturnedG1 = new LinkedHashSet<PlainVariable>();
		this.variablesToBeReturnedG2 = new LinkedHashSet<PlainVariable>();
		this.nonMappedPDGNodesG1MovableBefore = new TreeSet<PDGNode>();
		this.nonMappedPDGNodesG1MovableAfter = new TreeSet<PDGNode>();
		this.nonMappedPDGNodesG1MovableBeforeAndAfter = new TreeSet<PDGNode>();
		this.nonMappedPDGNodesG2MovableBefore = new TreeSet<PDGNode>();
		this.nonMappedPDGNodesG2MovableAfter = new TreeSet<PDGNode>();
		this.nonMappedPDGNodesG2MovableBeforeAndAfter = new TreeSet<PDGNode>();
		this.mappedNodesG1 = new TreeSet<PDGNode>();
		this.mappedNodesG2 = new TreeSet<PDGNode>();
		this.additionallyMatchedNodesG1 = new TreeSet<PDGNode>();
		this.additionallyMatchedNodesG2 = new TreeSet<PDGNode>();
		this.declaredLocalVariablesInAdditionallyMatchedNodesG1 = new LinkedHashSet<AbstractVariable>();
		this.declaredLocalVariablesInAdditionallyMatchedNodesG2 = new LinkedHashSet<AbstractVariable>();
		this.declaredVariablesInRemainingNodesDefinedByMappedNodesG1 = new LinkedHashSet<PlainVariable>();
		this.declaredVariablesInRemainingNodesDefinedByMappedNodesG2 = new LinkedHashSet<PlainVariable>();
		if(getMaximumStateWithMinimumDifferences() != null) {
			this.mappedNodesG1 = getMaximumStateWithMinimumDifferences().getMappedNodesG1();
			this.mappedNodesG2 = getMaximumStateWithMinimumDifferences().getMappedNodesG2();
			findNonMappedNodes(pdg1, getAllNodesInSubTreePDG1(), mappedNodesG1, nonMappedNodesG1);
			findNonMappedNodes(pdg2, getAllNodesInSubTreePDG2(), mappedNodesG2, nonMappedNodesG2);
			for(PDGNode nodeG1 : nonMappedNodesG1) {
				boolean advancedMatch = getCloneStructureRoot().isGapNodeG1InAdditionalMatches(nodeG1);
				List<ASTNodeDifference> differencesForAdvancedMatch = new ArrayList<ASTNodeDifference>();
				if(advancedMatch) {
					additionallyMatchedNodesG1.add(nodeG1);
					for(ASTNodeDifference difference : getNodeDifferences()) {
						if(isExpressionUnderStatement(difference.getExpression1().getExpression(), nodeG1.getASTStatement())) {
							differencesForAdvancedMatch.add(difference);
						}
					}
					List<AbstractVariable> nonAnonymousDeclaredVariablesG1 = new ArrayList<AbstractVariable>();
					Iterator<AbstractVariable> declaredVariableIteratorG1 = nodeG1.getDeclaredVariableIterator();
					while(declaredVariableIteratorG1.hasNext()) {
						AbstractVariable declaredVariableG1 = declaredVariableIteratorG1.next();
						String key1 = declaredVariableG1.getVariableBindingKey();
						String declaringType1 = key1.substring(0, key1.indexOf(";"));
						if(!declaringType1.contains("$")) {
							nonAnonymousDeclaredVariablesG1.add(declaredVariableG1);
						}
					}
					declaredLocalVariablesInAdditionallyMatchedNodesG1.addAll(nonAnonymousDeclaredVariablesG1);
				}
				PDGNodeGap nodeGap = new PDGNodeGap(nodeG1, null, advancedMatch, differencesForAdvancedMatch);
				CloneStructureNode node = new CloneStructureNode(nodeGap);
				PDGBlockNode tryNode = pdg1.isDirectlyNestedWithinBlockNode(nodeG1);
				if(tryNode != null) {
					CloneStructureNode cloneStructureTry = getCloneStructureRoot().findNodeG1(tryNode);
					if(cloneStructureTry != null) {
						node.setParent(cloneStructureTry);
					}
					else {
						getCloneStructureRoot().addGapChild(node);
					}
				}
				else {
					getCloneStructureRoot().addGapChild(node);
				}
			}
			nonMappedNodesG1.removeAll(additionallyMatchedNodesG1);
			mappedNodesG1.addAll(additionallyMatchedNodesG1);
			for(PDGNode nodeG2 : nonMappedNodesG2) {
				boolean advancedMatch = getCloneStructureRoot().isGapNodeG2InAdditionalMatches(nodeG2);
				List<ASTNodeDifference> differencesForAdvancedMatch = new ArrayList<ASTNodeDifference>();
				if(advancedMatch) {
					additionallyMatchedNodesG2.add(nodeG2);
					for(ASTNodeDifference difference : getNodeDifferences()) {
						if(isExpressionUnderStatement(difference.getExpression2().getExpression(), nodeG2.getASTStatement())) {
							differencesForAdvancedMatch.add(difference);
						}
					}
					List<AbstractVariable> nonAnonymousDeclaredVariablesG2 = new ArrayList<AbstractVariable>();
					Iterator<AbstractVariable> declaredVariableIteratorG2 = nodeG2.getDeclaredVariableIterator();
					while(declaredVariableIteratorG2.hasNext()) {
						AbstractVariable declaredVariableG2 = declaredVariableIteratorG2.next();
						String key2 = declaredVariableG2.getVariableBindingKey();
						String declaringType2 = key2.substring(0, key2.indexOf(";"));
						if(!declaringType2.contains("$")) {
							nonAnonymousDeclaredVariablesG2.add(declaredVariableG2);
						}
					}
					declaredLocalVariablesInAdditionallyMatchedNodesG2.addAll(nonAnonymousDeclaredVariablesG2);
				}
				PDGNodeGap nodeGap = new PDGNodeGap(null, nodeG2, advancedMatch, differencesForAdvancedMatch);
				CloneStructureNode node = new CloneStructureNode(nodeGap);
				PDGBlockNode tryNode = pdg2.isDirectlyNestedWithinBlockNode(nodeG2);
				if(tryNode != null) {
					CloneStructureNode cloneStructureTry = getCloneStructureRoot().findNodeG2(tryNode);
					if(cloneStructureTry != null) {
						node.setParent(cloneStructureTry);
					}
					else {
						getCloneStructureRoot().addGapChild(node);
					}
				}
				else {
					getCloneStructureRoot().addGapChild(node);
				}
			}
			nonMappedNodesG2.removeAll(additionallyMatchedNodesG2);
			mappedNodesG2.addAll(additionallyMatchedNodesG2);
			findRenamedVariables(renamedVariables, renamedFields);
			findPassedParameters();
			List<Expression> expressions1 = new ArrayList<Expression>();
			List<Expression> expressions2 = new ArrayList<Expression>();
			List<AbstractExpression> fieldAccessReplacedWithGetterExpressions1 = new ArrayList<AbstractExpression>();
			List<AbstractExpression> fieldAccessReplacedWithGetterExpressions2 = new ArrayList<AbstractExpression>();
			List<AbstractExpression> fieldAssignmentReplacedWithSetterExpressions1 = new ArrayList<AbstractExpression>();
			List<AbstractExpression> fieldAssignmentReplacedWithSetterExpressions2 = new ArrayList<AbstractExpression>();
			for(ASTNodeDifference nodeDifference : getNodeDifferences()) {
				if(!nodeDifference.containsDifferenceType(DifferenceType.FIELD_ACCESS_REPLACED_WITH_GETTER) && !nodeDifference.containsDifferenceType(DifferenceType.FIELD_ASSIGNMENT_REPLACED_WITH_SETTER)) {
					Expression expression1 = nodeDifference.getExpression1().getExpression();
					Expression expr1 = ASTNodeDifference.getParentExpressionOfMethodNameOrTypeName(expression1);
					if(expression1.equals(expr1)) {
						expressions1.add(expression1);
					}
					else {
						expressions1.add(expr1);
					}
					Expression expression2 = nodeDifference.getExpression2().getExpression();
					Expression expr2 = ASTNodeDifference.getParentExpressionOfMethodNameOrTypeName(expression2);
					if(expression2.equals(expr2)) {
						expressions2.add(expression2);
					}
					else {
						expressions2.add(expr2);
					}
				}
				else if(nodeDifference.containsDifferenceType(DifferenceType.FIELD_ACCESS_REPLACED_WITH_GETTER)) {
					fieldAccessReplacedWithGetterExpressions1.add(nodeDifference.getExpression1());
					fieldAccessReplacedWithGetterExpressions2.add(nodeDifference.getExpression2());
				}
				else if(nodeDifference.containsDifferenceType(DifferenceType.FIELD_ASSIGNMENT_REPLACED_WITH_SETTER)) {
					fieldAssignmentReplacedWithSetterExpressions1.add(nodeDifference.getExpression1());
					fieldAssignmentReplacedWithSetterExpressions2.add(nodeDifference.getExpression2());
				}
			}
			ITypeBinding commonSuperclass = ASTNodeMatcher.commonSuperType(pdg1.getMethod().getMethodDeclaration().resolveBinding().getDeclaringClass(),
					pdg2.getMethod().getMethodDeclaration().resolveBinding().getDeclaringClass());
			findLocallyAccessedFields(pdg1, mappedNodesG1, commonSuperclass, directlyAccessedLocalFieldsG1, indirectlyAccessedLocalFieldsG1, directlyModifiedLocalFieldsG1, indirectlyModifiedLocalFieldsG1,
					directlyAccessedLocalMethodsG1, indirectlyAccessedLocalMethodsG1, expressions1, fieldAccessReplacedWithGetterExpressions1, fieldAssignmentReplacedWithSetterExpressions1, this.iCompilationUnit1);
			findLocallyAccessedFields(pdg2, mappedNodesG2, commonSuperclass, directlyAccessedLocalFieldsG2, indirectlyAccessedLocalFieldsG2, directlyModifiedLocalFieldsG2, indirectlyModifiedLocalFieldsG2,
					directlyAccessedLocalMethodsG2, indirectlyAccessedLocalMethodsG2, expressions2, fieldAccessReplacedWithGetterExpressions2, fieldAssignmentReplacedWithSetterExpressions2, this.iCompilationUnit2);
			this.variablesToBeReturnedG1 = variablesToBeReturned(pdg1, getRemovableNodesG1());
			this.variablesToBeReturnedG2 = variablesToBeReturned(pdg2, getRemovableNodesG2());
			checkCloneStructureNodeForPreconditions(getCloneStructureRoot());
			processNonMappedNodesMovableBeforeAndAfter();
			checkPreconditionsAboutReturnedVariables();
			checkIfAllPossibleExecutionFlowsEndInReturn();
			this.cloneRefactoringType = computeRefactoringType();
			this.declaredVariablesInRemainingNodesDefinedByMappedNodesG1 = findDeclaredVariablesInRemainingNodesDefinedByMappedNodes(pdg1, getRemovableNodesG1());
			this.declaredVariablesInRemainingNodesDefinedByMappedNodesG2 = findDeclaredVariablesInRemainingNodesDefinedByMappedNodes(pdg2, getRemovableNodesG2());
		}
	}

	private CloneStructureNode getCloneStructureRoot() {
		return cloneStructureRoot;
	}

	private MappingState getMaximumStateWithMinimumDifferences() {
		return finalState;
	}

	private TreeSet<PDGNode> getAllNodesInSubTreePDG1() {
		return allNodesInSubTreePDG1;
	}

	private TreeSet<PDGNode> getAllNodesInSubTreePDG2() {
		return allNodesInSubTreePDG2;
	}

	private void findNonMappedNodes(PDG pdg, TreeSet<PDGNode> allNodes, Set<PDGNode> mappedNodes, Set<PDGNode> nonMappedNodes) {
		for(PDGNode pdgNode : allNodes) {
			if(!mappedNodes.contains(pdgNode)) {
				nonMappedNodes.add(pdgNode);
			}
			PDGBlockNode blockNode = pdg.isDirectlyNestedWithinBlockNode(pdgNode);
			if(blockNode != null && nonMappedNodes.contains(blockNode) && mappedNodes.contains(pdgNode)) {
				mappedNodes.remove(pdgNode);
				nonMappedNodes.add(pdgNode);
			}
			PDGNode controlParent = pdgNode.getControlDependenceParent();
			if(controlParent != null && nonMappedNodes.contains(controlParent) && mappedNodes.contains(pdgNode)) {
				if(pdgNode instanceof PDGControlPredicateNode && finalState.containsNodeInMappings(pdgNode)) {
					continue;
				}
				mappedNodes.remove(pdgNode);
				nonMappedNodes.add(pdgNode);
			}
		}
	}

	private Set<PlainVariable> findDeclaredVariablesInRemainingNodesDefinedByMappedNodes(PDG pdg, Set<PDGNode> mappedNodes) {
		Set<PDGNode> remainingNodes = new TreeSet<PDGNode>();
		Iterator<GraphNode> iterator = pdg.getNodeIterator();
		while(iterator.hasNext()) {
			PDGNode pdgNode = (PDGNode)iterator.next();
			if(!mappedNodes.contains(pdgNode)) {
				remainingNodes.add(pdgNode);
			}
		}
		Set<PlainVariable> declaredVariablesInRemainingNodes = new LinkedHashSet<PlainVariable>();
		for(PDGNode remainingNode : remainingNodes) {
			Iterator<AbstractVariable> declaredVariableIterator = remainingNode.getDeclaredVariableIterator();
			while(declaredVariableIterator.hasNext()) {
				AbstractVariable declaredVariable = declaredVariableIterator.next();
				if(declaredVariable instanceof PlainVariable) {
					declaredVariablesInRemainingNodes.add((PlainVariable)declaredVariable);
				}
			}
		}
		Set<PlainVariable> declaredVariablesInRemainingNodesDefinedByMappedNodes = new LinkedHashSet<PlainVariable>();
		for(PlainVariable variable : declaredVariablesInRemainingNodes) {
			int definedInMappedNodesCounter = 0;
			for(PDGNode mappedNode : mappedNodes) {
				if(mappedNode.definesLocalVariable(variable)) {
					definedInMappedNodesCounter++;
				}
			}
			if(definedInMappedNodesCounter > 0) {
				declaredVariablesInRemainingNodesDefinedByMappedNodes.add(variable);
			}
		}
		return declaredVariablesInRemainingNodesDefinedByMappedNodes;
	}

	private void findDeclaredVariablesInMappedNodesUsedByNonMappedNodes(PDG pdg, Set<PDGNode> mappedNodes, Set<PDGNode> unmappedNodes, Set<AbstractVariable> variables) {
		Set<PDGNode> nodes = new TreeSet<PDGNode>();
		nodes.addAll(mappedNodes);
		nodes.addAll(unmappedNodes);
		for(PDGNode mappedNode : nodes) {
			for(Iterator<AbstractVariable> declaredVariableIterator = mappedNode.getDeclaredVariableIterator(); declaredVariableIterator.hasNext();) {
				AbstractVariable declaredVariable = declaredVariableIterator.next();
				for(GraphNode node : pdg.getNodes()) {
					PDGNode pdgNode = (PDGNode)node;
					if(!mappedNodes.contains(pdgNode) && !pdgNode.equals(mappedNode)) {
						if(pdgNode.usesLocalVariable(declaredVariable) || pdgNode.definesLocalVariable(declaredVariable)) {
							variables.add(declaredVariable);
							break;
						}
					}
				}
			}
			//special handling for parameters
			ListIterator<ParameterObject> parameterIterator = pdg.getMethod().getParameterListIterator();
			while(parameterIterator.hasNext()) {
				ParameterObject parameter = parameterIterator.next();
				VariableDeclaration variableDeclaration = parameter.getVariableDeclaration();
				AbstractVariable definedVariable = null;
				for(Iterator<AbstractVariable> definedVariableIterator = mappedNode.getDefinedVariableIterator(); definedVariableIterator.hasNext();) {
					AbstractVariable variable = definedVariableIterator.next();
					if(variable instanceof PlainVariable && variable.getVariableBindingKey().equals(variableDeclaration.resolveBinding().getKey())) {
						definedVariable = variable;
						break;
					}
				}
				if(definedVariable != null) {
					for(GraphNode node : pdg.getNodes()) {
						PDGNode pdgNode = (PDGNode)node;
						if(!mappedNodes.contains(pdgNode) && !pdgNode.equals(mappedNode)) {
							if(pdgNode.usesLocalVariable(definedVariable) || pdgNode.definesLocalVariable(definedVariable)) {
								variables.add(definedVariable);
								break;
							}
						}
					}
				}
			}
		}
	}

	private void findPassedParameters() {
		Set<AbstractVariable> passedParametersG1 = extractPassedParameters(pdg1, mappedNodesG1);
		Set<AbstractVariable> passedParametersG2 = extractPassedParameters(pdg2, mappedNodesG2);
		Set<VariableDeclaration> variableDeclarationsInMethod1 = pdg1.getVariableDeclarationsInMethod();
		Set<VariableDeclaration> variableDeclarationsInMethod2 = pdg2.getVariableDeclarationsInMethod();
		Set<VariableDeclaration> variableDeclarationsAndFieldAccessedInMethod1 = pdg1.getVariableDeclarationsAndAccessedFieldsInMethod();
		Set<VariableDeclaration> variableDeclarationsAndFieldAccessedInMethod2 = pdg2.getVariableDeclarationsAndAccessedFieldsInMethod();
		for(PDGNodeMapping nodeMapping : getMaximumStateWithMinimumDifferences().getSortedNodeMappings()) {
			PDGNode nodeG1 = nodeMapping.getNodeG1();
			PDGNode nodeG2 = nodeMapping.getNodeG2();
			List<AbstractVariable> nonAnonymousDeclaredVariablesG1 = new ArrayList<AbstractVariable>();
			List<AbstractVariable> anonymousDeclaredVariablesG1 = new ArrayList<AbstractVariable>();
			Iterator<AbstractVariable> declaredVariableIteratorG1 = nodeG1.getDeclaredVariableIterator();
			while(declaredVariableIteratorG1.hasNext()) {
				AbstractVariable declaredVariableG1 = declaredVariableIteratorG1.next();
				String key1 = declaredVariableG1.getVariableBindingKey();
				for(VariableDeclaration variableDeclaration : variableDeclarationsInMethod1) {
					IVariableBinding declaredVariableBinding = variableDeclaration.resolveBinding();
					if(declaredVariableBinding.getKey().equals(key1)) {
						if(!declaredVariableBinding.getDeclaringMethod().getDeclaringClass().isAnonymous()) {
							nonAnonymousDeclaredVariablesG1.add(declaredVariableG1);
						}
						else {
							anonymousDeclaredVariablesG1.add(declaredVariableG1);
						}
					}
				}
			}
			List<AbstractVariable> nonAnonymousDeclaredVariablesG2 = new ArrayList<AbstractVariable>();
			List<AbstractVariable> anonymousDeclaredVariablesG2 = new ArrayList<AbstractVariable>();
			Iterator<AbstractVariable> declaredVariableIteratorG2 = nodeG2.getDeclaredVariableIterator();
			while(declaredVariableIteratorG2.hasNext()) {
				AbstractVariable declaredVariableG2 = declaredVariableIteratorG2.next();
				String key2 = declaredVariableG2.getVariableBindingKey();
				for(VariableDeclaration variableDeclaration : variableDeclarationsInMethod2) {
					IVariableBinding declaredVariableBinding = variableDeclaration.resolveBinding();
					if(declaredVariableBinding.getKey().equals(key2)) {
						if(!declaredVariableBinding.getDeclaringMethod().getDeclaringClass().isAnonymous()) {
							nonAnonymousDeclaredVariablesG2.add(declaredVariableG2);
						}
						else {
							anonymousDeclaredVariablesG2.add(declaredVariableG2);
						}
					}
				}
			}
			int min = Math.min(nonAnonymousDeclaredVariablesG1.size(), nonAnonymousDeclaredVariablesG2.size());
			for(int i=0; i<min; i++) {
				AbstractVariable declaredVariableG1 = nonAnonymousDeclaredVariablesG1.get(i);
				AbstractVariable declaredVariableG2 = nonAnonymousDeclaredVariablesG2.get(i);
				ArrayList<AbstractVariable> declaredVariables = new ArrayList<AbstractVariable>();
				declaredVariables.add(declaredVariableG1);
				declaredVariables.add(declaredVariableG2);
				VariableBindingKeyPair keyPair = new VariableBindingKeyPair(declaredVariableG1.getVariableBindingKey(),
						declaredVariableG2.getVariableBindingKey());
				declaredLocalVariablesInMappedNodes.put(keyPair, declaredVariables);
			}
			int min2 = Math.min(anonymousDeclaredVariablesG1.size(), anonymousDeclaredVariablesG2.size());
			for(int i=0; i<min2; i++) {
				AbstractVariable declaredVariableG1 = anonymousDeclaredVariablesG1.get(i);
				AbstractVariable declaredVariableG2 = anonymousDeclaredVariablesG2.get(i);
				ArrayList<AbstractVariable> declaredVariables = new ArrayList<AbstractVariable>();
				declaredVariables.add(declaredVariableG1);
				declaredVariables.add(declaredVariableG2);
				VariableBindingKeyPair keyPair = new VariableBindingKeyPair(declaredVariableG1.getVariableBindingKey(),
						declaredVariableG2.getVariableBindingKey());
				declaredLocalVariablesInMappedNodesWithinAnonymousClass.put(keyPair, declaredVariables);
			}
			Set<AbstractVariable> dataDependences1 = nodeG1.incomingDataDependencesFromNodesDeclaringOrDefiningVariables();
			Set<AbstractVariable> dataDependences2 = nodeG2.incomingDataDependencesFromNodesDeclaringOrDefiningVariables();
			dataDependences1.retainAll(passedParametersG1);
			dataDependences2.retainAll(passedParametersG2);
			List<AbstractVariable> variables1 = new ArrayList<AbstractVariable>(dataDependences1);
			List<AbstractVariable> variables2 = new ArrayList<AbstractVariable>(dataDependences2);
			if(dataDependences1.size() == dataDependences2.size()) {
				List<String> variableNames1 = new ArrayList<String>();
				List<String> variableNames2 = new ArrayList<String>();
				for(int i=0; i<variables1.size(); i++) {
					variableNames1.add(variables1.get(i).getVariableName());
					AbstractVariable variable2 = variables2.get(i);
					String renamedVariableName = findRenamedVariableName2(variable2);
					if(renamedVariableName != null)
						variableNames2.add(renamedVariableName);
					else
						variableNames2.add(variable2.getVariableName());
				}
				if(variableNames1.containsAll(variableNames2) && variableNames2.containsAll(variableNames1) &&
						variableNames1.size() > 0 && variableNames2.size() > 0) {
					//sort variables based on their names
					List<AbstractVariable> sortedVariables1 = new ArrayList<AbstractVariable>();
					List<AbstractVariable> sortedVariables2 = new ArrayList<AbstractVariable>();
					for(int i=0; i<variables1.size(); i++) {
						AbstractVariable variable1 = variables1.get(i);
						IVariableBinding variableBinding1 = getVariableBinding(variable1, variableDeclarationsAndFieldAccessedInMethod1);
						sortedVariables1.add(variable1);
						for(int j=0; j<variables2.size(); j++) {
							AbstractVariable variable2 = variables2.get(j);
							IVariableBinding variableBinding2 = getVariableBinding(variable2, variableDeclarationsAndFieldAccessedInMethod2);
							String renamedVariableName = findRenamedVariableName2(variable2);
							if((variable2.getVariableName().equals(variable1.getVariableName()) ||
									variable1.getVariableName().equals(renamedVariableName)) &&
									(variable2.getVariableType().equals(variable1.getVariableType()) ||
											variableBinding1 != null && variableBinding2 != null && ASTNodeMatcher.commonSuperType(variableBinding1.getType(), variableBinding2.getType()) != null)) {
								sortedVariables2.add(variable2);
								break;
							}
						}
					}
					if(sortedVariables1.size() == sortedVariables2.size()) {
						variables1 = sortedVariables1;
						variables2 = sortedVariables2;
					}
				}
				else {
					//sort variables based on their types (if a variable has the same type with multiple variables, apply first match)
					List<AbstractVariable> sortedVariables1 = new ArrayList<AbstractVariable>();
					List<AbstractVariable> sortedVariables2 = new ArrayList<AbstractVariable>();
					sortVariables(variables1, variables2, sortedVariables1, sortedVariables2);
					if(sortedVariables1.size() == sortedVariables2.size()) {
						variables1 = sortedVariables1;
						variables2 = sortedVariables2;
					}
				}
			}
			else {
				//there is a different number of incoming dependencies
				List<AbstractVariable> sortedVariables1 = new ArrayList<AbstractVariable>();
				List<AbstractVariable> sortedVariables2 = new ArrayList<AbstractVariable>();
				sortVariables(variables1, variables2, sortedVariables1, sortedVariables2);
				if(sortedVariables1.size() == sortedVariables2.size()) {
					variables1 = sortedVariables1;
					variables2 = sortedVariables2;
				}
			}
			for(int i=0; i<variables1.size(); i++) {
				AbstractVariable variable1 = variables1.get(i);
				AbstractVariable variable2 = variables2.get(i);
				if(passedParametersG1.contains(variable1) && passedParametersG2.contains(variable2)) {
					ArrayList<AbstractVariable> variableDeclarations = new ArrayList<AbstractVariable>();
