SoMoX/Change Impact Case Study
< SoMoX
When changing SoMoX input data model from the SISSy AST to the MoDisco AST model, we have performed a case study on change impact analyses (Lehnert et al. http://www.db-thueringen.de/servlets/DocumentServlet?id=19544).
After unsuccessfully searching for a public available implementation of an according approach, we have decided to implement a query according to Lee et al. (http://dl.acm.org/citation.cfm?id=832261.833279).
While we are working on providing an Eclipse plugin to ease the analysis application, below, the code of the query used in the case study is provided below.
package org.somox.changeimpact;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gmt.modisco.infra.query.core.exception.ModelQueryExecutionException;
import org.eclipse.gmt.modisco.infra.query.core.java.IJavaModelQuery;
import org.eclipse.gmt.modisco.infra.query.core.java.ParameterValueList;
import org.eclipse.gmt.modisco.java.ASTNode;
import org.eclipse.gmt.modisco.java.AbstractMethodDeclaration;
import org.eclipse.gmt.modisco.java.AbstractMethodInvocation;
import org.eclipse.gmt.modisco.java.AbstractTypeDeclaration;
import org.eclipse.gmt.modisco.java.ClassInstanceCreation;
import org.eclipse.gmt.modisco.java.ImportDeclaration;
import org.eclipse.gmt.modisco.java.Model;
import org.eclipse.gmt.modisco.java.NamedElement;
import org.eclipse.gmt.modisco.java.SingleVariableAccess;
import org.eclipse.gmt.modisco.java.Type;
import org.eclipse.gmt.modisco.java.TypeAccess;
import org.eclipse.gmt.modisco.java.VariableDeclaration;
/**
* The impact query published in the study thesis.<br>
* It detects type accesses, method invocations, import statements and variable accesses
* from a source area to a target area. The area is defined as the starting package name.
* @author Oliver Burkhardt
*
*/
public class ImpactQuery2 implements
IJavaModelQuery<Model, Collection<ASTNode>> {
private static final String SOURCE = "org.somox";
private static final String TARGET = "de.fzi.gast";
public Collection<ASTNode> evaluate(final Model context,
final ParameterValueList parameterValues)
throws ModelQueryExecutionException {
Collection<ASTNode> result = new ArrayList<ASTNode>();
TreeIterator<EObject> it = context.eAllContents();
while (it.hasNext()) {
ASTNode element = (ASTNode) it.next();
//has to be handled before checking for source area
if (element instanceof ImportDeclaration) {
handleImportDeclaration(element, result);
}
if (!isInSourceArea(element)) {
continue;
}
if (element instanceof TypeAccess) {
handleTypeAccess(element, result);
} else if (element instanceof AbstractMethodInvocation) {
handleMethodInvocation(element, result);
} else if (element instanceof SingleVariableAccess) {
handleSingleVariableAccess(element, result);
}
}
return result;
}
/**
* Handles an import declaration.
* @param element The import statement to consider.
* @param result the result set
*/
private void handleImportDeclaration(Object element,
Collection<ASTNode> result) {
ImportDeclaration importDecl = (ImportDeclaration) element;
NamedElement importedElement = importDecl.getImportedElement();
if (isInTargetArea(importedElement)) {
result.add(importDecl);
}
}
private boolean isInSourceArea(ASTNode element) {
return computeFullQualifiedName(element).startsWith(SOURCE);
}
/**
* Computes for a given variable access, the location (type) of the accessed variable.
* @param element
* @param result
*/
private void handleSingleVariableAccess(Object element,
Collection<ASTNode> result) {
SingleVariableAccess svaAccess = (SingleVariableAccess) element;
VariableDeclaration variable = svaAccess.getVariable();
if (variable == null) {
return;
}
Type typeContainer = getAbstractTypeDeclarationContainer(variable);
if (typeContainer != null & isInTargetArea(typeContainer)) {
result.add(svaAccess);
}
}
/**
* Computes the container (type) for a given {@link ASTNode} object.
* @param input The {@link ASTNode} object
* @return The type the input is contained in. Null if the {@link ASTNode} is not contained in a type.
*/
private Type getAbstractTypeDeclarationContainer(ASTNode input) {
ASTNode node = input;
while (!(node.eContainer() instanceof AbstractTypeDeclaration)) {
if (! (node.eContainer() instanceof ASTNode)) {
return null;
}
node = (ASTNode) node.eContainer();
}
return (Type) node.eContainer();
}
/**
* Handles a method invocation. If the accessed method in the target area, then the method invocation is added to the result list.
* @param element The object to handle as method invocation.
* @param result The result set.
*/
private void handleMethodInvocation(Object element,
Collection<ASTNode> result) {
AbstractMethodInvocation methodInvoc = (AbstractMethodInvocation) element;
AbstractMethodDeclaration method = methodInvoc.getMethod();
if (isInTargetArea(method)) {
result.add(methodInvoc);
}
}
/**
* Handles a type access. The access is added to the the result list.
* @param element The element to handle as type access.
* @param result The result set.
*/
private void handleTypeAccess(Object element, Collection<ASTNode> result) {
TypeAccess access = (TypeAccess) element;
// Type accesses in ClassInstanceCreations are not handled, because else they would be counted twice.
if (access.eContainer() instanceof ClassInstanceCreation) {
return;
}
if (access.getType() instanceof AbstractTypeDeclaration) {
AbstractTypeDeclaration atd = (AbstractTypeDeclaration) access
.getType();
if(atd != null & isInTargetArea(atd)){
result.add(access);
}
}
}
/**
* Returns if the {@link ASTNode} object is in the target area.
* @param type The {@link ASTNode} input object.
* @return if the input is in the target area
*/
private boolean isInTargetArea(ASTNode type) {
return computeFullQualifiedName(type).startsWith(TARGET);
}
/**
* Computes the full qualified name for an {@link ASTNode} object
* @param input the {@link ASTNode} object
* @return the full qualified name
*/
private static String computeFullQualifiedName(ASTNode input) {
EObject pack = input;
String result = "";
if (pack instanceof NamedElement) {
result = ((NamedElement) pack).getName();
}
while (pack != null) {
if (pack.eContainer() != null
&& pack.eContainer() instanceof NamedElement) {
pack = pack.eContainer();
result = ((NamedElement) pack).getName() + "." + result;
} else {
pack = pack.eContainer();
}
}
return result;
}
}