/******************************************************************************* * 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.internal.bestpractices.quickfix; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Status; 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.JavaCore; import org.eclipse.jdt.core.JavaModelException; 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.Block; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ExpressionStatement; import org.eclipse.jdt.core.dom.IBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodInvocation; import org.eclipse.jdt.internal.ui.JavaPluginImages; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IMarkerResolution2; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.springframework.ide.eclipse.bestpractices.BestPracticesPluginConstants; import org.springsource.ide.eclipse.commons.core.StatusHandler; /** * Resolution for missing property markers. Creates a corresponding setter in * the class referenced by the bean definition. * @author Wesley Coelho * @author Steffen Pingel * @author Leo Dos Santos * @author Christian Dupuis */ public abstract class AbstractCreateMethodMarkerResolution implements IMarkerResolution2 { private static final String MESSAGE_ATTRIBUTE_KEY = "message"; private String markerMessage = ""; public AbstractCreateMethodMarkerResolution(IMarker marker) { markerMessage = marker.getAttribute(MESSAGE_ATTRIBUTE_KEY, ""); } protected String extractQuotedString(String startTag, String message) { int startPos = message.indexOf(startTag) + startTag.length(); int endPos = message.indexOf("'", startPos); return message.substring(startPos, endPos); } @SuppressWarnings("unchecked") protected List<Expression> getArguments(Expression invocationExpression) { MethodInvocation methodInvocation = (MethodInvocation) invocationExpression; List<Expression> arguments = new ArrayList<Expression>(); for (Iterator<Expression> argumentIter = methodInvocation.arguments().iterator(); argumentIter.hasNext();) { Expression argumentExpression = argumentIter.next(); arguments.add(argumentExpression); } return arguments; } public abstract String getDescription(); public Image getImage() { return null; } public abstract String getLabel(); protected String getMarkerMessage() { return markerMessage; } protected Expression getMockMethodInvocation() { ASTParser fooParser = ASTParser.newParser(AST.JLS3); fooParser.setKind(ASTParser.K_STATEMENTS); String mockMethodInvocationCode = getNewMethodName() + "(" + getNewMethodParameters() + ");"; fooParser.setSource(mockMethodInvocationCode.toCharArray()); fooParser.setResolveBindings(true); ASTNode parsedAstNode = fooParser.createAST(null); Block codeBlock = (Block) parsedAstNode; ExpressionStatement methodInvocationExpressionStatement = (ExpressionStatement) codeBlock.statements().get(0); return methodInvocationExpressionStatement.getExpression(); } protected abstract String getNewMethodName(); /** * Override to provide parameters for the new method name. Parameters should * not include types and should be as they would appear in a method call to * the new method, e.g. "object" or "obj1, obj2". */ protected String getNewMethodParameters() { return ""; } protected abstract String getTargetClass(); protected ITypeBinding getTargetTypeBinding(IJavaProject javaProject, IType targetType) { ASTParser bindingParser = ASTParser.newParser(AST.JLS3); bindingParser.setKind(ASTParser.K_COMPILATION_UNIT); bindingParser.setSource(targetType.getCompilationUnit()); bindingParser.setResolveBindings(true); bindingParser.setProject(javaProject); IBinding[] bindings = bindingParser .createBindings(new IJavaElement[] { targetType }, new NullProgressMonitor()); ITypeBinding typeBinding = (ITypeBinding) bindings[0]; return typeBinding; } public void run(IMarker marker) { IProject project = marker.getResource().getProject(); IJavaProject javaProject = JavaCore.create(project); IType targetType = null; try { targetType = javaProject.findType(getTargetClass()); } catch (JavaModelException e) { StatusHandler.log(e.getStatus()); MessageDialog.openError(Display.getDefault().getActiveShell(), "Quick Fix Error", "Could not find class '" + getTargetClass() + "'"); return; } if (targetType == null) { StatusHandler.log(new Status(Status.ERROR, BestPracticesPluginConstants.PLUGIN_ID, "Could not find class '" + getTargetClass() + "'")); MessageDialog.openError(Display.getDefault().getActiveShell(), "Quick Fix Error", "Could not find class '" + getTargetClass() + "'"); return; } Expression mockMethodInvocation = getMockMethodInvocation(); ITypeBinding typeBinding = getTargetTypeBinding(javaProject, targetType); List<Expression> arguments = getArguments(mockMethodInvocation); Image image = JavaPluginImages.get(JavaPluginImages.IMG_MISC_PUBLIC); // XXX e3.4 work around changed class name in Eclipse 3.4 ICompletionProposal newMethodCompletionProposal; try { Class<?> clazz; try { // Eclipse 3.3 clazz = Class.forName("org.eclipse.jdt.internal.ui.text.correction.NewMethodCompletionProposal"); } catch (ClassNotFoundException e) { // Eclipse 3.4 clazz = Class .forName("org.eclipse.jdt.internal.ui.text.correction.proposals.NewMethodCorrectionProposal"); } Constructor<?> constructor = clazz.getConstructor(String.class, ICompilationUnit.class, ASTNode.class, List.class, ITypeBinding.class, int.class, Image.class); newMethodCompletionProposal = (ICompletionProposal) constructor.newInstance("Add missing property", targetType.getCompilationUnit(), mockMethodInvocation, arguments, typeBinding, 0, image); } catch (Throwable t) { StatusHandler.log(new Status(IStatus.ERROR, BestPracticesPluginConstants.PLUGIN_ID, "Problems occured when creating completion proposal", t)); MessageDialog.openError(Display.getDefault().getActiveShell(), "Quick Fix Error", "Could not execute correction proposal"); return; } // Get the XML document model IStructuredModel model = null; try { model = XmlQuickFixUtil.getModel(marker); // Execute Quick Fix newMethodCompletionProposal.apply(model.getStructuredDocument()); } catch (CoreException e) { StatusHandler.log(e.getStatus()); MessageDialog.openError(Display.getDefault().getActiveShell(), "Quick Fix Error", "Could not find error marker"); } finally { if (model != null) { model.releaseFromEdit(); model = null; } } } }