package fr.inria.diversify.transformation.query;
import fr.inria.diversify.diversification.InputProgram;
import fr.inria.diversify.transformation.ReplaceNewListTransformation;
import fr.inria.diversify.transformation.Transformation;
import fr.inria.diversify.transformation.logger.LogWriter;
import fr.inria.diversify.util.Log;
import org.reflections.Reflections;
import spoon.reflect.code.CtAssignment;
import spoon.reflect.code.CtConstructorCall;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtReturn;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtExecutableReference;
import spoon.reflect.reference.CtTypeReference;
import spoon.support.reflect.code.CtInvocationImpl;
import spoon.support.reflect.code.CtLocalVariableImpl;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.*;
/**
* Created by lguerin on 02/06/15.
*/
public class ReplaceNewListRandomQuery extends TransformationQuery {
private PrintWriter fileWriter;
private HashMap<String,Integer> classes=new HashMap<String,Integer>();
private HashMap<String,Integer> constructors=new HashMap<String,Integer>();
public ReplaceNewListRandomQuery(InputProgram inputProgram) {
super(inputProgram);
}
/**this function collect all CtConstructorCall with the static type List.
* Choose a candidate randomly and search an other constructor which can replace this candidate.
* It create an new CtConstructorCall with the new constructor and create the corresponding transformation.
* @return Transformation
* @throws QueryException
*/
@Override
public Transformation query() throws QueryException {
Reflections reflections=new Reflections("org.apache.commons.collections4.map.LinkedMap.linkedMapList");
ReplaceNewListTransformation transformation = new ReplaceNewListTransformation();
List<CtElement> newClasses = getInputProgram().getAllElement(CtConstructorCall.class);
List<CtConstructorCall> candidates=new ArrayList<>();
for(int i=0;i<newClasses.size();i++){
if(isCandidate((CtConstructorCall)newClasses.get(i))){
CtConstructorCall ctConstructorCall=(CtConstructorCall)newClasses.get(i);
candidates.add(ctConstructorCall);
analyzeConstructor(ctConstructorCall);
}
}
if(!candidates.isEmpty()){
Random r=new Random();
int random = (int)(Math.random() * candidates.size());
transformation.setTransplantationPoint((CtConstructorCall)candidates.get(random));
CtConstructorCall newCtConstructorCall= constructCtConstructorCall((CtConstructorCall) candidates.get(random));
transformation.setTransplant(newCtConstructorCall);
printResult(candidates.size());
return transformation;
}
return null;
}
/**
* Check if the ctConstructorCall have the static type List.
* Return true if this ctConstructorCall can be a candidate for this transformation
* @param ctConstructorCall
* @return boolean
*/
private boolean isCandidate(CtConstructorCall ctConstructorCall) {
CtElement parent=ctConstructorCall.getParent();
Class staticType=null;
try{
ctConstructorCall.getType().getActualClass();
}catch (Exception e){
Log.debug("The class can't be load");
}
try{
if(parent instanceof CtLocalVariableImpl ) {
staticType= ((CtLocalVariableImpl) parent).getType().getActualClass();
}else if(parent instanceof CtAssignment){
staticType= ((CtAssignment) parent).getType().getActualClass();
}else if(parent instanceof spoon.reflect.declaration.CtField){
staticType= ((spoon.reflect.declaration.CtField) parent).getType().getActualClass();
}else if(parent instanceof CtInvocationImpl){
parent=(CtInvocationImpl) parent;
List<CtExpression> argumentsInvocation=((CtInvocationImpl) parent).getArguments();
CtExecutableReference executable=((CtInvocationImpl) parent).getExecutable();
List<CtTypeReference> argumentsExecutable=executable.getParameters();
for(int i=0; i<argumentsInvocation.size();i++){
if(argumentsInvocation.get(i).equals(ctConstructorCall)){
staticType=argumentsExecutable.get(i).getActualClass();
}
}
}else if(parent instanceof CtReturn){
while (! (parent instanceof CtMethod) ){parent=parent.getParent();}
staticType=((CtMethod) parent).getType().getActualClass();
}
}catch(Exception e){
return false;
}
return (staticType!=null && staticType.getSimpleName().equals("List"));
}
/**
* this function construct an ctConstructorCall which can replace the ctConstructor
* @param ctConstructorCall
* @return CtConstructorCall
*/
private CtConstructorCall constructCtConstructorCall(CtConstructorCall ctConstructorCall) {
Factory factory=ctConstructorCall.getFactory();
CtConstructorCall newConstructorCall=factory.Core().createConstructorCall();
List<CtExpression<?>> arguments=ctConstructorCall.getArguments();
Class typeArgs[]=new Class[arguments.size()];
for(int i=0;i<arguments.size();i++){typeArgs[i]=arguments.get(i).getType().getActualClass();}
List<Constructor> potentialsConstructors= getPotentialsConstructors(getSubTypeOfJavaUtilList());
Constructor selectedConstructor=selectConstructor(potentialsConstructors, typeArgs,ctConstructorCall);
if(selectedConstructor!=null){
Class other=selectedConstructor.getDeclaringClass();
newConstructorCall.setType(factory.Type().createReference(other));
newConstructorCall.setArguments(arguments);
}else{
newConstructorCall=ctConstructorCall;
}
return newConstructorCall;
}
/**
* Choose a constructor in the potentials constructors list which have compatibles parameters with typeArgs.
* The constructor chosen should be different to the constructor of ctConstructorCall.
* If there are not a compatible constructor, this function return null.
* @param potentialsConstructors
* @param typeArgs
* @param ctConstructorCall
* @return Constructor
*/
private Constructor selectConstructor(List<Constructor> potentialsConstructors, Class[] typeArgs, CtConstructorCall ctConstructorCall) {
for(int i=0;i<potentialsConstructors.size();i++){
Class params[]=potentialsConstructors.get(i).getParameterTypes();
if(compareConstructorsArguments(params,typeArgs) && (!potentialsConstructors.get(i).getName().equals(ctConstructorCall.getType().getActualClass().getName()))){
return potentialsConstructors.get(i);
}
}
return null;
}
/**
* This function compare the arguments of a constructor and the arguments of a constructorCall
* for example:
* compareConstructorsArguments([Collection.class],[ArrayList.class]) return True because an ArrayList can instantiate a Collection.
* compareConstructorsArguments([ArrayList.class],[integer.class]) return False because an integer can't instantiate an ArrayList.
* compareConstructorsArguments([Collection.class],[ArrayList.class, Integer.class]) return False because the sizes are different.
* @param constructorArguments
* @param constructorCallArguments
* @return boolean
*/
private boolean compareConstructorsArguments(Class[] constructorArguments, Class[] constructorCallArguments){
Reflections reflection=new Reflections(".*");
if(constructorArguments.length!= constructorCallArguments.length){
return false;
}
for(int i=0;i<constructorArguments.length;i++){
if( !matchClass(constructorArguments[i], constructorCallArguments[i] )){
return false;
}
}
return true;
}
/**
* Check if potentialSubType is a subType of potentialSuperType
* @param potentialSuperType
* @param potentialSubType
* @return boolean
*/
private boolean matchClass(Class potentialSuperType, Class potentialSubType){
if(potentialSubType==null){
return false;
}else if(potentialSuperType.getName().equals(potentialSubType.getName())){
return true;
}else if(potentialSubType.getName().equals(Object.class.getName())){
return false;
}else{
List<Class> superClassList=new ArrayList<>(Arrays.asList(potentialSubType.getInterfaces()));
superClassList.add(potentialSubType.getSuperclass());
if(superClassList.isEmpty()){
return false;
}else{
for(int i=0;i<superClassList.size();i++){
if(matchClass(potentialSuperType, superClassList.get(i))){
return true;
}
}
return false;
}
}
}
/**
* Return a list which contains all subTypes of List in java.util
* @return List<Class>: SubType of List
*/
private List<Class> getSubTypeOfJavaUtilList() {
Reflections reflections = new Reflections(".*");
Set<Class<? extends List>> subTypes=reflections.getSubTypesOf(List.class);
List<Class> javaUtilsubTypes=new ArrayList<>();
Iterator<Class<? extends List>> it=subTypes.iterator();
while(it.hasNext()){
Class current=it.next();
if(current.toString().startsWith("class java.util")){
javaUtilsubTypes.add(current);
}
}
return javaUtilsubTypes;
}
/**
* Return all constructors of classes in classList
* @param classList
* @return List<Constructor>
*/
private List<Constructor> getPotentialsConstructors(List<Class> classList){
List<Constructor> potentialsConstructors=new ArrayList<>();
for(int i=0;i<classList.size();i++){
try{
Constructor constructorsTab[]=classList.get(i).getConstructors();
for(int j=0;j<constructorsTab.length;j++){ potentialsConstructors.add(constructorsTab[j]);}
}catch (Exception e){}
}
return potentialsConstructors;
}
private void printResult(int nb) {
LogWriter.out("candidates,"+nb);
Set<String> c=classes.keySet();
Iterator<String> it=c.iterator();
LogWriter.out("Classes,"+nb);
while(it.hasNext()){
String current =it.next();
LogWriter.out(current+","+classes.get(current));
}
LogWriter.out("Constructors,"+nb);
c=constructors.keySet();
it=c.iterator();
while(it.hasNext()){
String current =it.next();
LogWriter.out(current+","+constructors.get(current));
}
}
private void analyzeConstructor(CtConstructorCall ctConstructorCall) {
CtTypeReference classConstructor=ctConstructorCall.getType();
String nameConstructor=classConstructor.getQualifiedName();
String typesArguments=nameConstructor+"(";
List<CtExpression> arguments=ctConstructorCall.getArguments();
for(int i=0;i<arguments.size();i++){typesArguments=typesArguments+" "+arguments.get(i).getType().getSimpleName();}
typesArguments=typesArguments+")";
if(classes.get(nameConstructor)!=null){
classes.put(nameConstructor, (classes.get(nameConstructor)+1) );
}else{
classes.put(nameConstructor,1);
}
if(constructors.get(typesArguments)!=null){
constructors.put(typesArguments, (constructors.get(typesArguments) + 1));
}else{
constructors.put(typesArguments,1);
}
}
}