/**
* Copyright (C) 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mujava.op;
import java.io.*;
import openjava.mop.*;
import openjava.ptree.*;
/**
* <p>Generate OAN (Arguments of overloading method call change) --
* change the number of the argument in method invocations,
* but only if there is an overloading method that can accept
* the new argument list
* </p>
* @author Yu-Seung Ma
* @version 1.0
*/
public class OAN extends mujava.op.util.Mutator
{
Environment env = null;
OverloadingHelper oM_helper = new OverloadingHelper();
MethodCall method_call= null;
public OAN(FileEnvironment file_env, ClassDeclaration cdecl, CompilationUnit comp_unit)
{
super( file_env, comp_unit );
this.env = getEnvironment();
}
public OJClass bindedType(String name)
{
Environment env = getEnvironment();
OJClass bindedtype = env.lookupBind( name );
return bindedtype;
}
private static final String getFirst( String qname )
{
if (qname == null)
return null;
int dot = qname.indexOf( '.' );
if (dot == -1)
return qname;
return qname.substring( 0, dot );
}
/**
* Generate OAN mutants
* @param p
* @param self
* @param target
*/
public void generateMutant(MethodCall p, OJMethod self, OJMethod target)
{
int i, j;
int num;
int[][] compatibleIndex;
compatibleIndex = oM_helper.genCompatibleLocations(self, target);
ExpressionList argList;
Expression arg_exp;
String mutant = null;
String args = null;
if (compatibleIndex == null)
{
mutant = genMutantCode(p, null);
outputToFile(p, mutant);
}
else
{
try
{
num = compatibleIndex.length;
for (i=0; i<num; i++)
{
for (j=0; j<target.getParameterTypes().length; j++)
{
argList = p.getArguments();
arg_exp = argList.get(compatibleIndex[i][j]);
if (args == null)
args = arg_exp.toString();
else
args = args + arg_exp.toString();
if (j != target.getParameterTypes().length-1)
{
args = args + ",";
}
}
mutant = genMutantCode(p, args);
outputToFile(p, mutant);
args = null;
}
} catch(Exception e)
{
System.out.println("Error " + e);
}
}
}
/**
* Prepare a string of method call with the number of arguments changed
* @param p
* @param args
* @return mutated line
*/
public String genMutantCode(MethodCall p, String args)
{
String mutant = p.toString();
int index = mutant.lastIndexOf("(");
mutant = mutant.substring(0, index);
if (args == null)
{
mutant = mutant + "()";
}
else
{
mutant = mutant + "(" + args + ")";
}
return mutant;
}
/**
* Retrieve the methods of the same number of arguments
* @param c
* @param name
* @return list of methods
*/
public OJMethod[] getMethods(OJClass c, String name)
{
int i;
int num = 0;
OJMethod[] m = c.getDeclaredMethods();
boolean[] same = new boolean[m.length];
for (i=0; i<m.length; i++)
{
if (name.equals(m[i].getName()))
{
same[i] = true;
num++;
}
else
{
same[i] = false;
}
}
OJMethod[] result = new OJMethod[num];
num = 0;
for (i=0; i<m.length; i++)
{
if (same[i])
{
result[num] = m[i];
num++;
}
}
return result;
}
public int findIndex(OJMethod[] m, ExpressionList args)
{
OJClass type = null;
OJClass[] pList = null;
boolean find_flag = false;
if (m != null)
{
try
{
for ( int i=0; i<m.length; i++)
{
pList = m[i].getParameterTypes();
if (pList.length == args.size())
{
find_flag = true;
for (int j=0; j<pList.length; j++)
{
Expression one_parameter = args.get(j);
type = getType(one_parameter);
// We did not consider polymorphism,
// Only consider exact type.
if ( !(type.equals(pList[j])) )
{
find_flag = false;
break;
}
}
if (find_flag)
{
return i;
}
}
}
} catch (Exception e)
{
System.err.print(" -_- OAN Error " + e);
}
}
return -1;
}
/**
* If the number of arguments is 0 or 1, do not generate mutant.
* Otherwise, determine if the method is an overloading method that
* can accept the new list of argument, generate OAN mutants.
*/
public void visit( MethodCall p ) throws ParseTreeException
{
int i;
Expression ref = p.getReferenceExpr();
String method_name = p.getName();
OJClass bindedC = null;
// If the number of arguments is 0 or 1, it is needless
if (p.getArguments().size() < 2)
return;
if (ref == null)
{
Environment env = getEnvironment();
bindedC = env.lookupClass(env.currentClassName());
}
else
{
String first = getFirst(ref.toString());
bindedC = bindedType(first);
}
if (bindedC != null)
{
// find methods whose method name is "method_name"
OJMethod[] oM = getMethods(bindedC, method_name);
// if overloading method exist
if (oM.length > 1)
{
// find index of p among the "method_name" methods
int index = findIndex(oM, p.getArguments());
if (index >= 0)
{
for ( i=0; i<oM.length ; i++ )
{
if ( i != index && oM_helper.sameReturnType(oM[index], oM[i]) &&
oM_helper.compatibleParameter(oM[index], oM[i]) )
{
generateMutant(p, oM[index], oM[i]);
}
}
}
}
}
}
/**
* Output OAN mutants to files
* @param original
* @param mutant
*/
public void outputToFile(MethodCall original, String mutant)
{
if (comp_unit == null)
return;
String f_name;
num++;
f_name = getSourceName(this);
String mutant_dir = getMuantID();
try
{
PrintWriter out = getPrintWriter(f_name);
OAN_Writer writer = new OAN_Writer( mutant_dir, out );
writer.setMutant(original, mutant);
comp_unit.accept( writer );
out.flush();
out.close();
} catch ( IOException e ) {
System.err.println( "fails to create " + f_name );
} catch ( ParseTreeException e ) {
System.err.println( "errors during printing " + f_name );
e.printStackTrace();
}
}
}