package gr.uom.java.ast.decomposition.matching.loop; import gr.uom.java.ast.decomposition.matching.conditional.AbstractControlStructureUtilities; import gr.uom.java.ast.util.ExpressionExtractor; import gr.uom.java.ast.util.StatementExtractor; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ArrayAccess; import org.eclipse.jdt.core.dom.Assignment; import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.EnhancedForStatement; import org.eclipse.jdt.core.dom.Expression; 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.IMethodBinding; 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.MethodDeclaration; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.NumberLiteral; 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.SimpleName; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.TryStatement; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; @SuppressWarnings("unchecked") public class AbstractLoopUtilities { public static boolean isUpdatingVariable(Expression updater, SimpleName variable) { if (updater instanceof PrefixExpression) { PrefixExpression prefixExpression = (PrefixExpression) updater; return isSameVariable(prefixExpression.getOperand(), variable); } else if (updater instanceof PostfixExpression) { PostfixExpression postfixExpression = (PostfixExpression) updater; return isSameVariable(postfixExpression.getOperand(), variable); } else if (updater instanceof Assignment) { Assignment assignment = (Assignment) updater; return isSameVariable(assignment.getLeftHandSide(), variable); } else if (updater instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation) updater; return isSameVariable(methodInvocation.getExpression(), variable); } return false; } private static boolean isSameVariable(Expression expression, SimpleName variable) { if (expression instanceof SimpleName) { SimpleName simpleNameOperand = (SimpleName) expression; return variable.resolveBinding().isEqualTo(simpleNameOperand.resolveBinding()); } return false; } public static boolean isCollectionSizeInvocation(Expression expression) { if (expression instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation) expression; Expression callingExpression = methodInvocation.getExpression(); IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); if (methodBinding != null && callingExpression != null) { return (methodBinding.getName().equals("size") && AbstractLoopUtilities.isCollection(callingExpression.resolveTypeBinding())); } } return false; } public static boolean isDataStructureSizeInvocation(Expression expression) { if (expression instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation) expression; Expression callingExpression = methodInvocation.getExpression(); IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); if (methodBinding != null && callingExpression != null) { AbstractLoopBindingInformation bindingInformation = AbstractLoopBindingInformation.getInstance(); return bindingInformation.dataStructureSizeMethodContains(methodBinding.getKey()); } } return false; } public static boolean isLengthFieldAccess(Expression expression) { SimpleName name = null; ITypeBinding typeBinding = null; if (expression instanceof QualifiedName) { QualifiedName qualifiedName = (QualifiedName) expression; name = qualifiedName.getName(); Name qualifier = qualifiedName.getQualifier(); typeBinding = qualifier.resolveTypeBinding(); } else if (expression instanceof FieldAccess) { FieldAccess fieldAccess = (FieldAccess)expression; name = fieldAccess.getName(); Expression fieldAsccessExpression = fieldAccess.getExpression(); typeBinding = fieldAsccessExpression.resolveTypeBinding(); } if (name != null && typeBinding != null) { IBinding nameBinding = name.resolveBinding(); if (nameBinding != null && nameBinding.getKind() == IBinding.VARIABLE && typeBinding != null) { IVariableBinding nameVariableBinding = (IVariableBinding) nameBinding; return (nameVariableBinding.getName().equals("length") && typeBinding.isArray()); } } return false; } public static boolean isCollection(ITypeBinding typeBinding) { return isSubclassOf(typeBinding, "java.util.AbstractCollection") || isSubinterfaceOf(typeBinding, "java.util.Collection") || isSubinterfaceOf(typeBinding, "java.lang.Iterable"); } public static boolean isSubclassOf(ITypeBinding typeBinding, String qualifiedName) { do { if (typeBinding.getQualifiedName().startsWith(qualifiedName)) { return true; } typeBinding = typeBinding.getSuperclass(); } while (typeBinding != null); return false; } public static boolean isSubinterfaceOf(ITypeBinding typeBinding, String qualifiedName) { if (typeBinding.getQualifiedName().startsWith(qualifiedName)) { return true; } else { ITypeBinding[] superInterfaces = typeBinding.getInterfaces(); for (ITypeBinding superInterface : superInterfaces) { if (isSubinterfaceOf(superInterface, qualifiedName)) { return true; } } return false; } } public static VariableDeclaration getVariableDeclaration(SimpleName variable) { VariableDeclaration variableDeclaration = null; MethodDeclaration method = findParentMethodDeclaration(variable); List<VariableDeclaration> variableDeclarations = getAllVariableDeclarations(method); // find the variable's initializer IBinding binding = variable.resolveBinding(); if (binding.getKind() == IBinding.VARIABLE) { IVariableBinding variableBinding = (IVariableBinding) binding; for (VariableDeclaration currentVariableDeclaration : variableDeclarations) { IVariableBinding currentVariableDeclarationBinding = currentVariableDeclaration.resolveBinding(); if (currentVariableDeclarationBinding.isEqualTo(variableBinding)) { variableDeclaration = currentVariableDeclaration; break; } } } return variableDeclaration; } private static List<VariableDeclaration> getAllVariableDeclarations(MethodDeclaration method) { StatementExtractor statementExtractor = new StatementExtractor(); ExpressionExtractor expressionExtractor = new ExpressionExtractor(); List<SingleVariableDeclaration> methodParameters = method.parameters(); Block methodBody = method.getBody(); List<Statement> variableDeclarationStatements = statementExtractor.getVariableDeclarationStatements(methodBody); List<Expression> variableDeclarationExpressions = expressionExtractor.getVariableDeclarationExpressions(methodBody); List<Statement> enhancedForStatements = statementExtractor.getEnhancedForStatements(methodBody); List<VariableDeclaration> variableDeclarations = new ArrayList<VariableDeclaration>(methodParameters); if (method.getParent() instanceof TypeDeclaration) { TypeDeclaration typeDeclaration = (TypeDeclaration) method.getParent(); FieldDeclaration[] fieldDeclarations = typeDeclaration.getFields(); for (FieldDeclaration fieldDeclaration : fieldDeclarations) { List<VariableDeclarationFragment> fragments = fieldDeclaration.fragments(); variableDeclarations.addAll(fragments); } } for (Statement currentStatement : variableDeclarationStatements) { if (currentStatement instanceof VariableDeclarationStatement) { VariableDeclarationStatement currentVariableDeclarationStatement = (VariableDeclarationStatement) currentStatement; variableDeclarations.addAll(currentVariableDeclarationStatement.fragments()); } } for (Expression currentExpression : variableDeclarationExpressions) { if (currentExpression instanceof VariableDeclarationExpression) { VariableDeclarationExpression currentVariableDeclarationExpression = (VariableDeclarationExpression) currentExpression; variableDeclarations.addAll(currentVariableDeclarationExpression.fragments()); } } for (Statement currentStatement : enhancedForStatements) { if (currentStatement instanceof EnhancedForStatement) { EnhancedForStatement currentEnhancedForStatement = (EnhancedForStatement) currentStatement; variableDeclarations.add(currentEnhancedForStatement.getParameter()); } } return variableDeclarations; } public static MethodDeclaration findParentMethodDeclaration(ASTNode node) { MethodDeclaration parentMethodDeclaration = null; ASTNode parent = node.getParent(); while (parent != null) { if (parent instanceof MethodDeclaration) { parentMethodDeclaration = (MethodDeclaration) parent; break; } parent = parent.getParent(); } return parentMethodDeclaration; } // returns null if the main variable (LHS in an assignment, expression in postfix, prefix, or methodInvocation) is not being UPDATED public static Integer getUpdateValue(Expression updater) { if (updater instanceof PrefixExpression || updater instanceof PostfixExpression) { return AbstractLoopUtilities.getIncrementValue(updater); } else if (updater instanceof Assignment) { Assignment assignment = (Assignment) updater; return assignmentUpdateValue(assignment); } else if (updater instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation) updater; AbstractLoopBindingInformation bindingInformation = AbstractLoopBindingInformation.getInstance(); return bindingInformation.getUpdateMethodValue(methodInvocation.resolveMethodBinding().getMethodDeclaration().getKey()); } return null; } // returns null if specified expression is not a Prefix or PostfixExpression private static Integer getIncrementValue(Expression expression) { Integer incrementValue = null; String operator = null; if (expression instanceof PrefixExpression) { PrefixExpression prefixExpression = (PrefixExpression) expression; operator = prefixExpression.getOperator().toString(); } else if (expression instanceof PostfixExpression) { PostfixExpression postfixExpression = (PostfixExpression) expression; operator = postfixExpression.getOperator().toString(); } if (operator != null && operator.equals("++")) { incrementValue = 1; } else if (operator != null && operator.equals("--")) { incrementValue = (-1); } return incrementValue; } // returns null if the assignment is not UPDATING the variable on the left hand side or if the updateValue cannot be evaluated to an integer private static Integer assignmentUpdateValue(Assignment assignment) { Integer updateValue = null; Expression leftHandSide = assignment.getLeftHandSide(); Assignment.Operator operator = assignment.getOperator(); Expression rightHandSide = assignment.getRightHandSide(); if (operator == Assignment.Operator.PLUS_ASSIGN) { updateValue = AbstractLoopUtilities.getIntegerValue(rightHandSide); } else if (operator == Assignment.Operator.MINUS_ASSIGN) { Integer rightHandSideIntegerValue = AbstractLoopUtilities.getIntegerValue(rightHandSide); if (rightHandSideIntegerValue != null) { updateValue = (-1) * rightHandSideIntegerValue; } } else if (leftHandSide instanceof SimpleName && operator == Assignment.Operator.ASSIGN && rightHandSide instanceof InfixExpression) { SimpleName leftHandSideSimpleName = (SimpleName) leftHandSide; IBinding leftHandSideBinding = leftHandSideSimpleName.resolveBinding(); InfixExpression infixExpression = (InfixExpression) rightHandSide; InfixExpression.Operator infixOperator = infixExpression.getOperator(); Expression rightOperand = infixExpression.getRightOperand(); Expression leftOperand = infixExpression.getLeftOperand(); if (infixOperator.toString().equals("+") || infixOperator.toString().equals("-")) { if (leftOperand instanceof SimpleName) { SimpleName leftOperandSimpleName = (SimpleName) leftOperand; IBinding leftOperandBinding = leftOperandSimpleName.resolveBinding(); if (leftOperandBinding.isEqualTo(leftHandSideBinding)) { Integer rightOperandIntegerValue = AbstractLoopUtilities.getIntegerValue(rightOperand); if (infixOperator.toString().equals("+") && rightOperandIntegerValue != null) { updateValue = rightOperandIntegerValue; } else if (infixOperator.toString().equals("-") && rightOperandIntegerValue != null) { updateValue = (-1) * rightOperandIntegerValue; } } } else if (rightOperand instanceof SimpleName) { SimpleName rightOperandSimpleName = (SimpleName) rightOperand; IBinding rightOperandBinding = rightOperandSimpleName.resolveBinding(); if (rightOperandBinding.isEqualTo(leftHandSideBinding)) { Integer leftOperandIntegerValue = AbstractLoopUtilities.getIntegerValue(leftOperand); if (infixOperator.toString().equals("+") && leftOperandIntegerValue != null) { updateValue = leftOperandIntegerValue; } } } } } return updateValue; } // returns null if specified expression cannot be evaluated to an Integer public static Integer getIntegerValue(Expression expression) { Integer numberLiteralValue = null; if (expression instanceof NumberLiteral) { NumberLiteral numberLiteral = (NumberLiteral) expression; try { numberLiteralValue = Integer.parseInt(numberLiteral.getToken()); } catch (NumberFormatException e) {} } else if (expression instanceof PrefixExpression) { PrefixExpression prefixExpression = (PrefixExpression) expression; PrefixExpression.Operator prefixOperator = prefixExpression.getOperator(); Expression operand = prefixExpression.getOperand(); if (operand instanceof NumberLiteral && (prefixOperator.toString().equals("+") || prefixOperator.toString().equals("-"))) { NumberLiteral numberLiteral = (NumberLiteral) operand; try { numberLiteralValue = Integer.parseInt(prefixOperator.toString() + numberLiteral.getToken()); } catch (NumberFormatException e) {} } } return numberLiteralValue; } // returns a boolean indicating which side of the specified infixExpression the specified variable is on // returns null if it is on neither side public static boolean isVariableLeftOperand(SimpleName variable, InfixExpression infixExpression) { boolean leftOperandIsVariable = false; Expression leftOperand = infixExpression.getLeftOperand(); //Expression rightOperand = infixExpression.getRightOperand(); IBinding infixVariableBinding = variable.resolveBinding(); if (leftOperand instanceof SimpleName) { SimpleName leftOperandSimpleName = (SimpleName) leftOperand; IBinding leftOperandBinding = leftOperandSimpleName.resolveBinding(); if (leftOperandBinding.isEqualTo(infixVariableBinding)) { leftOperandIsVariable = true; } } else if (leftOperand instanceof InfixExpression) { InfixExpression infixLeftOperand = (InfixExpression) leftOperand; boolean left = isVariableLeftOperand(variable, infixLeftOperand); boolean right = isVariableRightOperand(variable, infixLeftOperand); List<Expression> extendedOperands = infixLeftOperand.extendedOperands(); boolean variableFoundInExtendedOperands = false; for (Expression expression : extendedOperands) { if (expression instanceof SimpleName) { SimpleName simpleName = (SimpleName) expression; IBinding simpleNameBinding = simpleName.resolveBinding(); if (simpleNameBinding.isEqualTo(infixVariableBinding)) { variableFoundInExtendedOperands = true; break; } } } if (left || right || variableFoundInExtendedOperands) { leftOperandIsVariable = true; } } return leftOperandIsVariable; } private static boolean isVariableRightOperand(SimpleName variable, InfixExpression infixExpression) { boolean rightOperandIsVariable = false; //Expression leftOperand = infixExpression.getLeftOperand(); Expression rightOperand = infixExpression.getRightOperand(); IBinding infixVariableBinding = variable.resolveBinding(); if (rightOperand instanceof SimpleName) { SimpleName rightOperandSimpleName = (SimpleName) rightOperand; IBinding rightOperandBinding = rightOperandSimpleName.resolveBinding(); if (rightOperandBinding.isEqualTo(infixVariableBinding)) { rightOperandIsVariable = true; } } else if (rightOperand instanceof InfixExpression) { InfixExpression infixRightOperand = (InfixExpression) rightOperand; boolean left = isVariableLeftOperand(variable, infixRightOperand); boolean right = isVariableRightOperand(variable, infixRightOperand); List<Expression> extendedOperands = infixRightOperand.extendedOperands(); boolean variableFoundInExtendedOperands = false; for (Expression expression : extendedOperands) { if (expression instanceof SimpleName) { SimpleName simpleName = (SimpleName) expression; IBinding simpleNameBinding = simpleName.resolveBinding(); if (simpleNameBinding.isEqualTo(infixVariableBinding)) { variableFoundInExtendedOperands = true; break; } } } if (left || right || variableFoundInExtendedOperands) { rightOperandIsVariable = true; } } return rightOperandIsVariable; } public static List<Statement> unBlock(List<Statement> statements) { List<Statement> returnList = new ArrayList<Statement>(); for (Statement currentStatement : statements) { if (currentStatement instanceof Block) { List<Statement> subList = ((Block)currentStatement).statements(); returnList.addAll(unBlock(subList)); } else if (currentStatement instanceof TryStatement) { List<Statement> subList = ((TryStatement)currentStatement).getBody().statements(); returnList.addAll(unBlock(subList)); } else { returnList.add(currentStatement); } } return returnList; } // this method finds the first variable to be initialized (if any) using the control variable to access the data structure it is traversing (if any) public static SimpleName getVariableInitializedUsingControlVariable(ControlVariable controlVariable, Statement conditionalLoopBody) { SimpleName initializedVariableName = null; List<ASTNode> variableDeclarationsAndAssignmentsContainingVariable = getVariableDeclarationsAndAssignmentsContainingAccessUsingVariable(conditionalLoopBody, controlVariable); // find the node in variableDeclarationsAndAssignmentsContainingVariable that has the smallest start position, compare new variable created to the enhancedForVariable and then return it if (variableDeclarationsAndAssignmentsContainingVariable.size() > 0) { Collections.sort(variableDeclarationsAndAssignmentsContainingVariable, new EarliestStartPositionComparator()); ASTNode nodeContainingVariable = variableDeclarationsAndAssignmentsContainingVariable.get(0); if (nodeContainingVariable instanceof VariableDeclaration) { initializedVariableName = ((VariableDeclaration)nodeContainingVariable).getName(); } else if (nodeContainingVariable instanceof Assignment && ((Assignment)nodeContainingVariable).getLeftHandSide() instanceof SimpleName) { initializedVariableName = (SimpleName)((Assignment)nodeContainingVariable).getLeftHandSide(); } } return initializedVariableName; } public static List<ASTNode> getVariableDeclarationsAndAssignmentsContainingAccessUsingVariable(Statement body, ControlVariable variable) { StatementExtractor statementExtractor = new StatementExtractor(); ExpressionExtractor expressionExtractor = new ExpressionExtractor(); ArrayList<ASTNode> returnList = new ArrayList<ASTNode>(); List<Statement> variableDeclarationStatements = statementExtractor.getVariableDeclarationStatements(body); List<Expression> assignments = expressionExtractor.getAssignments(body); for (Statement currentStatement : variableDeclarationStatements) { List<VariableDeclarationFragment> fragments = ((VariableDeclarationStatement)currentStatement).fragments(); for (VariableDeclarationFragment fragment : fragments) { Expression initializer = fragment.getInitializer(); if (isAccessUsingVariable(initializer, variable)) { returnList.add(fragment); } } } for (Expression currentExpression : assignments) { Assignment currentAssignment = (Assignment)currentExpression; Expression rightHandSide = currentAssignment.getRightHandSide(); if (isAccessUsingVariable(rightHandSide, variable)) { returnList.add(currentAssignment); } } List<Expression> methodInvocations = expressionExtractor.getMethodInvocations(body); for (Expression expression : methodInvocations) { if (expression instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation)expression; Expression methodInvocationExpression = methodInvocation.getExpression(); if(methodInvocationExpression != null) { if (isAccessUsingVariable(methodInvocationExpression, variable)) { returnList.add(methodInvocation); } } } } return returnList; } private static boolean isAccessUsingVariable(Expression expression, ControlVariable controlVariable) { List<SimpleName> variableOccurrences = getOccurrencesOfSimpleName(expression, controlVariable.getVariable()); Expression controlVariableDataStructure = controlVariable.getDataStructureExpression(); if (variableOccurrences.size() == 1) { SimpleName variableOccurrence = variableOccurrences.get(0); ASTNode firstParent = variableOccurrence.getParent(); ASTNode secondParent = variableOccurrence.getParent().getParent(); if (firstParent != null && secondParent != null) { // if expression is an array access if (expression instanceof ArrayAccess) { ArrayAccess arrayAccess = (ArrayAccess)expression; Expression array = arrayAccess.getArray(); if ((firstParent.equals(arrayAccess) || (firstParent instanceof PostfixExpression && secondParent.equals(arrayAccess)))) { if (array instanceof Name && controlVariableDataStructure instanceof Name) { Name controlVariableDataStructureName = (Name)controlVariableDataStructure; return ((Name)array).resolveBinding().isEqualTo(controlVariableDataStructureName.resolveBinding()); } else if (array instanceof MethodInvocation && controlVariableDataStructure instanceof MethodInvocation) { MethodInvocation controlVariableDataStructureMethodInvocation = (MethodInvocation)controlVariableDataStructure; return ((MethodInvocation)array).resolveMethodBinding().isEqualTo(controlVariableDataStructureMethodInvocation.resolveMethodBinding()); } } } // if expression is a method invocation else if (expression instanceof MethodInvocation) { MethodInvocation methodInvocation = (MethodInvocation)expression; IMethodBinding methodBinding = methodInvocation.resolveMethodBinding().getMethodDeclaration(); AbstractLoopBindingInformation bindingInformation = AbstractLoopBindingInformation.getInstance(); if (methodInvocation.getExpression() != null) { // if the variable is the expression of the method (ex: variable is an iterator) if (methodInvocation.getExpression().equals(variableOccurrence)) { return bindingInformation.updateMethodValuesContains(methodBinding.getKey()); } // if the variable is an argument of the method OR (the first parent is a postfix expression AND an argument of the method) else if (isExpressionAnArgument(variableOccurrence, methodInvocation) || (firstParent instanceof PostfixExpression && isExpressionAnArgument((PostfixExpression)firstParent, methodInvocation))) { if (bindingInformation.dataStructureAccessMethodsContains(methodBinding.getKey())) { // check that the expression is the variables traversed data structure if (methodInvocation.getExpression() instanceof Name && controlVariableDataStructure instanceof Name) { Name methodExpressionName = (Name)methodInvocation.getExpression(); Name controlVariableDataStructureName = (Name)controlVariableDataStructure; return methodExpressionName.resolveBinding().isEqualTo(controlVariableDataStructureName.resolveBinding()); } else if (methodInvocation.getExpression() instanceof MethodInvocation && controlVariableDataStructure instanceof MethodInvocation) { MethodInvocation methodExpressionMethodInvocation = (MethodInvocation)methodInvocation.getExpression(); MethodInvocation controlVariableDataStructureMethodInvocation = (MethodInvocation)controlVariableDataStructure; return methodExpressionMethodInvocation.resolveMethodBinding().isEqualTo(controlVariableDataStructureMethodInvocation.resolveMethodBinding()); } } } } } } } return false; } private static List<SimpleName> getOccurrencesOfSimpleName(Expression expression, SimpleName simpleName) { List<SimpleName> returnList = new ArrayList<SimpleName>(); ExpressionExtractor expressionExtractor = new ExpressionExtractor(); List<Expression> simpleNames = expressionExtractor.getVariableInstructions(expression); for (Expression currentExpression : simpleNames) { SimpleName currentSimpleName = (SimpleName)currentExpression; IBinding currentSimpleNameBinding = currentSimpleName.resolveBinding(); if (currentSimpleNameBinding != null && currentSimpleNameBinding.isEqualTo(simpleName.resolveBinding())) { returnList.add(currentSimpleName); } } return returnList; } private static boolean isExpressionAnArgument(Expression expression, MethodInvocation methodInvocation) { List<Expression> arguments = methodInvocation.arguments(); for (Expression currentArgument : arguments) { Expression unparenthesizedArgument = AbstractControlStructureUtilities.unparenthesize(currentArgument); if (currentArgument.equals(expression) || unparenthesizedArgument.equals(expression)) { return true; } } return false; } }