/*******************************************************************************
* 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 java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
import org.springframework.ide.eclipse.quickfix.QuickfixImages;
import org.springframework.ide.eclipse.quickfix.jdt.util.ProposalCalculatorUtil;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author Terry Denney
* @since 2.6
*/
public class RequestMappingVariableCompletionProposal extends AnnotationCompletionProposal {
private final MethodDeclaration decl;
private final SingleVariableDeclaration param;
private final String variableName;
private StringLiteral oldTemplate;
private String newTemplateString;
private int cursorOffset;
public RequestMappingVariableCompletionProposal(SingleVariableDeclaration param, int startPos, int length,
Annotation annotation, MethodDeclaration decl, JavaContentAssistInvocationContext javaContext) {
this(param, param.getName().getFullyQualifiedName(), startPos, length, annotation, decl, javaContext);
}
public RequestMappingVariableCompletionProposal(SingleVariableDeclaration param, String variableName, int startPos,
int length, Annotation annotation, MethodDeclaration decl, JavaContentAssistInvocationContext javaContext) {
super("", javaContext.getCompilationUnit(), QuickfixImages.getImage(QuickfixImages.LOCAL_VARIABLE));
this.param = param;
this.variableName = variableName;
this.decl = decl;
setUpProposal(startPos, length, annotation);
// give higher priority to method parameter that has @PathVariable
// annotation
if (param != null && ProposalCalculatorUtil.hasAnnotation("PathVariable", param)) {
setRelevance(100);
}
}
@SuppressWarnings("unchecked")
private void setUpProposal(int startPos, int length, Annotation annotation) {
String typeName = ProposalCalculatorUtil.getTypeName(param.getType());
StringBuilder displayName = new StringBuilder();
displayName.append(variableName);
displayName.append(": ");
displayName.append(typeName);
displayName.append(" - ");
displayName.append(decl.getName().getFullyQualifiedName());
displayName.append("(");
List<SingleVariableDeclaration> params = decl.parameters();
for (int i = 0; i < params.size(); i++) {
if (i > 0) {
displayName.append(", ");
}
SingleVariableDeclaration currentParam = params.get(i);
displayName.append(ProposalCalculatorUtil.getTypeName(currentParam.getType()));
displayName.append(" ");
if (currentParam == param) {
displayName.append(variableName);
}
else {
displayName.append(currentParam.getName().getFullyQualifiedName());
}
}
displayName.append(")");
setDisplayName(displayName.toString());
if (annotation instanceof NormalAnnotation) {
List<MemberValuePair> pairs = ((NormalAnnotation) annotation).values();
for (MemberValuePair pair : pairs) {
Expression expression = pair.getValue();
int valueStartPos = expression.getStartPosition();
int valueLength = expression.getLength();
if (valueStartPos <= startPos && valueStartPos + valueLength >= startPos + length) {
setUpProposal(pair.getValue(), startPos, length);
break;
}
}
}
else if (annotation instanceof SingleMemberAnnotation) {
setUpProposal(((SingleMemberAnnotation) annotation).getValue(), startPos, length);
}
}
private void setUpProposal(Expression expression, int startPos, int length) {
int valueStartPos = expression.getStartPosition();
int valueLength = expression.getLength();
if (expression instanceof StringLiteral) {
valueStartPos++; // skip over open quotes
valueLength -= 2; // skip over quotes
StringLiteral stringLiteral = (StringLiteral) expression;
this.oldTemplate = stringLiteral;
int lengthDiff = valueLength - length;
int offset = startPos - valueStartPos;
int remainingLength = lengthDiff - offset;
StringBuffer buffer = new StringBuffer();
String literalValue = stringLiteral.getLiteralValue();
buffer.append(literalValue.substring(0, startPos - valueStartPos));
buffer.append(variableName);
buffer.append(literalValue.substring(valueLength - remainingLength));
this.cursorOffset = startPos - valueStartPos;
newTemplateString = buffer.toString();
}
}
@Override
protected ASTRewrite getRewrite() throws CoreException {
AST ast = decl.getAST();
ASTRewrite rewrite = ASTRewrite.create(ast);
if (newTemplateString == null || oldTemplate == null) {
return rewrite;
}
boolean isLinked = false;
if (param != null && !ProposalCalculatorUtil.hasAnnotation("PathVariable", param)) {
String requestMappingTypeName = PathVariable.class.getCanonicalName();
if (!ProposalCalculatorUtil.containsImport(getCompilationUnit(), requestMappingTypeName)) {
CompilationUnit astRoot = ASTResolving.findParentCompilationUnit(decl);
ImportRewrite importRewrite = createImportRewrite(astRoot);
importRewrite.addImport(requestMappingTypeName);
}
SingleMemberAnnotation annotation = ast.newSingleMemberAnnotation();
annotation.setTypeName(ast.newSimpleName("PathVariable"));
StringLiteral pathVariableName = ast.newStringLiteral();
pathVariableName.setLiteralValue(variableName);
ITrackedNodePosition trackPathVariable = rewrite.track(pathVariableName);
addLinkedPosition(new StringLiteralTrackedPosition(trackPathVariable), true, "PathVariable");
annotation.setValue(pathVariableName);
isLinked = true;
ChildListPropertyDescriptor property;
property = SingleVariableDeclaration.MODIFIERS2_PROPERTY;
rewrite.getListRewrite(param, property).insertLast(annotation, null);
}
StringLiteral newTemplate = ast.newStringLiteral();
newTemplate.setLiteralValue(newTemplateString);
ITrackedNodePosition trackTemplateVariable = rewrite.track(newTemplate);
rewrite.replace(oldTemplate, newTemplate, null);
if (isLinked) {
addLinkedPosition(
new StringLiteralTrackedPosition(trackTemplateVariable, cursorOffset, variableName.length(), false),
false, "PathVariable");
}
else {
setTrackPosition(new StringLiteralTrackedPosition(trackTemplateVariable, cursorOffset
+ variableName.length(), 0, true));
}
return rewrite;
}
}