/******************************************************************************* * Copyright (c) 2012, 2013 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; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.core.resources.IFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CompilationParticipant; import org.eclipse.jdt.core.compiler.ReconcileContext; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.IAnnotationBinding; import org.eclipse.jdt.core.dom.IMemberValuePairBinding; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.springframework.ide.eclipse.quickfix.jdt.util.ProposalCalculatorUtil; import org.springframework.ide.eclipse.quickfix.jdt.util.UriTemplateVariable; import org.springsource.ide.eclipse.commons.core.SpringCoreUtils; /** * Compilation participant to display warning in Spring annotation definitions * * @author Terry Denney * @author Martin Lippert * @since 2.6 */ public class AnnotationCompilationParticipant extends CompilationParticipant { @SuppressWarnings("unchecked") private List<String> findPathVariables(AbstractTypeDeclaration typeDecl) { List<String> pathVariables = new ArrayList<String>(); List<BodyDeclaration> bodyDecls = typeDecl.bodyDeclarations(); for (BodyDeclaration bodyDecl : bodyDecls) { if (bodyDecl instanceof MethodDeclaration) { MethodDeclaration methodDecl = (MethodDeclaration) bodyDecl; pathVariables.addAll(findPathVariables(methodDecl)); } } return pathVariables; } private List<String> findPathVariables(ITypeBinding typeBinding) { List<String> pathVariables = new ArrayList<String>(); if (typeBinding == null) { return pathVariables; } IMethodBinding[] methods = typeBinding.getDeclaredMethods(); for (IMethodBinding method : methods) { for (int i = 0; i < method.getParameterTypes().length; i++) { IAnnotationBinding[] annotations = method.getParameterAnnotations(i); for (IAnnotationBinding annotation : annotations) { if ("PathVariable".equals(annotation.getName())) { IMemberValuePairBinding[] memberValuePairs = annotation.getAllMemberValuePairs(); String valueStr = null; for (IMemberValuePairBinding memberValuePair : memberValuePairs) { if ("value".equals(memberValuePair.getName())) { Object value = memberValuePair.getValue(); if (value instanceof String) { valueStr = (String) value; } } } // "value" attribute not explicitly defined, use // parameter name instead if (valueStr == null || valueStr.length() == 0) { IJavaElement javaMethod = method.getJavaElement(); if (javaMethod instanceof IMethod) { try { valueStr = ((IMethod) javaMethod).getParameterNames()[i]; } catch (JavaModelException e) { } } } if (valueStr != null) { pathVariables.add(valueStr); } } } } } pathVariables.addAll(findPathVariables(typeBinding.getSuperclass())); return pathVariables; } @SuppressWarnings("unchecked") private List<String> findPathVariables(MethodDeclaration methodDecl) { List<String> pathVariables = new ArrayList<String>(); List<SingleVariableDeclaration> params = methodDecl.parameters(); for (SingleVariableDeclaration param : params) { String variableName = ProposalCalculatorUtil.getPathVariableName(param); if (variableName != null) { pathVariables.add(variableName); } } return pathVariables; } @SuppressWarnings("unchecked") @Override public void reconcile(ReconcileContext context) { if (context.getDelta() == null) { return; } CompilationUnit cuAST = context.getDelta().getCompilationUnitAST(); ICompilationUnit cu = context.getWorkingCopy(); IFile file = (IFile) cu.getResource(); List<MissingPathVariableWarning> problems = new ArrayList<MissingPathVariableWarning>(); if (cuAST != null) { List<AbstractTypeDeclaration> typeDecls = cuAST.types(); for (AbstractTypeDeclaration typeDecl : typeDecls) { List<BodyDeclaration> bodyDecls = typeDecl.bodyDeclarations(); for (BodyDeclaration bodyDecl : bodyDecls) { if (bodyDecl instanceof MethodDeclaration) { MethodDeclaration methodDecl = (MethodDeclaration) bodyDecl; List<String> currentPathVariables = findPathVariables(methodDecl); Set<Annotation> annotations = ProposalCalculatorUtil.findAnnotations("RequestMapping", methodDecl); for (Annotation annotation : annotations) { List<UriTemplateVariable> variables = ProposalCalculatorUtil .getUriTemplatVariables(annotation); for (UriTemplateVariable variable : variables) { boolean found = false; for (String currentPathVariable : currentPathVariables) { if (Pattern.matches(variable.getVariableName(), currentPathVariable)) { found = true; break; } } if (!found) { problems.add(new MissingPathVariableWarning(annotation, variable, file, cuAST .getLineNumber(variable.getOffset()))); } } } } } List<String> pathVariables; if (context.isResolvingBindings()) { pathVariables = new ArrayList<String>(); ITypeBinding typeBinding = typeDecl.resolveBinding(); while (typeBinding != null && !typeDecl.getAST().resolveWellKnownType("java.lang.Object").equals(typeBinding)) { pathVariables.addAll(findPathVariables(typeBinding)); typeBinding = typeBinding.getSuperclass(); } } else { pathVariables = findPathVariables(typeDecl); } Set<Annotation> annotations = ProposalCalculatorUtil.findAnnotations("RequestMapping", typeDecl); for (Annotation annotation : annotations) { List<UriTemplateVariable> variables = ProposalCalculatorUtil.getUriTemplatVariables(annotation); for (UriTemplateVariable variable : variables) { if (!pathVariables.contains(variable.getVariableName())) { problems.add(new MissingPathVariableWarning(annotation, variable, file, cuAST .getLineNumber(variable.getOffset()))); } } } } } context.putProblems(MissingPathVariableWarning.MARKER_TYPE, problems.toArray(new CategorizedProblem[problems.size()])); } @Override public boolean isActive(IJavaProject project) { return SpringCoreUtils.isSpringProject(project.getProject()); } }