/******************************************************************************* * 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.computers; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MarkerAnnotation; 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.TypeDeclaration; import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.internal.core.SourceRefElement; import org.eclipse.jdt.internal.core.SourceType; import org.eclipse.jdt.internal.ui.text.correction.AssistContext; import org.eclipse.jdt.ui.SharedASTProvider; import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.source.SourceViewer; import org.springframework.ide.eclipse.quickfix.jdt.proposals.RequestMappingVariableCompletionProposal; import org.springframework.ide.eclipse.quickfix.jdt.util.ProposalCalculatorUtil; /** * @author Terry Denney * @author Martin Lippert * @since 2.6 */ public class RequestMappingVariableProposalComputer extends AnnotationProposalComputer { // TODO: clean up code and restructure to not find ASTNode till proposal is // invoked @Override protected List<ICompletionProposal> computeCompletionProposals(SourceMethod method, String value, IAnnotation annotation, JavaContentAssistInvocationContext javaContext) throws JavaModelException { // // TODO Auto-generated method stub // return super.computeCompletionProposals(type, value, annotation, // javaContext); // } // @Override // protected List<ICompletionProposal> // computeCompletionProposals(SourceMethod method, // LocationInformation locationInfo, Annotation annotation, // JavaContentAssistInvocationContext javaContext) // throws JavaModelException { return computeCompletionProposalsHelper(method, value, annotation, javaContext); } @Override protected List<ICompletionProposal> computeCompletionProposals(SourceType type, String value, IAnnotation annotation, JavaContentAssistInvocationContext javaContext) throws JavaModelException { // // TODO Auto-generated method stub // return super.computeCompletionProposals(type, value, annotation, // javaContext); // } // @Override // protected List<ICompletionProposal> // computeCompletionProposals(SourceType type, LocationInformation // locationInfo, // Annotation annotation, JavaContentAssistInvocationContext // javaContext) throws JavaModelException { return computeCompletionProposalsHelper(type, value, annotation, javaContext); } // private List<ICompletionProposal> // computeCompletionProposalsHelper(IMember element, // LocationInformation locationInfo, Annotation annotation, // JavaContentAssistInvocationContext javaContext) // throws JavaModelException { private List<ICompletionProposal> computeCompletionProposalsHelper(IMember element, String value, IAnnotation a, JavaContentAssistInvocationContext javaContext) throws JavaModelException { List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); ITextViewer viewer = javaContext.getViewer(); if (viewer instanceof SourceViewer) { ICompilationUnit cu = javaContext.getCompilationUnit(); SourceViewer sourceViewer = (SourceViewer) javaContext.getViewer(); int invocationOffset = javaContext.getInvocationOffset(); AssistContext assistContext = new AssistContext(cu, sourceViewer, invocationOffset, 0, SharedASTProvider.WAIT_NO); ASTNode node = ((SourceRefElement) a).findNode(assistContext.getASTRoot()); if (node == null) { node = assistContext.getCoveredNode(); } if (!(node instanceof Annotation)) { return proposals; } LocationInformation locationInfo = null; if (node instanceof NormalAnnotation) { NormalAnnotation normalAnnotation = (NormalAnnotation) node; @SuppressWarnings("unchecked") List<MemberValuePair> pairs = normalAnnotation.values(); for (MemberValuePair pair : pairs) { Expression expression = pair.getValue(); if (expression instanceof StringLiteral) { locationInfo = getLocationInformation((StringLiteral) expression, javaContext); } } } else if (node instanceof SingleMemberAnnotation) { SingleMemberAnnotation singleMemberAnnotation = (SingleMemberAnnotation) node; Expression expression = singleMemberAnnotation.getValue(); if (expression instanceof StringLiteral) { locationInfo = getLocationInformation((StringLiteral) expression, javaContext); } } if (locationInfo == null) { return proposals; } int locationOffset = locationInfo.getOffset(); int locationLength = locationInfo.getLength(); String content = locationInfo.getFilter(); if (invocationOffset >= locationOffset && invocationOffset <= locationOffset + locationLength) { int startIndex; int index = 0; boolean found = false; while (!found) { startIndex = content.indexOf("{"); if (startIndex < 0 || startIndex + locationOffset >= invocationOffset) { break; } content = content.substring(startIndex + 1); index += startIndex + 1; if (!(content.contains("{"))) { found = true; } } locationOffset += 1; // ignore opening quote if (found) { ISourceRange sourceRange = element.getSourceRange(); //Now get the 'real' ast. assistContext = new AssistContext(javaContext.getCompilationUnit(), sourceViewer, sourceRange.getOffset(), sourceRange.getLength(), SharedASTProvider.WAIT_YES); node = assistContext.getCoveringNode(); Annotation annotation = (Annotation) ((SourceRefElement) a).findNode(assistContext.getASTRoot()); if (content.endsWith("}")) { content = content.substring(0, content.length() - 1); } if (node instanceof MethodDeclaration) { MethodDeclaration methodDecl = (MethodDeclaration) node; proposals.addAll(getProposals(methodDecl, annotation, content, locationOffset, index, javaContext)); } else if (node instanceof TypeDeclaration) { TypeDeclaration typeDecl = (TypeDeclaration) node; MethodDeclaration[] methodDecls = typeDecl.getMethods(); for (MethodDeclaration methodDecl : methodDecls) { proposals.addAll(getProposals(methodDecl, annotation, content, locationOffset, index, javaContext)); } } } } } return proposals; } private List<ICompletionProposal> getProposals(MethodDeclaration methodDecl, Annotation annotation, String filter, int valueOffset, int variableOffset, JavaContentAssistInvocationContext javaContext) { List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); @SuppressWarnings("unchecked") List<SingleVariableDeclaration> params = methodDecl.parameters(); for (SingleVariableDeclaration param : params) { ITypeBinding typeBinding = param.getType().resolveBinding(); if (!ProposalCalculatorUtil.isKnownRequestMappingParamType(javaContext.getProject().getProject(), typeBinding)) { Set<Annotation> pathVariables = ProposalCalculatorUtil.findAnnotations("PathVariable", param); // boolean differentVariableName = false; if (pathVariables.size() > 0) { for (Annotation pathVariable : pathVariables) { if (pathVariable instanceof SingleMemberAnnotation) { Expression expression = ((SingleMemberAnnotation) pathVariable).getValue(); if (expression instanceof StringLiteral) { String variableName = ((StringLiteral) expression).getLiteralValue(); if (variableName.startsWith(filter)) { proposals.add(new RequestMappingVariableCompletionProposal(param, variableName, valueOffset + variableOffset, filter.length(), annotation, methodDecl, javaContext)); // differentVariableName = true; } } } else if (pathVariable instanceof MarkerAnnotation) { String paramName = param.getName().getFullyQualifiedName(); if (paramName.startsWith(filter)) { proposals.add(new RequestMappingVariableCompletionProposal(param, paramName, valueOffset + variableOffset, filter.length(), annotation, methodDecl, javaContext)); } } } } } } return proposals; } }