/*******************************************************************************
* Copyright (c) 2012 VMware, Inc.
* 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:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.quickfix.jdt.proposals;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility2;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
import org.eclipse.osgi.util.NLS;
import org.springframework.ide.eclipse.quickfix.QuickfixImages;
/**
* @author Kaitlin Duck Sherwood
* @since 2.9
*/
public class AddAutowiredConstructorCompletionProposal extends AnnotationCompletionProposal {
private final TypeDeclaration typeDecl;
private final ICompilationUnit compilationUnit;
private final IVariableBinding[] variableBindings;
public AddAutowiredConstructorCompletionProposal(TypeDeclaration typeDecl, ICompilationUnit cu,
IVariableBinding[] myVariables) {
super(NLS.bind("Add {0} constructor", "@Autowired"), cu, QuickfixImages.getImage(QuickfixImages.ANNOTATION));
this.typeDecl = typeDecl;
this.compilationUnit = cu;
this.variableBindings = myVariables;
}
@Override
protected ASTRewrite getRewrite() throws CoreException {
CompilationUnit astRoot = ASTResolving.findParentCompilationUnit(typeDecl);
AST ast = astRoot.getAST();
ASTRewrite astRewrite = ASTRewrite.create(ast);
ImportRewrite importRewrite = StubUtility.createImportRewrite(astRoot, true);
ListRewrite listRewriter = astRewrite.getListRewrite(typeDecl, (typeDecl).getBodyDeclarationsProperty());
if (listRewriter != null) {
IJavaProject javaProject = compilationUnit.getJavaProject();
CodeGenerationSettings settings = JavaPreferencesSettings.getCodeGenerationSettings(javaProject);
MethodDeclaration methodDecl = createNewConstructor(typeDecl, ast);
IMethodBinding constructorBinding = ASTNodes.getMethodBinding(methodDecl.getName());
ITypeBinding typeBinding = ASTNodes.getTypeBinding(typeDecl.getName());
ImportRewriteContext context = new ContextSensitiveImportRewriteContext(typeDecl, importRewrite);
MethodDeclaration stub = StubUtility2.createConstructorStub(compilationUnit, astRewrite, importRewrite,
context, typeBinding, constructorBinding, this.variableBindings, Modifier.PUBLIC, settings);
if (stub != null) {
IType type = null;
// Getting the IType in question turned out to be much more
// difficult than one would hope. At this point, it appears that
// typeDecl "belongs to" a different compilation unit than the
// one which we can get at from here. Thus, we have to look
// through *this* compilation unit for the IType with the name
// we are looking for.
for (Object type2 : compilationUnit.getAllTypes()) {
if (type2 instanceof IType) {
// Allegedly, getFullyQualifiedName will return the
// fully qualified name (e.g. a.b.c.Foo), but I have
// seen it return a name without the package type.
String typeName = typeDecl.getName().getFullyQualifiedName();
String type2Name = ((IType) type2).getFullyQualifiedName();
if (type2Name.endsWith(typeName)) {
type = (IType) type2;
break;
}
}
}
if (type == null) {
return astRewrite; // this should never get hit
}
IJavaElement[] fields = type.getChildren();
IJavaElement firstField;
if (fields.length == 0) {
firstField = null;
}
else {
firstField = fields[0];
}
ASTNode insertion = StubUtility2.getNodeToInsertBefore(listRewriter, firstField);
if (insertion != null && insertion.getParent() == typeDecl) {
listRewriter.insertBefore(stub, insertion, null);
}
else {
listRewriter.insertLast(stub, null);
}
constructorBinding = ASTNodes.getMethodBinding(stub.getName());
addAutowiredAnnotation(javaProject, astRewrite, stub, constructorBinding);
}
}
return astRewrite;
}
// based on StubUtility2.addOverrideAnnotation
public void addAutowiredAnnotation(IJavaProject project, ASTRewrite rewrite, MethodDeclaration decl,
IMethodBinding binding) {
final Annotation marker = rewrite.getAST().newMarkerAnnotation();
marker.setTypeName(rewrite.getAST().newSimpleName("Autowired")); //$NON-NLS-1$
rewrite.getListRewrite(decl, MethodDeclaration.MODIFIERS2_PROPERTY).insertFirst(marker, null);
}
@SuppressWarnings("unchecked")
private MethodDeclaration createNewConstructor(TypeDeclaration typeDecl2, AST ast) {
MethodDeclaration newConstructor = ast.newMethodDeclaration();
newConstructor.setConstructor(true);
newConstructor.setExtraDimensions(0);
newConstructor.setJavadoc(null);
int modifier = Modifier.PUBLIC & Modifier.CONSTRUCTOR_INVOCATION;
newConstructor.modifiers().addAll(ASTNodeFactory.newModifiers(ast, modifier));
newConstructor.setName(ast.newSimpleName(typeDecl.getName().toString()));
return newConstructor;
}
}