/**
* Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
*/
package org.evosuite.testcarver.codegen;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.set.hash.TIntHashSet;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Assignment.Operator;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ImportDeclaration;
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.Modifier.ModifierKeyword;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.evosuite.testcarver.capture.CaptureLog;
import org.evosuite.testcarver.capture.CaptureUtil;
import org.evosuite.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class JUnitCodeGenerator implements ICodeGenerator<CompilationUnit>
{
private static final Logger logger = LoggerFactory.getLogger(JUnitCodeGenerator.class);
//--- source generation
private final TIntObjectHashMap<String> oidToVarMapping;
private final TIntObjectHashMap<Class<?>> oidToTypeMapping;
private int varCounter;
private TIntHashSet failedRecords;
private boolean postprocessing;
private boolean isNewInstanceMethodNeeded;
private boolean isCallMethodMethodNeeded;
private boolean isSetFieldMethodNeeded;
private boolean isGetFieldMethodNeeded;
private boolean isXStreamNeeded;
private String cuName;
private String packageName;
public JUnitCodeGenerator(final String cuName, final String packageName)
{
if(cuName == null || cuName.isEmpty())
{
throw new IllegalArgumentException("Illegal compilation unit name: " + cuName);
}
if(packageName == null)
{
throw new NullPointerException("package name must not be null");
}
this.packageName = packageName;
this.cuName = cuName;
this.oidToVarMapping = new TIntObjectHashMap<String>();
this.oidToTypeMapping = new TIntObjectHashMap<Class<?>>();
this.failedRecords = new TIntHashSet();
this.init();
}
private void init()
{
this.oidToVarMapping.clear();
this.oidToTypeMapping.clear();
this.failedRecords.clear();
this.varCounter = 0;
this.postprocessing = false;
this.isNewInstanceMethodNeeded = false;
this.isCallMethodMethodNeeded = false;
this.isSetFieldMethodNeeded = false;
this.isGetFieldMethodNeeded = false;
this.isXStreamNeeded = false;
}
public void enablePostProcessingCodeGeneration()
{
this.postprocessing = true;
}
public void disablePostProcessingCodeGeneration(final TIntArrayList failedRecords)
{
this.postprocessing = false;
this.failedRecords.addAll(failedRecords);
}
private AST ast;
private CompilationUnit cu;
private Block methodBlock;
TypeDeclaration td;
@SuppressWarnings("unchecked")
@Override
public void before(CaptureLog log) {
ast = AST.newAST(AST.JLS3);
cu = ast.newCompilationUnit();
// package declaration
final PackageDeclaration p1 = ast.newPackageDeclaration();
p1.setName(ast.newName(packageName.split("\\.")));
cu.setPackage(p1);
// import specifications
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "org", "junit", "Test" }));
cu.imports().add(id);
// class declaration
td = ast.newTypeDeclaration();
td.setName(ast.newSimpleName(cuName));
td.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD));
cu.types().add(td);
// test method construction
final MethodDeclaration md = ast.newMethodDeclaration();
md.setName(ast.newSimpleName("test"));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
// sets @Test annotation to test method
final NormalAnnotation annotation = ast.newNormalAnnotation();
annotation.setTypeName(ast.newSimpleName("Test"));
md.modifiers().add(annotation);
// sets method to public
md.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.PUBLIC_KEYWORD));
td.bodyDeclarations().add(md);
methodBlock = ast.newBlock();
md.setBody(methodBlock);
}
@SuppressWarnings("unchecked")
@Override
public void after(CaptureLog log)
{
if(this.isXStreamNeeded)
{
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "com", "thoughtworks", "xstream", "XStream" }));
cu.imports().add(id);
// create XSTREAM constant: private static final XStream XSTREAM = new XStream();
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName("XSTREAM"));
final ClassInstanceCreation ci = ast.newClassInstanceCreation();
ci.setType(this.createAstType("XStream", ast));
vd.setInitializer(ci);
final FieldDeclaration xstreamConst = ast.newFieldDeclaration(vd);
xstreamConst.setType(this.createAstType("XStream", ast));
xstreamConst.modifiers().add(ast.newModifier(ModifierKeyword.PRIVATE_KEYWORD));
xstreamConst.modifiers().add(ast.newModifier(ModifierKeyword.STATIC_KEYWORD));
xstreamConst.modifiers().add(ast.newModifier(ModifierKeyword.FINAL_KEYWORD));
td.bodyDeclarations().add(xstreamConst);
this.isXStreamNeeded = false;
}
//-- creates utility method to set field value via reflections
if(this.isSetFieldMethodNeeded)
{
this.createSetFieldMethod(td, cu, ast);
this.isSetFieldMethodNeeded = false;
}
//-- creates utility method to get field value via reflections
if(this.isGetFieldMethodNeeded)
{
this.createGetFieldMethod(td, cu, ast);
this.isGetFieldMethodNeeded = false;
}
//-- creates utility method to call method via reflections
if(this.isCallMethodMethodNeeded)
{
this.createCallMethod(td, cu, ast);
this.isCallMethodMethodNeeded = false;
}
//-- creates utility method to call constructor via reflections
if(this.isNewInstanceMethodNeeded)
{
this.createNewInstanceMethod(td, cu, ast);
this.isNewInstanceMethodNeeded = false;
}
}
@Override
public CompilationUnit getCode() {
return this.cu;
}
@Override
public void clear()
{
this.init();
}
private String createNewVarName(final int oid, final String typeName)
{
return this.createNewVarName(oid, typeName, false);
}
private String createNewVarName(final int oid, final String typeName, final boolean asObject)
{
if(this.oidToVarMapping.containsKey(oid))
{
throw new IllegalStateException("There is already an oid to var mapping for oid " + oid);
}
// try
// {
if(asObject)
{
this.oidToTypeMapping.put(oid, Object.class);
}
else
{
this.oidToTypeMapping.put(oid, getClassForName(typeName));
}
// }
// catch(final ClassNotFoundException e)
// {
// throw new IllegalArgumentException(e);
// }
//
final String varName = "var" + this.varCounter++;
this.oidToVarMapping.put(oid, varName);
return varName;
}
// TODO easy but inefficient implementation
private Type createAstArrayType(final String type, final AST ast)
{
// NOTE: see asm4 guide p. 11 for more insights about internal type representation
final int arrayDim = type.lastIndexOf('[') + 1;
if(type.contains("[L"))
{
// --- object array
// TODO use regex for extraction
String extractedType = type.substring(type.indexOf('L') + 1, type.length() - 1);
extractedType = extractedType.replaceFirst("java\\.lang\\.", "");
extractedType = extractedType.replace('$', '.');
return ast.newArrayType(this.createAstType(extractedType, ast), arrayDim);
}
else
{
// --- primitive type array
if(type.contains("[I")) // int[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.INT), arrayDim);
}
else if(type.contains("[B")) // byte[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.BYTE), arrayDim);
}
else if(type.contains("[C")) // char[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.CHAR), arrayDim);
}
else if(type.contains("[D")) // double[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.DOUBLE), arrayDim);
}
else if(type.contains("[Z")) // boolean[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.BOOLEAN), arrayDim);
}
else if(type.contains("[F")) // float[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.FLOAT), arrayDim);
}
else if(type.contains("[S")) // short[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.SHORT), arrayDim);
}
else if(type.contains("[J")) // long[]
{
return ast.newArrayType(ast.newPrimitiveType(PrimitiveType.LONG), arrayDim);
}
else
{
throw new RuntimeException("Can not create array type for " + type);
}
}
}
private Type createAstType(final String type, final AST ast)
{
if(type.startsWith("[")) // does type specify an array?
{
return this.createAstArrayType(type, ast);
}
else
{
final String[] fragments = type.split("\\.");
if(fragments.length == 1)
{
if(fragments[0].contains("$"))
{
return ast.newSimpleType(ast.newName(fragments[0].replace('$', '.')));
}
else
{
return ast.newSimpleType(ast.newSimpleName(fragments[0]));
}
}
if(type.startsWith("java.lang"))
{
return ast.newSimpleType(ast.newSimpleName(fragments[2]));
}
final String[] pkgArray = new String[fragments.length - 1];
System.arraycopy(fragments, 0, pkgArray, 0, pkgArray.length);
final SimpleType pkgType = ast.newSimpleType(ast.newName(pkgArray));
final String clazzName = fragments[fragments.length - 1];
if(clazzName.contains("$"))
{
final String[] clazzSplit = clazzName.split("\\$");
final String[] newPkgType = new String[pkgArray.length + clazzSplit.length - 1];
System.arraycopy(pkgArray, 0, newPkgType, 0, pkgArray.length);
System.arraycopy(clazzSplit, 0, newPkgType, pkgArray.length, clazzSplit.length - 1);
if(clazzName.endsWith("[]"))
{
final QualifiedType t = ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.length - 1].replace("[]", "")));
return ast.newArrayType(t);
}
else
{
return ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.length - 1]));
}
}
else
{
final String[] clazzSplit = clazzName.split("\\$");
final String[] newPkgType = new String[pkgArray.length + clazzSplit.length - 1];
System.arraycopy(pkgArray, 0, newPkgType, 0, pkgArray.length);
System.arraycopy(clazzSplit, 0, newPkgType, pkgArray.length, clazzSplit.length - 1);
if(clazzName.endsWith("[]"))
{
final QualifiedType t = ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.length - 1].replace("[]", "")));
return ast.newArrayType(t);
}
else
{
return ast.newQualifiedType( ast.newSimpleType(ast.newName(newPkgType)), ast.newSimpleName(clazzSplit[clazzSplit.length - 1]));
}
}
}
}
/*
* Needed to preserve program flow:
*
* try
* {
* var0.doSth();
* }
* catch(Throwable t) {}
*/
@SuppressWarnings("unchecked")
private TryStatement createTryStmtWithEmptyCatch(final AST ast, Statement stmt)
{
final TryStatement tryStmt = ast.newTryStatement();
tryStmt.getBody().statements().add(stmt);
final CatchClause cc = ast.newCatchClause();
SingleVariableDeclaration excDecl = ast.newSingleVariableDeclaration();
excDecl.setType(ast.newSimpleType(ast.newSimpleName("Throwable")));
excDecl.setName(ast.newSimpleName("t"));
cc.setException(excDecl);
tryStmt.catchClauses().add(cc);
return tryStmt;
}
/*
* try
* {
* var0.doSth();
* }
* catch(Throwable t)
* {
* org.uni.saarland.sw.prototype.capture.PostProcessor.captureException(<logRecNo>);
* }
*/
@SuppressWarnings("unchecked")
private TryStatement createTryStatementForPostProcessing(final AST ast, final Statement stmt, final int logRecNo)
{
final TryStatement tryStmt = this.createTryStmtWithEmptyCatch(ast, stmt);
final CatchClause cc = (CatchClause) tryStmt.catchClauses().get(0);
final MethodInvocation m = ast.newMethodInvocation();
m.setExpression(ast.newName(new String[]{"org", "evosuite","testcarver","codegen", "PostProcessor"}));
m.setName(ast.newSimpleName("captureException"));
m.arguments().add(ast.newNumberLiteral(String.valueOf(logRecNo)));
cc.getBody().statements().add(ast.newExpressionStatement(m));
MethodInvocation m2 = ast.newMethodInvocation();
m2.setExpression(ast.newSimpleName("t"));
m2.setName(ast.newSimpleName("printStackTrace"));
cc.getBody().statements().add(ast.newExpressionStatement(m2));
return tryStmt;
}
@SuppressWarnings("unchecked")
private MethodInvocation createCallMethodOrNewInstanceCallStmt(final boolean isConstructorCall, final AST ast, final String varName, final String targetTypeName, final String methodName, final Object[] methodArgs, final org.objectweb.asm.Type[] paramTypes)
{
//-- construct getField() call
final MethodInvocation callMethodCall = ast.newMethodInvocation();
if(isConstructorCall)
{
callMethodCall.setName(ast.newSimpleName("newInstance"));
}
else
{
callMethodCall.setName(ast.newSimpleName("callMethod"));
}
StringLiteral stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(targetTypeName);
callMethodCall.arguments().add(stringLiteral); // class name
if(! isConstructorCall)
{
stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(methodName);
callMethodCall.arguments().add(stringLiteral); // method name
if(varName == null)
{
callMethodCall.arguments().add(ast.newNullLiteral()); // static call -> no receiver
}
else
{
callMethodCall.arguments().add(ast.newSimpleName(varName)); // receiver
}
}
// method arguments
ArrayCreation arrCreation = ast.newArrayCreation();
arrCreation.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Object"))));
ArrayInitializer arrInit = ast.newArrayInitializer();
arrCreation.setInitializer(arrInit);
callMethodCall.arguments().add(arrCreation);
Integer arg; // is either an oid or null
for(int i = 0; i < methodArgs.length; i++)
{
arg = (Integer) methodArgs[i];
if(arg == null)
{
arrInit.expressions().add(ast.newNullLiteral());
}
else
{
arrInit.expressions().add(ast.newSimpleName(this.oidToVarMapping.get(arg)));
}
}
// paramTypes
arrCreation = ast.newArrayCreation();
arrCreation.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Class"))));
arrInit = ast.newArrayInitializer();
arrCreation.setInitializer(arrInit);
callMethodCall.arguments().add(arrCreation);
org.objectweb.asm.Type type;
for(int i = 0; i < paramTypes.length; i++)
{
type = paramTypes[i];
if(type.equals(org.objectweb.asm.Type.BOOLEAN_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Boolean"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.BYTE_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Byte"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.CHAR_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Character"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.DOUBLE_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Double"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.FLOAT_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Float"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.INT_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Integer"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.LONG_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Long"));
arrInit.expressions().add(facc);
}
else if(type.equals(org.objectweb.asm.Type.SHORT_TYPE))
{
FieldAccess facc = ast.newFieldAccess();
facc.setName(ast.newSimpleName("TYPE"));
facc.setExpression(ast.newSimpleName("Short"));
arrInit.expressions().add(facc);
}
else
{
final TypeLiteral clazz = ast.newTypeLiteral();
clazz.setType(ast.newSimpleType(ast.newName(type.getClassName())));
arrInit.expressions().add(clazz);
}
}
return callMethodCall;
}
/* (non-Javadoc)
* @see org.evosuite.testcarver.codegen.ICodeGenerator#createMethodCallStmt(org.evosuite.testcarver.capture.CaptureLog, int)
*/
@SuppressWarnings("unchecked")
@Override
public void createMethodCallStmt(CaptureLog log, int logRecNo)
{
PostProcessor.notifyRecentlyProcessedLogRecNo(logRecNo);
// assumption: all necessary statements are created and there is one variable for reach referenced object
final int oid = log.objectIds.get(logRecNo);
Object[] methodArgs = log.params.get(logRecNo);
final String methodName = log.methodNames.get(logRecNo);
final String methodDesc = log.descList.get(logRecNo);
org.objectweb.asm.Type[] methodParamTypes = org.objectweb.asm.Type.getArgumentTypes(methodDesc);
Class<?>[] methodParamTypeClasses = new Class[methodParamTypes.length];
for(int i = 0; i < methodParamTypes.length; i++)
{
methodParamTypeClasses[i] = getClassForName(methodParamTypes[i].getClassName());
}
final String typeName = log.oidClassNames.get(log.oidRecMapping.get(oid));
Class<?> type = getClassForName(typeName);
final boolean haveSamePackage = type.getPackage().getName().equals(packageName); // TODO might be nicer...
final Statement finalStmt;
@SuppressWarnings("rawtypes")
final List arguments;
if(CaptureLog.OBSERVED_INIT.equals(methodName))
{
/*
* Person var0 = null;
* {
* var0 = new Person();
* }
* catch(Throwable t)
* {
* org.uni.saarland.sw.prototype.capture.PostProcessor.captureException(<logRecNo>);
* }
*/
// e.g. Person var0 = null;
final String varName = this.createNewVarName(oid, typeName);
VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(varName));
VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(typeName, ast));
vd.setInitializer(ast.newNullLiteral());
methodBlock.statements().add(stmt);
//FIXME this does not make any sense
try
{
this.getConstructorModifiers(type,methodParamTypeClasses);
}
catch(Exception e)
{
logger.error(""+e,e);
}
final int constructorTypeModifiers = this.getConstructorModifiers(type,methodParamTypeClasses);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(constructorTypeModifiers);
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
if(isReflectionAccessNeeded)
{
this.isNewInstanceMethodNeeded = true;
final MethodInvocation mi = this.createCallMethodOrNewInstanceCallStmt(true, ast, varName, typeName, methodName, methodArgs, methodParamTypes);
arguments = null;
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(varName));
assignment.setOperator(Operator.ASSIGN);
final CastExpression cast = ast.newCastExpression();
cast.setType(this.createAstType(typeName, ast));
cast.setExpression(mi);
assignment.setRightHandSide(cast);
finalStmt = ast.newExpressionStatement(assignment);
}
else
{
// e.g. var0 = new Person();
final ClassInstanceCreation ci = ast.newClassInstanceCreation();
final int recNo = log.oidRecMapping.get(oid);
final int dependencyOID = log.dependencies.getQuick(recNo);
if(dependencyOID == CaptureLog.NO_DEPENDENCY)
{
ci.setType(this.createAstType(typeName, ast));
}
else
{
// final String varTypeName = oidToVarMapping.get(dependencyOID) + "." + typeName.substring(typeName.indexOf('$') + 1);
// ci.setType(this.createAstType(varTypeName, ast));
/*
* e.g.
* OuterClass.InnerClass innerObject = outerObject.new InnerClass();
*/
ci.setType(this.createAstType(typeName.substring(typeName.indexOf('$') + 1), ast));
ci.setExpression(ast.newSimpleName(oidToVarMapping.get(dependencyOID)));
final int index = Arrays.binarySearch(methodArgs, dependencyOID);
if(index > -1)
{
logger.debug(varName + " xxxx3 " + index);
final Object[] newArgs = new Object[methodArgs.length - 1];
System.arraycopy(methodArgs, 0, newArgs, 0, index);
System.arraycopy(methodArgs, index + 1, newArgs, index, methodArgs.length - index - 1);
methodArgs = newArgs;
final Class<?>[] newParamTypeClasses = new Class<?>[methodParamTypeClasses.length - 1];
System.arraycopy(methodParamTypeClasses, 0, newParamTypeClasses, 0, index);
System.arraycopy(methodParamTypeClasses, index + 1, newParamTypeClasses, index, methodParamTypeClasses.length - index - 1);
methodParamTypeClasses = newParamTypeClasses;
final org.objectweb.asm.Type[] newParamTypes = new org.objectweb.asm.Type[methodParamTypes.length - 1];
System.arraycopy(methodParamTypes, 0, newParamTypes, 0, index);
System.arraycopy(methodParamTypes, index + 1, newParamTypes, index, methodParamTypes.length - index - 1);
methodParamTypes = newParamTypes;
}
}
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(varName));
assignment.setOperator(Operator.ASSIGN);
assignment.setRightHandSide(ci);
finalStmt = ast.newExpressionStatement(assignment);
arguments = ci.arguments();
}
}
else //------------------ handling for ordinary method calls e.g. var1 = var0.doSth();
{
String returnVarName = null;
final String desc = log.descList.get(logRecNo);
final String returnType = org.objectweb.asm.Type.getReturnType(desc).getClassName();
final Object returnValue = log.returnValues.get(logRecNo);
if(! CaptureLog.RETURN_TYPE_VOID.equals(returnValue))
{
Integer returnValueOID = (Integer) returnValue;
// e.g. Person var0 = null;
returnVarName = this.createNewVarName(returnValueOID, returnType);
VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(returnVarName));
VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(returnType, ast));
vd.setInitializer(ast.newNullLiteral());
methodBlock.statements().add(stmt);
}
final String varName = this.oidToVarMapping.get(oid);
final int methodTypeModifiers = this.getMethodModifiers(type, methodName, methodParamTypeClasses);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(methodTypeModifiers);
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
// e.g. Person var0 = var1.getPerson("Ben");
final MethodInvocation mi;
if(isReflectionAccessNeeded)
{
this.isCallMethodMethodNeeded = true;
mi = this.createCallMethodOrNewInstanceCallStmt(false, ast, varName, typeName, methodName, methodArgs, methodParamTypes);
arguments = null;
if(returnVarName != null)
{
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(returnVarName));
assignment.setOperator(Operator.ASSIGN);
final CastExpression cast = ast.newCastExpression();
cast.setType(this.createAstType(returnType, ast));
cast.setExpression(mi);
assignment.setRightHandSide(cast);
finalStmt = ast.newExpressionStatement(assignment);
}
else
{
finalStmt = ast.newExpressionStatement(mi);
}
}
else
{
mi = ast.newMethodInvocation();
if( log.isStaticCallList.get(logRecNo))
{
// can only happen, if this is a static method call (because constructor statement has been reported)
final String tmpType = log.oidClassNames.get(log.oidRecMapping.get(oid));
mi.setExpression(ast.newName(tmpType.split("\\.")));
}
else
{
try
{
mi.setExpression(ast.newSimpleName(varName));
}
catch(final IllegalArgumentException ex)
{
String msg = "";
msg += "--recno-- " + logRecNo + "\n";
msg += "--oid-- " + oid + "\n";
msg += "--method-- " + methodName + "\n";
msg += "--varName-- " + varName + "\n";
msg += "--oidToVarMap-- " + this.oidToVarMapping + "\n";
msg += (log) + "\n";
logger.error(msg);
throw new RuntimeException(msg);
}
}
mi.setName(ast.newSimpleName(methodName));
arguments = mi.arguments();
if(returnVarName != null)
{
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(ast.newSimpleName(returnVarName));
assignment.setOperator(Operator.ASSIGN);
assignment.setRightHandSide(mi);
finalStmt = ast.newExpressionStatement(assignment);
}
else
{
finalStmt = ast.newExpressionStatement(mi);
}
}
}
if(postprocessing)
{
final TryStatement tryStmt = this.createTryStatementForPostProcessing(ast, finalStmt, logRecNo);
methodBlock.statements().add(tryStmt);
}
else
{
if(this.failedRecords.contains(logRecNo))
{
// we just need an empty catch block to preserve program flow
final TryStatement tryStmt = this.createTryStmtWithEmptyCatch(ast, finalStmt);
methodBlock.statements().add(tryStmt);
}
else
{
methodBlock.statements().add(finalStmt);
}
}
if(arguments != null)
{
Class<?> methodParamType;
Class<?> argType;
Integer arg; // is either an oid or null
for(int i = 0; i < methodArgs.length; i++)
{
arg = (Integer) methodArgs[i];
if(arg == null)
{
arguments.add(ast.newNullLiteral());
}
else
{
methodParamType = CaptureUtil.getClassFromDesc(methodParamTypes[i].getDescriptor());
argType = this.oidToTypeMapping.get(arg);
// TODO: Warten was Florian und Gordon dazu sagen. Siehe Mail 04.08.2012
if(argType == null)
{
logger.error("Call within constructor needs instance of enclosing object as parameter -> ignored: " + arg);
methodBlock.statements().remove(methodBlock.statements().size() - 1);
return;
}
final CastExpression cast = ast.newCastExpression();
if(methodParamType.isPrimitive())
{
// cast to ensure that right method is called
// --> see doSth(int) and doSth(Integer)
if(methodParamType.equals(boolean.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.BOOLEAN));
}
else if(methodParamType.equals(byte.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.BYTE));
}
else if(methodParamType.equals(char.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.CHAR));
}
else if(methodParamType.equals(double.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.DOUBLE));
}
else if(methodParamType.equals(float.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.FLOAT));
}
else if(methodParamType.equals(int.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.INT));
}
else if(methodParamType.equals(long.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.LONG));
}
else if(methodParamType.equals(short.class))
{
cast.setType(ast.newPrimitiveType(PrimitiveType.SHORT));
}
else
{
throw new RuntimeException("unknown primitive type: " + methodParamType);
}
}
else
{
// we need an up-cast
if(methodParamType.getName().contains("."))
{
cast.setType(this.createAstType(methodParamType.getName(), ast));
}
else
{
cast.setType(createAstType(methodParamType.getName(), ast));
}
}
cast.setExpression(ast.newSimpleName(this.oidToVarMapping.get(arg)));
arguments.add(cast);
}
}
}
}
@SuppressWarnings("unchecked")
@Override
public void createPlainInitStmt(CaptureLog log, int logRecNo)
{
PostProcessor.notifyRecentlyProcessedLogRecNo(logRecNo);
// NOTE: PLAIN INIT: has always one non-null param
// TODO: use primitives
final int oid = log.objectIds.get(logRecNo);
if(this.oidToVarMapping.containsKey(oid))
{
// TODO this might happen because of Integer.valueOf o.รค. . Is this approach ok?
return;
}
final String type = log.oidClassNames.get(log.oidRecMapping.get(oid));
final Object value = log.params.get(logRecNo)[0];
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(this.createNewVarName(oid, value.getClass().getName())));
final VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
if(value instanceof Class)
{
stmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
}
else
{
stmt.setType(this.createAstType(type, ast));
}
if(value instanceof Number)
{
if(value instanceof Long)
{
vd.setInitializer(ast.newNumberLiteral(String.valueOf(value) + 'l'));
}
else if(value instanceof Double)
{
vd.setInitializer(ast.newNumberLiteral(String.valueOf(value) + 'd'));
}
else
{
vd.setInitializer(ast.newNumberLiteral(String.valueOf(value)));
}
}
else if(value instanceof String)
{
final StringLiteral literal = ast.newStringLiteral();
literal.setLiteralValue((String) value);
vd.setInitializer(literal);
}
else if(value instanceof Character)
{
final CharacterLiteral literal = ast.newCharacterLiteral();
literal.setCharValue((Character) value);
vd.setInitializer(literal);
}
else if(value instanceof Boolean)
{
final BooleanLiteral literal = ast.newBooleanLiteral((Boolean)value);
vd.setInitializer(literal);
}
else if(value instanceof Class)
{
final TypeLiteral clazz = ast.newTypeLiteral();
clazz.setType(ast.newSimpleType(ast.newName(((Class<?>) value).getName())));
vd.setInitializer(clazz);
}
else
{
throw new IllegalStateException("An error occurred while creating a plain statement: unsupported type: " + value.getClass().getName());
}
methodBlock.statements().add(stmt);
}
@SuppressWarnings("unchecked")
@Override
public void createUnobservedInitStmt(CaptureLog log, int logRecNo)
{
PostProcessor.notifyRecentlyProcessedLogRecNo(logRecNo);
// NOTE: PLAIN INIT: has always one non-null param
// TODO: use primitives
final int oid = log.objectIds.get(logRecNo);
// final String type = log.oidClassNames.get(log.oidRecMapping.get(oid));
final Object value = log.params.get(logRecNo)[0];
this.isXStreamNeeded = true;
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
// handling because there must always be a new instantiation statement for pseudo inits
this.oidToVarMapping.remove(oid);
vd.setName(ast.newSimpleName(this.createNewVarName(oid, log.oidClassNames.get(log.oidRecMapping.get(oid)), true)));
final MethodInvocation methodInvocation = ast.newMethodInvocation();
final Name name = ast.newSimpleName("XSTREAM");
methodInvocation.setExpression(name);
methodInvocation.setName(ast.newSimpleName("fromXML"));
final StringLiteral xmlParam = ast.newStringLiteral();
xmlParam.setLiteralValue((String) value);
methodInvocation.arguments().add(xmlParam);
// final CastExpression castExpr = ast.newCastExpression();
// castExpr.setType(this.createAstType(type, ast));
// castExpr.setExpression(methodInvocation);
// vd.setInitializer(castExpr);
vd.setInitializer(methodInvocation);
final VariableDeclarationStatement vs = ast.newVariableDeclarationStatement(vd);
vs.setType(this.createAstType("Object", ast));
methodBlock.statements().add(vs);
}
@SuppressWarnings("unchecked")
@Override
public void createFieldWriteAccessStmt(CaptureLog log, int logRecNo) {
// assumption: all necessary statements are created and there is one variable for reach referenced object
final Object[] methodArgs = log.params.get(logRecNo);
final String methodName = log.methodNames.get(logRecNo);
final int oid = log.objectIds.get(logRecNo);
final int captureId = log.captureIds.get(logRecNo);
final String fieldName = log.namesOfAccessedFields.get(captureId);
final String typeName = log.oidClassNames.get(log.oidRecMapping.get(oid));
final Class<?> type = getClassForName(typeName);
final int fieldTypeModifiers = this.getFieldModifiers(type, fieldName);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(fieldTypeModifiers);
final boolean haveSamePackage = type.getPackage().getName().equals(packageName); // TODO might be nicer...
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
if(isReflectionAccessNeeded)
{
this.isSetFieldMethodNeeded = true;
final String varName = this.oidToVarMapping.get(oid);
final MethodInvocation setFieldCall = ast.newMethodInvocation();
setFieldCall.setName(ast.newSimpleName("setField"));
StringLiteral stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(typeName);
setFieldCall.arguments().add(stringLiteral); // class name
stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(fieldName);
setFieldCall.arguments().add(stringLiteral); // field name
setFieldCall.arguments().add(ast.newSimpleName(varName)); // receiver
final Integer arg = (Integer) methodArgs[0];
if(arg == null)
{
setFieldCall.arguments().add(ast.newNullLiteral()); // value
}
else
{
setFieldCall.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(arg))); // value
}
methodBlock.statements().add(ast.newExpressionStatement(setFieldCall));
}
else
{
FieldAccess fa = ast.newFieldAccess();
if(CaptureLog.PUTSTATIC.equals(methodName))
{
fa.setExpression(ast.newName(typeName));
}
else
{
final String varName = this.oidToVarMapping.get(oid);
fa.setExpression(ast.newSimpleName(varName));
}
fa.setName(ast.newSimpleName(fieldName));
final Assignment assignment = ast.newAssignment();
assignment.setLeftHandSide(fa);
final Integer arg = (Integer) methodArgs[0];
if(arg == null)
{
assignment.setRightHandSide(ast.newNullLiteral());
}
else
{
final Class<?> argType = this.oidToTypeMapping.get(arg);
final String fieldDesc = log.descList.get(logRecNo);
final Class<?> fieldType = CaptureUtil.getClassFromDesc(fieldDesc);
if(fieldType.isAssignableFrom(argType) || fieldType.isPrimitive())
{
assignment.setRightHandSide(ast.newSimpleName(this.oidToVarMapping.get(arg)));
}
else
{
// we need an up-cast
final CastExpression cast = ast.newCastExpression();
cast.setType(ast.newSimpleType(ast.newName(fieldType.getName())));
cast.setExpression(ast.newSimpleName(this.oidToVarMapping.get(arg)));
assignment.setRightHandSide(cast);
}
}
methodBlock.statements().add(ast.newExpressionStatement(assignment));
}
}
@SuppressWarnings("unchecked")
@Override
public void createFieldReadAccessStmt(CaptureLog log, int logRecNo)
{
final String methodName = log.methodNames.get(logRecNo);
final int oid = log.objectIds.get(logRecNo);
final int captureId = log.captureIds.get(logRecNo);
String returnVarName = null;
final Object returnValue = log.returnValues.get(logRecNo);
if(! CaptureLog.RETURN_TYPE_VOID.equals(returnValue))
{
Integer returnValueOID = (Integer) returnValue;
final String descriptor = log.descList.get(logRecNo);
final String fieldTypeName = org.objectweb.asm.Type.getType(descriptor).getClassName();
final String typeName = log.oidClassNames.get(log.oidRecMapping.get(oid));
final String fieldName = log.namesOfAccessedFields.get(captureId);
final String receiverVarName = this.oidToVarMapping.get(oid);
final Class<?> type = getClassForName(typeName);
final int fieldTypeModifiers = this.getFieldModifiers(type, fieldName);
final boolean isPublic = java.lang.reflect.Modifier.isPublic(fieldTypeModifiers);
final boolean haveSamePackage = type.getPackage().getName().equals(packageName); // TODO might be nicer...
final boolean isReflectionAccessNeeded = ! isPublic && ! haveSamePackage;
// e.g. Person var0 = Person.BEN;
returnVarName = this.createNewVarName(returnValueOID, fieldTypeName);
VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
vd.setName(ast.newSimpleName(returnVarName));
if(isReflectionAccessNeeded)
{
this.isGetFieldMethodNeeded = true;
//-- construct getField() call
final MethodInvocation getFieldCall = ast.newMethodInvocation();
getFieldCall.setName(ast.newSimpleName("getField"));
StringLiteral stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(fieldTypeName);
getFieldCall.arguments().add(stringLiteral); // class name
stringLiteral = ast.newStringLiteral();
stringLiteral.setLiteralValue(fieldName);
getFieldCall.arguments().add(stringLiteral); // field name
if(receiverVarName == null)
{
getFieldCall.arguments().add(ast.newNullLiteral()); // static call -> no receiver
}
else
{
getFieldCall.arguments().add(ast.newSimpleName(receiverVarName)); // receiver
}
// cast from object to field type
final CastExpression cast = ast.newCastExpression();
cast.setType(ast.newSimpleType(ast.newName(fieldTypeName)));
cast.setExpression(getFieldCall);
vd.setInitializer(cast);
}
else
{
FieldAccess fa = ast.newFieldAccess();
if(CaptureLog.GETSTATIC.equals(methodName))
{
final String classType = log.oidClassNames.get(log.oidRecMapping.get(oid));
fa.setExpression(ast.newName(classType.replace('$', '.').split("\\.")));
}
else
{
fa.setExpression(ast.newSimpleName(receiverVarName));
}
fa.setName(ast.newSimpleName(fieldName));
vd.setInitializer(fa);
}
VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(fieldTypeName, ast));
methodBlock.statements().add(stmt);
}
}
@Override
public void createArrayInitStmt(final CaptureLog log, final int logRecNo) {
final int oid = log.objectIds.get(logRecNo);
final Object[] params = log.params.get(logRecNo);
final String arrTypeName = log.oidClassNames.get(log.oidRecMapping.get(oid));
final Class<?> arrType = getClassForName(arrTypeName);
// --- create array instance creation e.g. int[] var = new int[10];
final ArrayType arrAstType = (ArrayType) createAstArrayType(arrTypeName, ast);
final ArrayCreation arrCreationExpr = ast.newArrayCreation();
arrCreationExpr.setType(arrAstType);
arrCreationExpr.dimensions().add(ast.newNumberLiteral(String.valueOf(params.length)));
final String arrVarName = this.createNewVarName(oid, arrTypeName);
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
final SimpleName arrVarNameExpr = ast.newSimpleName(arrVarName);
vd.setName(arrVarNameExpr);
vd.setInitializer(arrCreationExpr);
final VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(vd);
varDeclStmt.setType(this.createAstType(arrTypeName, ast));
methodBlock.statements().add(varDeclStmt);
// create array access statements var[0] = var1;
Integer paramOID;
Assignment assign;
ArrayAccess arrAccessExpr;
for(int i = 0; i < params.length; i++)
{
assign = ast.newAssignment();
arrAccessExpr = ast.newArrayAccess();
arrAccessExpr.setIndex(ast.newNumberLiteral(String.valueOf(i)));
arrAccessExpr.setArray(arrVarNameExpr);
assign.setLeftHandSide(arrAccessExpr);
paramOID = (Integer) params[i];
if(paramOID == null)
{
assign.setRightHandSide(ast.newNullLiteral());
}
else
{
assign.setRightHandSide(ast.newSimpleName(this.oidToVarMapping.get(paramOID)));
}
methodBlock.statements().add(assign);
}
}
@SuppressWarnings("unchecked")
@Override
public void createCollectionInitStmt(final CaptureLog log, final int logRecNo)
{
final int oid = log.objectIds.get(logRecNo);
final Object[] params = log.params.get(logRecNo);
String collTypeName = log.oidClassNames.get(log.oidRecMapping.get(oid));
final Class<?> collType = getClassForName(collTypeName);
final String varName;
// -- determine if an alternative collection must be used for code generation
final boolean isPrivate = java.lang.reflect.Modifier.isPrivate(collType.getModifiers());
if(isPrivate || ! hasDefaultConstructor(collType))
{
if(Set.class.isAssignableFrom(collType))
{
collTypeName = HashSet.class.getName();
}
else if (List.class.isAssignableFrom(collType))
{
collTypeName = ArrayList.class.getName();
}
else if(Queue.class.isAssignableFrom(collType))
{
collTypeName = ArrayDeque.class.getName();
}
else
{
throw new RuntimeException("Collection " + collType + " is not supported");
}
}
// -- create code for instantiating collection
varName = this.createNewVarName(oid, collTypeName);
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
final SimpleName varNameExpr = ast.newSimpleName(varName);
vd.setName(varNameExpr);
final ClassInstanceCreation ci = ast.newClassInstanceCreation();
ci.setType(this.createAstType(collTypeName, ast));
vd.setInitializer(ci);
final VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(collTypeName, ast));
methodBlock.statements().add(stmt);
// --- create code for filling the collection
Integer paramOID;
MethodInvocation mi;
for(int i = 0; i < params.length; i++)
{
mi = ast.newMethodInvocation();
mi.setName(ast.newSimpleName("add"));
paramOID = (Integer) params[i];
if(paramOID == null)
{
mi.arguments().add(ast.newNullLiteral());
}
else
{
mi.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(paramOID)));
}
methodBlock.statements().add(mi);
}
}
private boolean hasDefaultConstructor(final Class<?> clazz)
{
for(final Constructor<?> c : clazz.getConstructors())
{
if(c.getParameterTypes().length == 0)
{
return true;
}
}
return false;
}
@Override
public void createMapInitStmt(final CaptureLog log, final int logRecNo) {
final int oid = log.objectIds.get(logRecNo);
final Object[] params = log.params.get(logRecNo);
String collTypeName = log.oidClassNames.get(log.oidRecMapping.get(oid));
final Class<?> collType = getClassForName(collTypeName);
final String varName;
// -- determine if an alternative collection must be used for code generation
final boolean isPrivate = java.lang.reflect.Modifier.isPrivate(collType.getModifiers());
if(isPrivate || ! hasDefaultConstructor(collType))
{
collTypeName = HashMap.class.getName();
}
// -- create code for instantiating collection
varName = this.createNewVarName(oid, collTypeName);
final VariableDeclarationFragment vd = ast.newVariableDeclarationFragment();
final SimpleName varNameExpr = ast.newSimpleName(varName);
vd.setName(varNameExpr);
final ClassInstanceCreation ci = ast.newClassInstanceCreation();
ci.setType(this.createAstType(collTypeName, ast));
vd.setInitializer(ci);
final VariableDeclarationStatement stmt = ast.newVariableDeclarationStatement(vd);
stmt.setType(this.createAstType(collTypeName, ast));
methodBlock.statements().add(stmt);
// --- create code for filling the collection
Integer valueOID;
Integer keyOID;
MethodInvocation mi;
for(int i = 0; i + 1< params.length; i+=2)
{
mi = ast.newMethodInvocation();
mi.setName(ast.newSimpleName("put"));
keyOID = (Integer) params[i];
mi.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(keyOID)));
valueOID = (Integer) params[i + 1];
if(valueOID == null)
{
mi.arguments().add(ast.newNullLiteral());
}
else
{
mi.arguments().add(ast.newSimpleName(this.oidToVarMapping.get(valueOID)));
}
methodBlock.statements().add(mi);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createSetFieldMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Field" }));
imports.add(id);
//-- create method frame: "public static void setProtectedField(final String clazzName, final String fieldName, final Object receiver, final Object value) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("setField"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("fieldName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("receiver"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("value"));
parameters.add(svd);
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Field f = clazz.getDeclaredField(fieldName);
// f.setAccessible(true);
// f.set(receiver, value);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Field f = clazz.getDeclaredField(fieldName);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredField"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("fieldName"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("f"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Field")));
methodStmts.add(varDeclStmt);
// f.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// f.set(receiver, value);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("set"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newSimpleName("receiver"));
minv.arguments().add(ast.newSimpleName("value"));
methodStmts.add(ast.newExpressionStatement(minv));
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createGetFieldMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
// public static void setField(final String clazzName, final String fieldName, final Object receiver, final Object value) throws Exception
// {
// final Class<?> clazz = Class.forName(clazzName);
// final Field f = clazz.getDeclaredField(fieldName);
// f.setAccessible(true);
// f.set(receiver, value);
// }
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Field" }));
imports.add(id);
//-- create method frame: "public static Object setProtectedField(final String clazzName, final String fieldName, final Object receiver) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("getField"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("fieldName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("receiver"));
parameters.add(svd);
md.setReturnType2(ast.newSimpleType(ast.newSimpleName("Object")));
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Field f = clazz.getDeclaredField(fieldName);
// f.setAccessible(true);
// return f.get(receiver);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Field f = clazz.getDeclaredField(fieldName);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredField"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("fieldName"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("f"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Field")));
methodStmts.add(varDeclStmt);
// f.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// return f.get(receiver);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("get"));
minv.setExpression(ast.newSimpleName("f"));
minv.arguments().add(ast.newSimpleName("receiver"));
final ReturnStatement returnStmt = ast.newReturnStatement();
returnStmt.setExpression(minv);
methodStmts.add(returnStmt);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createCallMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
// public static Object callMethod(final String clazzName, final String methodName, final Object receiver, final Object...args) throws Exception
// {
// final Class<?> clazz = Class.forName(clazzName);
// final Method m = clazz.getDeclaredMethod(methodName);
// m.setAccessible(true);
// return m.invoke(receiver, args);
// }
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Method" }));
imports.add(id);
//-- create method frame: "public static Object callMethod(final String clazzName, final String methodName, final Object receiver, final Object[] args, Class[] paramTypes) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("callMethod"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("methodName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("Object")));
svd.setName(ast.newSimpleName("receiver"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Object"))));
svd.setName(ast.newSimpleName("args"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Class"))));
svd.setName(ast.newSimpleName("paramTypes"));
parameters.add(svd);
md.setReturnType2(ast.newSimpleType(ast.newSimpleName("Object")));
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Method m = clazz.getDeclaredMethod(methodName, paramTypes);
// m.setAccessible(true);
// return m.invoke(receiver, args);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Method m = clazz.getDeclaredMethod(methodName);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredMethod"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("methodName"));
init.arguments().add(ast.newSimpleName("paramTypes"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("m"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Method")));
methodStmts.add(varDeclStmt);
// f.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("m"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// return m.invoke(receiver, args);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("invoke"));
minv.setExpression(ast.newSimpleName("m"));
minv.arguments().add(ast.newSimpleName("receiver"));
minv.arguments().add(ast.newSimpleName("args"));
final ReturnStatement returnStmt = ast.newReturnStatement();
returnStmt.setExpression(minv);
methodStmts.add(returnStmt);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void createNewInstanceMethod(final TypeDeclaration td, final CompilationUnit cu, final AST ast)
{
// public static Object newInstance(final String clazzName, final Object receiver, final Object[] args, final Class[] parameterTypes) throws Exception
// {
// final Class<?> clazz = Class.forName(clazzName);
// final Constructor c = clazz.getDeclaredConstructor(parameterTypes);
// c.setAccessible(true);
//
// return c.newInstance(args);
// }
//-- add necessary import statements
List imports = cu.imports();
ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "lang", "reflect", "Constructor" }));
imports.add(id);
//-- create method frame: "public static Object newInstance(final String clazzName, final Object[] args, Class[] paramTypes) throws Exception"
final MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);
md.setName(ast.newSimpleName("newInstance"));
List modifiers = md.modifiers();
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.PRIVATE_KEYWORD));
modifiers.add(ast.newModifier(Modifier.ModifierKeyword.STATIC_KEYWORD));
md.thrownExceptions().add(ast.newSimpleName("Exception"));
List parameters = md.parameters();
SingleVariableDeclaration svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newSimpleType(ast.newSimpleName("String")));
svd.setName(ast.newSimpleName("clazzName"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Object"))));
svd.setName(ast.newSimpleName("args"));
parameters.add(svd);
svd = ast.newSingleVariableDeclaration();
svd.setType(ast.newArrayType(ast.newSimpleType(ast.newSimpleName("Class"))));
svd.setName(ast.newSimpleName("paramTypes"));
parameters.add(svd);
md.setReturnType2(ast.newSimpleType(ast.newSimpleName("Object")));
//-- create method body
// final Class<?> clazz = Class.forName(clazzName);
// final Constructor c = clazz.getDeclaredConstructor(parameterTypes);
// c.setAccessible(true);
//
// return c.newInstance(args);
final Block methodBlock = ast.newBlock();
md.setBody(methodBlock);
final List methodStmts = methodBlock.statements();
// final Class clazz = Class.forName(clazzName);
MethodInvocation init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("forName"));
init.setExpression(ast.newSimpleName("Class"));
init.arguments().add(ast.newSimpleName("clazzName"));
VariableDeclarationFragment varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("clazz"));
varDeclFrag.setInitializer(init);
VariableDeclarationStatement varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Class")));
methodStmts.add(varDeclStmt);
// final Constructor c = clazz.getDeclaredConstructor(parameterTypes);
init = ast.newMethodInvocation();
init.setName(ast.newSimpleName("getDeclaredConstructor"));
init.setExpression(ast.newSimpleName("clazz"));
init.arguments().add(ast.newSimpleName("paramTypes"));
varDeclFrag = ast.newVariableDeclarationFragment();
varDeclFrag.setName(ast.newSimpleName("c"));
varDeclFrag.setInitializer(init);
varDeclStmt = ast.newVariableDeclarationStatement(varDeclFrag);
varDeclStmt.setType(ast.newSimpleType(ast.newSimpleName("Constructor")));
methodStmts.add(varDeclStmt);
// c.setAccessible(true);
MethodInvocation minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("setAccessible"));
minv.setExpression(ast.newSimpleName("c"));
minv.arguments().add(ast.newBooleanLiteral(true));
methodStmts.add(ast.newExpressionStatement(minv));
// return c.newInstance(args);
minv = ast.newMethodInvocation();
minv.setName(ast.newSimpleName("newInstance"));
minv.setExpression(ast.newSimpleName("c"));
minv.arguments().add(ast.newSimpleName("args"));
final ReturnStatement returnStmt = ast.newReturnStatement();
returnStmt.setExpression(minv);
methodStmts.add(returnStmt);
}
private int getFieldModifiers(final Class<?> clazz, final String fieldName)
{
try
{
final Field f = clazz.getDeclaredField(fieldName);
return f.getModifiers();
}
catch(final NoSuchFieldException e)
{
final Class<?> superClazz = clazz.getSuperclass();
if(superClazz == null)
{
throw new RuntimeException(e);
}
else
{
return this.getFieldModifiers(superClazz, fieldName);
}
}
catch(final Exception e)
{
throw new RuntimeException(e);
}
}
private int getMethodModifiers(final Class<?> clazz, final String methodName, final Class<?>[] paramTypes)
{
try
{
final Method m = clazz.getDeclaredMethod(methodName, paramTypes);
return m.getModifiers();
}
catch(final NoSuchMethodException e)
{
final Class<?> superClazz = clazz.getSuperclass();
if(superClazz == null)
{
throw new RuntimeException(e);
}
else
{
return this.getMethodModifiers(superClazz, methodName, paramTypes);
}
}
catch(final Exception e)
{
throw new RuntimeException(e);
}
}
private int getConstructorModifiers(final Class<?> clazz, final Class<?>[] paramTypes)
{
try
{
final Constructor<?> c = clazz.getDeclaredConstructor(paramTypes);
return c.getModifiers();
}
catch(final NoSuchMethodException e)
{
final Class<?> superClazz = clazz.getSuperclass();
if(superClazz == null)
{
throw new RuntimeException(e);
}
else
{
return this.getConstructorModifiers(superClazz, paramTypes);
}
}
catch(final Exception e)
{
throw new RuntimeException(e);
}
}
private final Class<?> getClassForName(String type)
{
try
{
if( type.equals("boolean"))
{
return Boolean.TYPE;
}
else if(type.equals("byte"))
{
return Byte.TYPE;
}
else if( type.equals("char"))
{
return Character.TYPE;
}
else if( type.equals("double"))
{
return Double.TYPE;
}
else if(type.equals("float"))
{
return Float.TYPE;
}
else if(type.equals("int"))
{
return Integer.TYPE;
}
else if( type.equals("long"))
{
return Long.TYPE;
}
else if(type.equals("short"))
{
return Short.TYPE;
}
else if(type.equals("String") ||type.equals("Boolean") ||type.equals("Boolean") || type.equals("Short") ||type.equals("Long") ||
type.equals("Integer") || type.equals("Float") || type.equals("Double") ||type.equals("Byte") ||
type.equals("Character") )
{
return Class.forName("java.lang." + type);
}
if(type.endsWith("[]"))
{
type = type.replace("[]", "");
final Class<?> baseClass = getClassForName(type);
if(baseClass.isPrimitive())
{
if(int.class.equals(baseClass))
{
return int[].class;
}
else if(byte.class.equals(baseClass))
{
return byte[].class;
}
else if(char.class.equals(baseClass))
{
return char[].class;
}
else if(double.class.equals(baseClass))
{
return double[].class;
}
else if(boolean.class.equals(baseClass))
{
return boolean[].class;
}
else if(float.class.equals(baseClass))
{
return float[].class;
}
else if(short.class.equals(baseClass))
{
return short[].class;
}
else
{
return long[].class;
}
}
else if(baseClass.isArray())
{
return Class.forName("[" + type);
}
else
{
return Class.forName("[L" + type + ";");
}
}
else
{
return Class.forName(Utils.getClassNameFromResourcePath(type));
}
}
catch (final ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
}