/*******************************************************************************
* Copyright (c) 2010 Michal Antkiewicz.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michal Antkiewicz - initial API and implementation
******************************************************************************/
package ca.uwaterloo.gsd.fsml.javaMappingInterpreter;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IfStatement;
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.Name;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedType;
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.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.jdt.core.dom.WildcardType;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.internal.corext.codemanipulation.AddUnimplementedMethodsOperation;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
import ca.uwaterloo.gsd.fsml.javaMappingInterpreter.analysis.ASTUtils;
import ca.uwaterloo.gsd.fsml.stats.Stats;
/**
* @author Michal Antkiewicz <mantkiew@gsd.uwaterloo.ca>
*/
public class CodeTransforms {
public static LinkedCorrectionProposal currentProposal=null;
/**
* This method starts recoding modifications on the compilation unit. It should always be called before performing any
* changes to AST.
* @param cu
* @return false if the compilation unit cannot be modified
*/
public static boolean recordModifications(CompilationUnit cu) {
if ((cu.getFlags() & ASTNode.PROTECT) != 0)
// cu is unmodifiable! Abort
return false;
// try to record modifications
try {
cu.recordModifications();
// no exception means that the recording is turned on for the first time.
} catch (IllegalArgumentException e) {
if (!"AST modifications are already recorded".equals(e.getMessage()) &&
!"AST is already modified".equals(e.getMessage()))
// root belongs to different ast... Abort
return false;
}
// otherwise it means that the modification recording has already been started. Proceed.
return true;
}
public static IType createCompilationUnit(IJavaProject project, String className, String packageName, IProgressMonitor progressMonitor) {
// first check if a compilation unit already exists
String fqName = (packageName != null && !packageName.isEmpty()) ? packageName + "." + className : className;
try {
IType iType = project.findType(fqName);
if (iType != null && iType.exists())
return iType;
} catch (JavaModelException e1) {
e1.printStackTrace();
}
try {
IPackageFragment[] allFragments = project.getPackageFragments();
IPackageFragment fragment = null;
for (int i = 0; i < allFragments.length; i++) {
fragment = allFragments[i];
if (fragment.getElementName().equals(packageName))
break;
else
fragment = null;
}
if (fragment != null) {
// top-level classes only
String contents = (packageName.length()>0)? "package " + packageName + ";\n":"\n";
contents += "public class " + className + " {\n}";
ICompilationUnit icu = fragment.createCompilationUnit(className+".java", contents, false, progressMonitor);
icu.save(progressMonitor, true);
return icu.getType(className);
}
else {
// TODO: create the package?
}
} catch (JavaModelException e) {
e.printStackTrace();
}
return null;
}
public static void removeIType(IType iType, IProgressMonitor progressMonitor) {
try {
iType.delete(true, progressMonitor);
} catch (JavaModelException e) {
e.printStackTrace();
}
}
public static boolean removeCompilationUnit(String className, String packageName, IProgressMonitor progressMonitor) {
// TODO: implement removeCompilationUnit
return false;
}
public static Type addInterfaceDeclaration(IJavaProject project, CompilationUnit compilationUnit, TypeDeclaration typeDeclaration, String superInterfaceQName, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) typeDeclaration.getRoot());
if (!recordModifications(cu))
return null;
organizeImports(cu, superInterfaceQName);
// add interface
String interfaceName = Signature.getSimpleName(superInterfaceQName);
AST ast = typeDeclaration.getAST();
SimpleName name = ast.newSimpleName(interfaceName);
SimpleType newInterface = ast.newSimpleType(name);
Type found = null;
for (Iterator i = typeDeclaration.superInterfaceTypes().iterator(); i.hasNext(); ) {
SimpleType aux = (SimpleType) i.next();
if (aux.getName().toString().equals(interfaceName)) {
found = aux;
break;
}
}
if (found == null) {
typeDeclaration.superInterfaceTypes().add(newInterface);
// Michal: need to add unimplemented methods too
createUnimplementedMethods(project, superInterfaceQName, typeDeclaration, progressMonitor);
return newInterface;
}
else
return found;
}
public static boolean removeInterfaceDeclaration(CompilationUnit compilationUnit, TypeDeclaration typeDeclaration, String superInterfaceQName, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) typeDeclaration.getRoot());
if (!recordModifications(cu))
return false;
// remove interface
String interfaceName = Signature.getSimpleName(superInterfaceQName);
for (ListIterator i = typeDeclaration.superInterfaceTypes().listIterator(); i.hasNext(); ) {
Object ii = i.next();
if (ii instanceof SimpleType) {
SimpleType aux = (SimpleType) ii;
if (aux.getName().getFullyQualifiedName().equals(superInterfaceQName) || aux.getName().toString().equals(interfaceName)) {
i.remove();
return true;
}
}
}
return false;
}
/**
*
* @param compilationUnit
* @param type
* @param superClassQName
* @param progressMonitor
* @return
*/
public static Type addExtendsDeclaration(IJavaProject project, CompilationUnit compilationUnit, TypeDeclaration typeDeclaration, String superClassQName, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) typeDeclaration.getRoot());
if (!recordModifications(cu))
return null;
organizeImports(cu, superClassQName);
// set the super type
String superClassName = Signature.getSimpleName(superClassQName);
AST ast = typeDeclaration.getAST();
SimpleName name = ast .newSimpleName(superClassName);
SimpleType newSuperclass = ast.newSimpleType(name);
// simply add superclass type
typeDeclaration.setSuperclassType(newSuperclass);
createUnimplementedMethods(project, superClassQName, typeDeclaration, progressMonitor);
return newSuperclass;
}
public static boolean removeExtendsDeclaration(CompilationUnit compilationUnit, TypeDeclaration typeDeclaration, String superClassQName, IProgressMonitor progressMonitor) {
// TODO implement remove extends declaration
return false;
}
public static final int BEFORE_ADVICE = 0;
public static final int AFTER_ADVICE = -1;
public static List<ASTNode> weaveAdvice(int adviceType, CompilationUnit compilationUnit, MethodDeclaration methodDeclaration, String advice, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) methodDeclaration.getRoot());
List<ASTNode> result = null;
if (!recordModifications(cu))
return result;
// create AST for the advice
ASTParser adviceParser = ASTParser.newParser(AST.JLS3);
adviceParser.setSource(advice.toCharArray());
adviceParser.setResolveBindings(true);
adviceParser.setBindingsRecovery(true);
adviceParser.setKind(ASTParser.K_STATEMENTS);
Block adviceAst = (Block) adviceParser.createAST(progressMonitor);
// get list of statements
// weave the advice
Block body = methodDeclaration.getBody();
List<ASTNode> newStatements = ASTNode.copySubtrees(body.getAST(), adviceAst.statements());
if (adviceType==BEFORE_ADVICE) {
body.statements().addAll(0, newStatements);
} else if (adviceType==AFTER_ADVICE){
body.statements().addAll(newStatements);
}else {
newStatements=weaveAdviceHelper(adviceType, body,adviceAst,progressMonitor);
}
result = newStatements;
// not sure that this will work. Probably need to apply the changes first and then lookup
// the nodes again in the committed working copy.
return result;
}
private static List<ASTNode> weaveAdviceHelper(int adviceType, Block body, Block adviceAST, IProgressMonitor progressMonitor) {
List<ASTNode> result=null;
int targetLineNum= 0;
int currentOffset = adviceType;
List<ASTNode> statements = body.statements();
List<ASTNode> newStatements = ASTNode.copySubtrees(body.getAST(), adviceAST.statements());
for (ASTNode stmt : statements) {
if (stmt.getStartPosition() < currentOffset && stmt.getStartPosition()+stmt.getLength()>currentOffset) {
if (stmt instanceof SynchronizedStatement){
body = ((SynchronizedStatement)stmt).getBody();
return weaveAdviceHelper(currentOffset,body, adviceAST, progressMonitor);
} else if (stmt instanceof WhileStatement){
if (((WhileStatement)stmt).getBody() instanceof Block)
body = (Block)((WhileStatement)stmt).getBody();
return weaveAdviceHelper(currentOffset,body, adviceAST, progressMonitor);
}else if (stmt instanceof IfStatement){
if (((IfStatement)stmt).getThenStatement().getStartPosition()<currentOffset &&
((IfStatement)stmt).getThenStatement().getStartPosition()+((IfStatement)stmt).getThenStatement().getLength()>currentOffset &&
((IfStatement)stmt).getThenStatement() instanceof Block){
body = (Block)((IfStatement)stmt).getThenStatement();
return weaveAdviceHelper(currentOffset,body, adviceAST, progressMonitor);
}else if (((IfStatement)stmt).getElseStatement() instanceof Block){
body = (Block)((IfStatement)stmt).getElseStatement();
return weaveAdviceHelper(currentOffset,body, adviceAST, progressMonitor);
}
} else if (stmt instanceof SwitchStatement){
//special case since switch statements don't have a block as its body
//doesn't handle things like nested switch statements
List<ASTNode> switchStmts = ((SwitchStatement)stmt).statements();
if (!switchStmts.isEmpty()){
targetLineNum=0;
for (ASTNode stmt2 : switchStmts) {
if (stmt2.getStartPosition()<currentOffset){
targetLineNum++;
}
}
switchStmts.addAll(targetLineNum, newStatements);
result = newStatements;
return result;
}
}
break;
}
else if (stmt.getStartPosition()<currentOffset){
targetLineNum++;
}
}
body.statements().addAll(targetLineNum, newStatements);
// not sure that this will work. Probably need to apply the changes first and then lookup
// the nodes again in the committed working copy.
result = newStatements;
return result;
}
public static boolean commentOutASTNode(CompilationUnit compilationUnit, ASTNode astNode, EClass eClass, EStructuralFeature feature, IProgressMonitor progressMonitor) {
// TODO:
String message = "Commented out by an FSML during the removal of " + eClass.getName() + (feature != null ? "::" + feature.getName() : "");
Stats.INSTANCE.printMessage(message);
return false;
}
public static boolean commentOutSourceReference(IType type, ISourceReference sourceReference, String message, IProgressMonitor progressMonitor) {
boolean result = true;
ICompilationUnit workingCopy = null;
if (sourceReference.exists()) {
try {
workingCopy = type.getCompilationUnit().getWorkingCopy(progressMonitor);
int offset = sourceReference.getSourceRange().getOffset();
int length = sourceReference.getSourceRange().getLength();
IBuffer buffer = workingCopy.getBuffer();
buffer.replace(offset, length, "/* Commented out by an FSML " + message + "\n " + buffer.getText(offset, length) + " */");
workingCopy.reconcile(ICompilationUnit.NO_AST, false, null, null);
workingCopy.commitWorkingCopy(false, progressMonitor);
return true;
} catch (JavaModelException e) {
e.printStackTrace();
} catch (MalformedTreeException e) {
e.printStackTrace();
} finally {
if (workingCopy != null) {
try {
workingCopy.discardWorkingCopy();
} catch (JavaModelException e) { e.printStackTrace(); }
}
progressMonitor.done();
}
}
return result;
}
public static String createMethodString(IJavaProject project, CompilationUnit compilationUnit, TypeDeclaration typeDeclaration, String visibility, String name, String signature, String body, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) typeDeclaration.getRoot());
if (!recordModifications(cu))
return null;
StringBuffer contents = new StringBuffer();
String[] parameterTypes = Signature.getParameterTypes(signature);
String[] parameterNames = new String[parameterTypes.length];
String returnType = Signature.getReturnType(signature);
returnType = Signature.getElementType(returnType);
organizeImports(cu, Signature.toString(returnType));
for (int i = 0; i < parameterTypes.length; i++) {
String parameterTypeSignature = Signature.getElementType(parameterTypes[i]);
organizeImports(cu, Signature.toString(parameterTypeSignature));
parameterNames[i] = Signature.getSimpleName(CodeTransforms.convertSlashPathsToDotPaths(parameterTypes[i]));
char char0 = Character.toLowerCase(parameterNames[i].charAt(0));
parameterNames[i] = char0 + (parameterNames[i].length() > 1 ? parameterNames[i].substring(1, parameterNames[i].length()-1) : "");
if (parameterNames[i].equals("class"))
parameterNames[i] = "aClass";
}
contents.append(visibility + " ");
contents.append(Signature.toString(signature, name, parameterNames, false, true));
if (body == null) {
// Michal: need to add a call to super if the method overrides a method from the superclass
// TODO: check all superclasses not just the direct one!
String superInvocation = "";
Type superclassType = typeDeclaration.getSuperclassType();
if (superclassType != null) {
String fqSuperclassName = ASTUtils.getFullyQualifiedName(superclassType);
// check if the supertype has a similar method that has to be called super
try {
IType superIType = project.findType(fqSuperclassName);
for (IMethod iMethod : superIType.getMethods()) {
if (iMethod.getElementName().equals(name)) {
// compare signature
String iMethodSig = iMethod.getSignature();
iMethodSig = convertSlashPathsToDotPaths(iMethodSig);
if (iMethodSig.equals(signature)) {
// construct super method invocation if the method is not abstract
if (!Flags.isAbstract(iMethod.getFlags())) {
superInvocation = "super." + name + "(";
// add parameters
for (String parameterName : parameterNames) {
superInvocation += parameterName;
if (!parameterNames[parameterNames.length-1].equals(parameterName))
superInvocation += ", ";
}
superInvocation += ")";
break;
}
}
}
}
} catch (JavaModelException e) {
// no problem. just continue.
Stats.INSTANCE.logError("CodeTransforms.createMethodString(): cannot process methods of the supertype");
}
}
// Michal: need to initialize here, otherwise 'null' could be in the body and the mehtod will not parse.
body = "";
if (Signature.toString(returnType).equals("void")) {
if (!superInvocation.isEmpty())
body = "\t" + superInvocation + ";";
}
else {
body = "\treturn ";
if (!superInvocation.isEmpty())
body += superInvocation + ";";
else {
if(signature.substring(signature.indexOf(')')+1).indexOf('[')!=-1) //return type is an array
body += "null;";
else if (Signature.toString(returnType).equals("boolean"))
body += "false;";
else if (Signature.toString(returnType).equals("float"))
body += "-1f;";
else if (Signature.toString(returnType).equals("byte") || Signature.toString(returnType).equals("short") ||Signature.toString(returnType).equals("long")||Signature.toString(returnType).equals("double")||Signature.toString(returnType).equals("int"))
body += "-1;";
else if (Signature.toString(returnType).equals("char"))
body += "'0';";
else
body += "null;";
}
}
}
contents.append("{\n" + body + "\n}");
return contents.toString();
}
private static boolean equalTypes(Type type1, Type type2) {
if (type1.isArrayType() && type2.isArrayType()) {
ArrayType casted_type1 = (ArrayType) type1;
ArrayType casted_type2 = (ArrayType) type2;
if (casted_type1.getDimensions() != casted_type2.getDimensions() ||
!equalTypes(casted_type1.getComponentType(), casted_type2.getComponentType()) ||
!equalTypes(casted_type1.getElementType(), casted_type2.getElementType())) {
return false;
}
return true;
}
else if (type1.isParameterizedType() && type2.isParameterizedType()) {
ParameterizedType casted_type1 = (ParameterizedType) type1;
ParameterizedType casted_type2 = (ParameterizedType) type2;
if (!equalTypes(casted_type1.getType(), casted_type2.getType()) ||
casted_type1.typeArguments().size() != casted_type2.typeArguments().size()) {
return false;
}
for (int i = 0; i < casted_type1.typeArguments().size(); ++i) {
if (!equalTypes((Type)casted_type1.typeArguments().get(i), (Type)casted_type2.typeArguments().get(i))) {
return false;
}
}
return true;
}
else if (type1.isPrimitiveType() && type2.isPrimitiveType()) {
PrimitiveType casted_type1 = (PrimitiveType) type1;
PrimitiveType casted_type2 = (PrimitiveType) type2;
if (!casted_type1.getPrimitiveTypeCode().toString().equals(casted_type2.getPrimitiveTypeCode().toString())) {
return false;
}
return true;
}
else if (type1.isQualifiedType() && type2.isQualifiedType()) {
QualifiedType casted_type1 = (QualifiedType) type1;
QualifiedType casted_type2 = (QualifiedType) type2;
if (casted_type1.getName().getIdentifier().equals(casted_type2.getName().getIdentifier()) ||
!equalTypes(casted_type1.getQualifier(), casted_type2.getQualifier())) {
return false;
}
return true;
}
else if (type1.isSimpleType() && type2.isSimpleType()) {
SimpleType casted_type1 = (SimpleType) type1;
SimpleType casted_type2 = (SimpleType) type2;
return casted_type1.getName().getFullyQualifiedName().equals(casted_type2.getName().getFullyQualifiedName());
}
else if (type1.isWildcardType() && type2.isWildcardType()) {
WildcardType casted_type1 = (WildcardType) type1;
WildcardType casted_type2 = (WildcardType) type2;
if (casted_type1.isUpperBound() != casted_type2.isUpperBound() ||
!equalTypes(casted_type1.getBound(), casted_type2.getBound())) {
return false;
}
return true;
}
return false;
}
/**
* Creates a new method in the type declaration.
* @param project
* @param compilationUnit If null, the compilation unit is retrieved from the methodInvocation
* @param typeDeclaration
* @param visibility
* @param name
* @param signature
* @param body
* @param progressMonitor
* @return the created method declaration or null otherwise
*/
public static MethodDeclaration createMethod(IJavaProject project, CompilationUnit compilationUnit, TypeDeclaration typeDeclaration, String visibility, String name, String signature, String body, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) typeDeclaration.getRoot());
if (!recordModifications(cu))
return null;
String contents = createMethodString(project, compilationUnit, typeDeclaration, visibility, name, signature, body, progressMonitor);
// create a MethodDeclaration ast node
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(contents.toString().toCharArray());
parser.setProject(project);
parser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
ASTNode node = parser.createAST(progressMonitor);
if (node instanceof TypeDeclaration) {
node = ((TypeDeclaration) node).getMethods()[0];
}
if (node instanceof MethodDeclaration) {
node = ASTNode.copySubtree(typeDeclaration.getAST(), node);
MethodDeclaration newMethod = (MethodDeclaration) node;
boolean methodAlreadyCreated = false;
MethodDeclaration[] existingMethods = typeDeclaration.getMethods();
if (existingMethods != null) {
for (MethodDeclaration m : existingMethods) {
if (m.getName().getIdentifier().equals(newMethod.getName().getIdentifier()) &&
equalTypes(m.getReturnType2(), newMethod.getReturnType2())) {
methodAlreadyCreated = true;
for (int i = 0; i < m.parameters().size(); ++i) {
if (!equalTypes(((SingleVariableDeclaration)m.parameters().get(i)).getType(),
((SingleVariableDeclaration)newMethod.parameters().get(i)).getType())) {
methodAlreadyCreated = false;
break;
}
}
if (methodAlreadyCreated) {
break;
}
}
}
}
if (!methodAlreadyCreated) {
typeDeclaration.bodyDeclarations().add(node);
}
return (MethodDeclaration) node;
}
return null;
}
/**
* Creates a new field in the type declaration.
* @param project
* @param compilationUnit If null, the compilation unit is retrieved from the methodInvocation
* @param typeDeclaration
* @param visibility
* @param fieldName
* @param fieldType
* @param progressMonitor
* @return the created field declaration fragment or null otherwise
*/
public static VariableDeclarationFragment createField(IJavaProject project, CompilationUnit compilationUnit, TypeDeclaration typeDeclaration, String visibility, String fieldName, String fieldType, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) typeDeclaration.getRoot());
if (!recordModifications(cu))
return null;
organizeImports(cu, fieldType);
StringBuffer contents = new StringBuffer();
contents.append(visibility + " ");
contents.append(Signature.getSimpleName(fieldType) + " ");
contents.append(fieldName + ";");
// create a FieldDeclaration ast node
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(contents.toString().toCharArray());
parser.setProject(project);
parser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
ASTNode node = parser.createAST(progressMonitor);
if (node instanceof TypeDeclaration) {
node = ((TypeDeclaration) node).getFields()[0];
}
if (node instanceof FieldDeclaration) {
node = ASTNode.copySubtree(typeDeclaration.getAST(), node);
MethodDeclaration[] methodDeclarations = typeDeclaration.getMethods();
if (methodDeclarations.length > 0) {
MethodDeclaration firstMethodDeclaration = methodDeclarations[0];
int index = typeDeclaration.bodyDeclarations().indexOf(firstMethodDeclaration);
typeDeclaration.bodyDeclarations().add(index, node);
}
else
// add at the end
typeDeclaration.bodyDeclarations().add(node);
FieldDeclaration fieldDeclaration = (FieldDeclaration) node;
return (VariableDeclarationFragment) fieldDeclaration.fragments().get(0);
}
return null;
}
public static String convertSlashPathsToDotPaths(String slashPath) {
StringBuilder dotPath = new StringBuilder();
for (int i = 0; i < slashPath.length(); ++i) {
char curChar = slashPath.charAt(i);
if (curChar == '/') {
dotPath.append('.');
}
else {
dotPath.append(curChar);
}
}
return dotPath.toString();
}
/**
* WARNING: this can only be executed after a transformation that turned recording of modifications on.
* @param cu
* @param fullyQualifiedName
*/
public static void organizeImports(ASTNode astNode, String fullyQualifiedName) {
CompilationUnit cu = (CompilationUnit) (astNode instanceof CompilationUnit ? astNode : astNode.getRoot());
if (fullyQualifiedName.equals("void") ||
fullyQualifiedName.equals("int") ||
fullyQualifiedName.equals("String") ||
fullyQualifiedName.equals("boolean") ||
fullyQualifiedName.equals("float") ||
fullyQualifiedName.equals("Thread") ||
fullyQualifiedName.equals("double") ||
fullyQualifiedName.equals("char") ||
fullyQualifiedName.equals("short") ||
fullyQualifiedName.equals("long") ||
fullyQualifiedName.equals("byte") ||
Signature.getQualifier(fullyQualifiedName).equals("java.lang"))
return;
// see if import declaration for the given type exists
ImportDeclaration foundImportDeclaration = null;
for (Iterator i = cu.imports().iterator(); i.hasNext(); ) {
ImportDeclaration importDeclaration = (ImportDeclaration) i.next();
if (importDeclaration.getName().getFullyQualifiedName().equals(CodeTransforms.convertSlashPathsToDotPaths(fullyQualifiedName))) {
foundImportDeclaration = importDeclaration;
break;
}
}
AST ast = cu.getAST();
if (foundImportDeclaration == null) {
// need to create one...
ImportDeclaration importDeclaration = ast.newImportDeclaration();
Name name = ast.newName(CodeTransforms.convertSlashPathsToDotPaths(fullyQualifiedName));
importDeclaration.setName(name);
cu.imports().add(importDeclaration);
}
}
/**
* Sets the initializer of a fields declaration fragment
* @param compilationUnit
* @param variableDeclarationFragment
* @param expression
* @param progressMonitor
* @return
*/
public static Expression setFieldsInitializer(CompilationUnit compilationUnit, VariableDeclarationFragment variableDeclarationFragment, String expression, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) variableDeclarationFragment.getRoot());
if (!recordModifications(cu))
return null;
ASTParser argExParser = ASTParser.newParser(AST.JLS3);
argExParser.setSource(expression.toCharArray());
argExParser.setResolveBindings(true);
argExParser.setBindingsRecovery(true);
argExParser.setKind(ASTParser.K_EXPRESSION);
Expression newInitializer = (Expression) argExParser.createAST(progressMonitor);
newInitializer = (Expression) ASTNode.copySubtree(variableDeclarationFragment.getAST(), newInitializer);
variableDeclarationFragment.setInitializer(newInitializer);
return newInitializer;
}
/**
* This replaces the index^th argument of the method invocation.
* @param compilationUnit If null, the compilation unit is retrieved from the methodInvocation
* @param methodInvocation a MethodInvocation or a ClassInstanceCreation
* @param index of the argument to replace. Index is 1-based.
* @param argumentExpression
* @param progressMonitor
* @return the replaced expression or null otherwise
*/
public static Expression replaceMethodCallArgument(CompilationUnit compilationUnit, Expression invocation, int index, String argumentExpression, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) invocation.getRoot());
if (!recordModifications(cu))
return null;
// need to decrease index because indexes in AST are 0-based
if (index > 0)
index--;
String contents = argumentExpression;
if (argumentExpression == null || argumentExpression == "")
contents = "null";
ASTParser argExParser = ASTParser.newParser(AST.JLS3);
argExParser.setSource(contents.toCharArray());
argExParser.setResolveBindings(true);
argExParser.setBindingsRecovery(true);
argExParser.setKind(ASTParser.K_EXPRESSION);
Expression newArgument = (Expression) argExParser.createAST(progressMonitor);
newArgument = (Expression) ASTNode.copySubtree(invocation.getAST(), newArgument);
if (invocation instanceof MethodInvocation)
((MethodInvocation) invocation).arguments().set(index, newArgument);
else
((ClassInstanceCreation) invocation).arguments().set(index, newArgument);
return newArgument;
}
public static void createUnimplementedMethods(IJavaProject project, String typeName, TypeDeclaration typeDeclaration, IProgressMonitor progressMonitor) {
// Michal: temporarily disable
return;
/*IType instanceType;
try {
instanceType = project.findType(typeName);
if (instanceType == null) {
return;
}
if (!instanceType.isInterface() && !(instanceType.isClass() && Flags.isAbstract(instanceType.getFlags()))) {
// if this type is not an interface or abstract class, then we don't need to travel up the
// hierarchy any more
return;
}
IMethod[] instanceMethods = instanceType.getMethods();
for (IMethod method : instanceMethods) {
if (!method.isConstructor() && (instanceType.isInterface() || Flags.isAbstract(method.getFlags()))) {
String visibility = "";
if (Flags.isPublic(method.getFlags())) {
visibility = "public";
} else if (Flags.isProtected(method.getFlags())) {
visibility = "protected";
}
CodeTransforms.createMethod(project, null, typeDeclaration, visibility, method.getElementName(), method.getSignature(), null, progressMonitor);
}
}
// super abstract class and interfaces
String superClassName = instanceType.getSuperclassName();
if (superClassName != null && !superClassName.equals("java.lang.Object")) {
CodeTransforms.createUnimplementedMethods(project, superClassName, typeDeclaration, progressMonitor);
}
for (String superInterfaceName : instanceType.getSuperInterfaceNames()) {
CodeTransforms.createUnimplementedMethods(project, superInterfaceName, typeDeclaration, progressMonitor);
}
} catch (JavaModelException e) {
e.printStackTrace();
}*/
}
public static VariableDeclarationStatement declareVariable(IJavaProject project, CompilationUnit compilationUnit, ASTNode astNode, String variableName, String variableType, String variableInitializerValue, String variableConstructorSignature, boolean annonymousSubclass, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) astNode.getRoot());
if (!recordModifications(cu))
return null;
organizeImports(cu, variableType);
// find the closest parent which is a Statement
ASTNode aux = astNode;
while (!(aux instanceof Statement) && aux != null) {
aux = aux.getParent();
}
if (aux == null)
return null;
// else aux is a statement in which the variable is used
// need to declare the variable before the statement
String variableSimpleType = Signature.getSimpleName(variableType);
String declaration = variableSimpleType + " " + variableName;
if (variableConstructorSignature != null) {
// create the initializer
declaration += " = new " + variableSimpleType + constructDefaultCallParameters(variableConstructorSignature);
if (annonymousSubclass) {
// create annonymous subclass for interfaces and abstract classes
//declaration += " {\n\t }";
IType instanceType;
try {
instanceType = project.findType(variableType);
if (instanceType.isInterface() || (instanceType.isClass() && Flags.isAbstract(instanceType.getFlags()))) {
// create anonymous subclass for interfaces and abstract classes
declaration += " {\n\t";
IMethod[] instanceMethods = instanceType.getMethods();
StringBuffer contents = new StringBuffer();
/* Michal: temporarily disable
* for (IMethod method : instanceMethods) {
if (!method.isConstructor() && (instanceType.isInterface() || Flags.isAbstract(method.getFlags()))) {
// if this is an interface, provide empty stub of methods
// if this is an abstract class, ONLY provide empty stubs for abstract methods
String visibility = "";
if (Flags.isPublic(method.getFlags())) {
visibility = "public";
} else if (Flags.isProtected(method.getFlags())) {
visibility = "protected";
}
contents.append(CodeTransforms.createMethodString(project, cu, null, visibility, method.getElementName(), method.getSignature(), null, progressMonitor));
contents.append("\n");
}
} */
contents.append("}");
declaration += contents.toString();
}
} catch (JavaModelException e) {
e.printStackTrace();
}
}
}
else if (variableInitializerValue != null)
declaration += " = " + variableInitializerValue;
declaration += ";";
ASTParser argExParser = ASTParser.newParser(AST.JLS3);
argExParser.setSource(declaration.toCharArray());
argExParser.setResolveBindings(true);
argExParser.setBindingsRecovery(true);
argExParser.setKind(ASTParser.K_STATEMENTS);
Block block = (Block) argExParser.createAST(progressMonitor);
VariableDeclarationStatement variableDeclaration = (VariableDeclarationStatement) block.statements().get(0);
variableDeclaration = (VariableDeclarationStatement) ASTNode.copySubtree(aux.getAST(), variableDeclaration);
// insert the declaration before the statement
Statement statement = (Statement) aux;
block = (Block) statement.getParent();
int index = block.statements().indexOf(statement);
block.statements().add(index, variableDeclaration);
return variableDeclaration;
}
public static ExpressionStatement assignVariableValue(CompilationUnit compilationUnit, ASTNode astNode, String variableName, String value, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) astNode.getRoot());
if (!recordModifications(cu))
return null;
// find the closest parent which is a Statement
ASTNode aux = astNode;
while (!(aux instanceof Statement) && aux != null) {
aux = aux.getParent();
}
if (aux == null)
return null;
// else aux is a statement in which the variable is used
// need to declare the variable before the statement
String assignment = variableName + " = " + value + ";";
ASTParser argExParser = ASTParser.newParser(AST.JLS3);
argExParser.setSource(assignment.toCharArray());
argExParser.setResolveBindings(true);
argExParser.setBindingsRecovery(true);
argExParser.setKind(ASTParser.K_STATEMENTS);
Block block = (Block) argExParser.createAST(progressMonitor);
ExpressionStatement variableAssignment = (ExpressionStatement) block.statements().get(0);
variableAssignment = (ExpressionStatement) ASTNode.copySubtree(aux.getAST(), variableAssignment);
// insert the declaration before the statement
Statement statement = (Statement) aux;
block = (Block) statement.getParent();
int index = block.statements().indexOf(statement);
block.statements().add(index, variableAssignment);
return variableAssignment;
}
public static String constructDefaultCallParameters(String[] parameterTypes) {
StringBuffer result = new StringBuffer("(");
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].equals("I;") ||
parameterTypes[i].equals("L;") ||
parameterTypes[i].equals("F;") ||
parameterTypes[i].equals("B;"))
result.append("0");
else if (parameterTypes[i].equals("Z;"))
result.append("false");
else
result.append("null");
if (i < parameterTypes.length - 1)
result.append(", ");
}
result.append(")");
return result.toString();
}
public static String constructDefaultCallParameters(String methodSignature) {
return constructDefaultCallParameters(Signature.getParameterTypes(methodSignature));
}
// Old, non-incremental versions. Should never use, because they recreate ASTs and commit working copies.
/**
* @deprecated
*/
private static boolean createMethod(IType iType, String visibility, String name, String signature, String body, IProgressMonitor progressMonitor) {
StringBuffer contents = new StringBuffer();
String[] parameterTypes = Signature.getParameterTypes(signature);
String[] parameterNames = new String[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
parameterNames[i] = Signature.getSimpleName(parameterTypes[i]);
char char0 = Character.toLowerCase(parameterNames[i].charAt(0));
parameterNames[i] = char0 + parameterNames[i].substring(1, parameterNames[i].length()-1);
}
Signature.toString(signature, name, parameterNames, false, true);
contents.append(visibility + " ");
contents.append(Signature.toString(signature, name, parameterNames, false, true));
contents.append("{ \n" + body + "\n}");
try {
iType.createMethod(contents.toString(), null, false, progressMonitor);
} catch (JavaModelException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* @deprecated
*/
private static boolean createField(IType contextClass, String visibility, String fieldName, String fieldType, IProgressMonitor progressMonitor) {
StringBuffer contents = new StringBuffer();
contents.append(visibility + " ");
contents.append(fieldType + " ");
contents.append(fieldName + ";");
try {
contextClass.createField(contents.toString(), null, false, progressMonitor);
} catch (JavaModelException e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* @deprecated
*/
private static boolean replaceMethodCallArgument(MethodInvocation contextMethodInvocation, int index, String argumentExpression, IProgressMonitor progressMonitor) {
if (contextMethodInvocation == null)
return false;
// need to decrease index
if (index > 0)
index--;
ASTNode astRoot = contextMethodInvocation.getRoot();
CompilationUnit cu = (CompilationUnit) astRoot;
IJavaElement je = cu.getJavaElement();
if (!(je instanceof ICompilationUnit))
return false;
ICompilationUnit icu = (ICompilationUnit) je;
ICompilationUnit workingCopy = null;
try {
workingCopy = icu.getWorkingCopy(progressMonitor);
Document document = new Document(workingCopy.getSource());
ASTParser argExParser = ASTParser.newParser(AST.JLS3);
argExParser.setSource(argumentExpression.toCharArray());
argExParser.setResolveBindings(true);
argExParser.setBindingsRecovery(true);
argExParser.setKind(ASTParser.K_EXPRESSION);
Expression newArgument = (Expression) argExParser.createAST(progressMonitor);
Expression existingArgument = (Expression) contextMethodInvocation.arguments().get(index);
// replace the ith argument of the context method call with the new argument
ASTRewrite rewrite = ASTRewrite.create(astRoot.getAST());
rewrite.replace(existingArgument, newArgument, null);
TextEdit edits = rewrite.rewriteAST(document, icu.getJavaProject().getOptions(true));
// computation of the new source code
edits.apply(document);
String newSource = document.get();
// update of the compilation unit
workingCopy.getBuffer().setContents(newSource);
workingCopy.commitWorkingCopy(true, progressMonitor);
return true;
} catch (JavaModelException e) {
e.printStackTrace();
return false;
} catch (MalformedTreeException e) {
e.printStackTrace();
return false;
} catch (BadLocationException e) {
e.printStackTrace();
return false;
} finally {
if (workingCopy != null) {
try {
workingCopy.discardWorkingCopy();
} catch (JavaModelException e) { e.printStackTrace(); }
}
progressMonitor.done();
}
}
/**
* @deprecated
*/
private static void addInterfaceDeclaration(IType type, String superInterfaceQName, IProgressMonitor progressMonitor) {
ICompilationUnit cu = null;
try {
// create an import first
cu = type.getCompilationUnit().getWorkingCopy(progressMonitor);
if (!cu.getImport(superInterfaceQName).exists()) {
cu.createImport(superInterfaceQName, null, progressMonitor);
cu.reconcile(AST.JLS3, false, null, progressMonitor);
}
// add implements declaration
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(cu);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(progressMonitor);
String source = cu.getBuffer().getContents();
Document document= new Document(source);
// start modifications
astRoot.recordModifications();
// add interface
TypeDeclaration typeDeclaration = (TypeDeclaration) astRoot.types().get(0);
String interfaceName = Signature.getSimpleName(superInterfaceQName);
SimpleName name = astRoot.getAST().newSimpleName(interfaceName);
SimpleType newInterface = astRoot.getAST().newSimpleType(name);
boolean found = false;
for (Iterator i = typeDeclaration.superInterfaceTypes().iterator(); i.hasNext(); ) {
SimpleType aux = (SimpleType) i.next();
if (aux.getName().equals(interfaceName))
found = true;
}
if (!found)
typeDeclaration.superInterfaceTypes().add(newInterface);
// computation of the text edits
TextEdit edits = astRoot.rewrite(document, cu.getJavaProject().getOptions(true));
// computation of the new source code
edits.apply(document);
String newSource = document.get();
// update of the compilation unit
cu.getBuffer().setContents(newSource);
cu.commitWorkingCopy(false, progressMonitor);
cu.discardWorkingCopy();
// add stubs for methods required by the interface
cu = type.getCompilationUnit().getWorkingCopy(progressMonitor);
parser= ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
parser.setSource(cu);
astRoot = (CompilationUnit) parser.createAST(progressMonitor);
final ITypeBinding binding= ASTNodes.getTypeBinding(astRoot, type);
AddUnimplementedMethodsOperation operation = new AddUnimplementedMethodsOperation(astRoot, binding, null, -1, true, true, true);
operation.run(progressMonitor);
cu.commitWorkingCopy(true, progressMonitor);
} catch (JavaModelException e) {
e.printStackTrace();
} catch (MalformedTreeException e) {
e.printStackTrace();
} catch (BadLocationException e) {
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
} finally {
if (cu != null) {
try {
cu.discardWorkingCopy();
} catch (JavaModelException e) { e.printStackTrace(); }
}
progressMonitor.done();
}
}
/**
* @deprecated
*/
private static void removeInterfaceDeclaration(IType type, String superInterfaceQName, IProgressMonitor progressMonitor) {
ICompilationUnit cu = null;
try {
// remove the import first
cu = type.getCompilationUnit().getWorkingCopy(progressMonitor);
IImportDeclaration importDeclaration = cu.getImport(superInterfaceQName);
if (importDeclaration.exists()) {
importDeclaration.delete(true, progressMonitor);
cu.reconcile(AST.JLS3, false, null, progressMonitor);
}
// remove implements declaration
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(cu);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(progressMonitor);
String source = cu.getBuffer().getContents();
Document document= new Document(source);
// start modifications
astRoot.recordModifications();
// remove interface
String interfaceName = Signature.getSimpleName(superInterfaceQName);
TypeDeclaration typeDeclaration = (TypeDeclaration) astRoot.types().get(0);
for (ListIterator i = typeDeclaration.superInterfaceTypes().listIterator(); i.hasNext(); ) {
Object ii = i.next();
if (ii instanceof SimpleType) {
SimpleType aux = (SimpleType) ii;
if (aux.getName().getFullyQualifiedName().equals(superInterfaceQName) || aux.getName().toString().equals(interfaceName))
i.remove();
}
}
// computation of the text edits
TextEdit edits = astRoot.rewrite(document, cu.getJavaProject().getOptions(true));
// computation of the new source code
edits.apply(document);
String newSource = document.get();
// update of the compilation unit
cu.getBuffer().setContents(newSource);
cu.commitWorkingCopy(false, progressMonitor);
cu.discardWorkingCopy();
// add stubs for methods required by the interface
cu = type.getCompilationUnit().getWorkingCopy(progressMonitor);
parser= ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
parser.setSource(cu);
astRoot = (CompilationUnit) parser.createAST(progressMonitor);
final ITypeBinding binding= ASTNodes.getTypeBinding(astRoot, type);
AddUnimplementedMethodsOperation operation = new AddUnimplementedMethodsOperation(astRoot, binding, null, -1, true, true, true);
operation.run(progressMonitor);
cu.commitWorkingCopy(true, progressMonitor);
} catch (JavaModelException e) {
e.printStackTrace();
} catch (MalformedTreeException e) {
e.printStackTrace();
} catch (BadLocationException e) {
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
} finally {
if (cu != null) {
try {
cu.discardWorkingCopy();
} catch (JavaModelException e) { e.printStackTrace(); }
}
progressMonitor.done();
}
}
/**
* @deprecated
*/
private static boolean addExtendsDeclaration(IType type, String superClassQName, IProgressMonitor progressMonitor) {
ICompilationUnit cu = null;
try {
// create an import first
cu = type.getCompilationUnit().getWorkingCopy(progressMonitor);
if (!cu.getImport(superClassQName).exists()) {
cu.createImport(superClassQName, null, progressMonitor);
cu.reconcile(AST.JLS3, false, null, progressMonitor);
}
// add implements declaration
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(cu);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(progressMonitor);
String source = cu.getBuffer().getContents();
Document document= new Document(source);
// start modifications
astRoot.recordModifications();
// add supertype
TypeDeclaration typeDeclaration = (TypeDeclaration) astRoot.types().get(0);
String superClassName = Signature.getSimpleName(superClassQName);
SimpleName name = astRoot.getAST().newSimpleName(superClassName);
SimpleType newSuperclass = astRoot.getAST().newSimpleType(name);
// simply add superclass type
typeDeclaration.setSuperclassType(newSuperclass);
// computation of the text edits
TextEdit edits = astRoot.rewrite(document, cu.getJavaProject().getOptions(true));
// computation of the new source code
edits.apply(document);
String newSource = document.get();
// update of the compilation unit
cu.getBuffer().setContents(newSource);
cu.commitWorkingCopy(false, progressMonitor);
cu.discardWorkingCopy();
// add stubs for methods required by the interface
cu = type.getCompilationUnit().getWorkingCopy(progressMonitor);
parser= ASTParser.newParser(AST.JLS3);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
parser.setSource(cu);
astRoot = (CompilationUnit) parser.createAST(progressMonitor);
final ITypeBinding binding = ASTNodes.getTypeBinding(astRoot, type);
AddUnimplementedMethodsOperation operation = new AddUnimplementedMethodsOperation(astRoot, binding, null, -1, true, true, true);
operation.run(progressMonitor);
cu.commitWorkingCopy(true, progressMonitor);
return true;
} catch (JavaModelException e) {
e.printStackTrace();
} catch (MalformedTreeException e) {
e.printStackTrace();
} catch (BadLocationException e) {
e.printStackTrace();
} catch (CoreException e) {
e.printStackTrace();
} finally {
if (cu != null) {
try {
cu.discardWorkingCopy();
} catch (JavaModelException e) { e.printStackTrace(); }
}
progressMonitor.done();
}
return false;
}
/**
* @deprecated
*/
private static List<ASTNode> weaveAdvice(int adviceType, IMethod targetMethod, String advice, IProgressMonitor progressMonitor) {
List<ASTNode> result = null;
ICompilationUnit cu = null;
if (targetMethod.exists()) {
try {
IType type = targetMethod.getDeclaringType();
cu = type.getCompilationUnit().getWorkingCopy(progressMonitor);
// add implements declaration
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(cu);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
CompilationUnit astRoot = (CompilationUnit) parser.createAST(progressMonitor);
String source = cu.getBuffer().getContents();
Document document= new Document(source);
// start modifications
astRoot.recordModifications();
// get body of the targetMethod
AbstractTypeDeclaration abstractType = (AbstractTypeDeclaration) astRoot.types().get(0);
MethodDeclaration methodDeclaration = null;
for (Object aux : abstractType.bodyDeclarations()) {
BodyDeclaration bodyDeclaration = (BodyDeclaration) aux;
if (bodyDeclaration.getStartPosition() == targetMethod.getSourceRange().getOffset() &&
bodyDeclaration instanceof MethodDeclaration) {
methodDeclaration = (MethodDeclaration) bodyDeclaration;
break;
}
}
if (methodDeclaration != null) {
// found!
// create AST for the advice
ASTParser adviceParser = ASTParser.newParser(AST.JLS3);
adviceParser.setSource(advice.toCharArray());
adviceParser.setResolveBindings(true);
adviceParser.setBindingsRecovery(true);
adviceParser.setKind(ASTParser.K_STATEMENTS);
Block adviceAst = (Block) adviceParser.createAST(progressMonitor);
// get list of statements
Block body = methodDeclaration.getBody();
List<ASTNode> newStatements = ASTNode.copySubtrees(body.getAST(), adviceAst.statements());
// weave the advice
switch (adviceType) {
case BEFORE_ADVICE:
body.statements().addAll(0, newStatements);
break;
case AFTER_ADVICE:
body.statements().addAll(newStatements);
break;
}
// not sure that this will work. Probably need to apply the changes first and then lookup
// the nodes again in the committed working copy.
result = newStatements;
} else {
// this should never occur, because targetMethod already exists
// create new method similar to target method with given body
// TODO create the method!
}
// computation of the text edits
TextEdit edits = astRoot.rewrite(document, cu.getJavaProject().getOptions(true));
// computation of the new source code
edits.apply(document);
String newSource = document.get();
// update of the compilation unit
cu.getBuffer().setContents(newSource);
cu.commitWorkingCopy(false, progressMonitor);
return result;
} catch (JavaModelException e) {
e.printStackTrace();
} catch (MalformedTreeException e) {
e.printStackTrace();
} catch (BadLocationException e) {
e.printStackTrace();
} finally {
if (cu != null) {
try {
cu.discardWorkingCopy();
} catch (JavaModelException e) { e.printStackTrace(); }
}
progressMonitor.done();
}
}
return result;
}
public static TypeDeclaration createNestedTypeDeclaration(IJavaProject project, CompilationUnit compilationUnit, TypeDeclaration topLevelTypeDeclaration, String nestedClassName, IProgressMonitor progressMonitor) {
CompilationUnit cu = (compilationUnit != null ? compilationUnit : (CompilationUnit) topLevelTypeDeclaration.getRoot());
if (!recordModifications(cu))
return null;
String contents = "class " + nestedClassName + " {\n }";
// create a TypeDeclaration ast node
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(contents.toString().toCharArray());
parser.setProject(project);
parser.setKind(ASTParser.K_CLASS_BODY_DECLARATIONS);
parser.setResolveBindings(true);
parser.setBindingsRecovery(true);
ASTNode node = parser.createAST(progressMonitor);
if (node instanceof TypeDeclaration) {
node = ((TypeDeclaration) node).getTypes()[0];
}
if (node instanceof TypeDeclaration) {
node = ASTNode.copySubtree(topLevelTypeDeclaration.getAST(), node);
topLevelTypeDeclaration.bodyDeclarations().add(node);
return (TypeDeclaration) node;
}
return null;
}
}