/*******************************************************************************
* 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.util;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IPackageDeclaration;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldDeclaration;
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.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleType;
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.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.WildcardType;
import org.eclipse.jdt.internal.core.SourceMethod;
import org.eclipse.jdt.internal.ui.text.correction.AssistContext;
import org.eclipse.jdt.ui.text.java.IInvocationContext;
import org.eclipse.jdt.ui.text.java.JavaContentAssistInvocationContext;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.beans.core.autowire.internal.provider.AutowireDependencyProvider;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansModelUtils;
import org.springframework.ide.eclipse.beans.core.internal.model.validation.rules.ValidationRuleUtils;
import org.springframework.ide.eclipse.beans.core.model.IBean;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.IBeansModel;
import org.springframework.ide.eclipse.beans.core.model.IBeansProject;
import org.springframework.ide.eclipse.core.SpringCore;
import org.springframework.ide.eclipse.core.java.JdtUtils;
import org.springframework.ide.eclipse.core.model.validation.ValidationProblem;
import org.springframework.ide.eclipse.core.model.validation.ValidationProblemAttribute;
/**
* @author Terry Denney
* @author Martin Lippert
*/
public class ProposalCalculatorUtil {
public static boolean containsImport(ICompilationUnit cu, String importName) {
String importPackageName = importName;
int index = importName.lastIndexOf(".");
if (index > 0) {
importPackageName = importName.substring(0, index);
}
try {
IPackageDeclaration[] packageDecls = cu.getPackageDeclarations();
for (IPackageDeclaration packageDecl : packageDecls) {
String packageName = packageDecl.getElementName();
if (packageName.equals(importPackageName)) {
return true;
}
}
IImportDeclaration[] importDecls = cu.getImports();
for (IImportDeclaration importDecl : importDecls) {
String importElementName = importDecl.getElementName();
index = importElementName.lastIndexOf(".");
String importElementPackageName = importElementName;
if (index > 0) {
importElementPackageName = importElementName.substring(0, index);
}
if (importElementName.endsWith("*")) {
if (importPackageName.equals(importElementPackageName)) {
return true;
}
}
else {
if (importName.equals(importElementName)) {
return true;
}
}
}
}
catch (JavaModelException e) {
SpringCore.log(e);
}
return false;
}
public static Set<Annotation> findAnnotations(String annotation, ASTNode node) {
AnnotationFinder finder = new AnnotationFinder(annotation);
node.accept(finder);
return finder.getAnnotations();
}
public static Set<Annotation> findAnnotations(String annotation, int invocationOffset, ASTNode node) {
AnnotationFinder finder = new AnnotationFinder(annotation, invocationOffset);
node.accept(finder);
return finder.getAnnotations();
}
public static boolean hasAnnotationInParameters(SourceMethod method, String annotationName) {
try {
if (method.getSource() != null) {
return method.getSource().contains('@' + annotationName);
}
return false;
}
catch (JavaModelException e) {
SpringCore.log(e);
return false;
}
}
public static boolean hasAnnotationOnType(ICompilationUnit cu, String annotationName) {
try {
IType[] allTypes = cu.getAllTypes();
for (IType type : allTypes) {
IAnnotation[] annotations = type.getAnnotations();
for (IAnnotation annotation : annotations) {
if (annotationName.equals(annotation.getElementName())) {
return true;
}
}
}
}
catch (JavaModelException e) {
SpringCore.log(e);
}
return false;
}
public static boolean hasAnnotation(IAnnotatable element, String annotationName) {
try {
IAnnotation[] annotations = element.getAnnotations();
for (IAnnotation annotation : annotations) {
if (annotationName.equals(annotation.getElementName())) {
return true;
}
}
}
catch (JavaModelException e) {
SpringCore.log(e);
}
return false;
}
/**
* @param declToMatch
* @param problemType
* @param cu
* @param typeNameToMatch problem marker should match this typeName, set to
* Null if it should match any types
* @return
*/
public static ValidationProblem findProblem(BodyDeclaration declToMatch, String problemType, ICompilationUnit cu,
String typeNameToMatch) {
IBeansModel model = BeansCorePlugin.getModel();
IBeansProject springProject = model.getProject(cu.getJavaProject().getProject());
Set<IBeansConfig> configs = springProject.getConfigs();
for (IBeansConfig config : configs) {
AutowireDependencyProvider provider = new AutowireDependencyProvider(config, config);
provider.resolveAutowiredDependencies();
List<ValidationProblem> problems = provider.getValidationProblems();
for (ValidationProblem problem : problems) {
ValidationProblemAttribute[] problemAttributes = problem.getAttributes();
boolean matched = false;
BodyDeclaration problemDecl = null;
String typeName = null;
for (ValidationProblemAttribute problemAttribute : problemAttributes) {
if (AutowireDependencyProvider.AUTOWIRE_PROBLEM_TYPE.equals(problemAttribute.getKey())) {
if (problemType.equals(problemAttribute.getValue())) {
matched = true;
}
}
else if ("JAVA_HANDLE".equals(problemAttribute.getKey())) {
problemDecl = getBodyDeclaration(JavaCore.create((String) problemAttribute.getValue()));
}
else if (AutowireDependencyProvider.BEAN_TYPE.equals(problemAttribute.getKey())) {
typeName = (String) problemAttribute.getValue();
}
}
if (matched && problemDecl != null && problemDecl.equals(declToMatch)) {
if (typeNameToMatch == null || (typeName != null && typeName.equals(typeNameToMatch))) {
return problem;
}
}
}
}
return null;
}
public static Set<String> getMatchingBeans(IInvocationContext context, ITypeBinding typeBinding) {
IProject project = null;
try {
project = context.getCompilationUnit().getUnderlyingResource().getProject();
}
catch (JavaModelException e) {
return new HashSet<String>();
}
String typeName = typeBinding.getQualifiedName();
IBeansProject springProject = BeansCorePlugin.getModel().getProject(project);
return getMatchingBeans(typeName, springProject, null);
}
public static Set<String> getMatchingBeans(IInvocationContext context, ITypeBinding typeBinding, String qualifier) {
IProject project = null;
try {
project = context.getCompilationUnit().getUnderlyingResource().getProject();
}
catch (JavaModelException e) {
return new HashSet<String>();
}
String typeName = typeBinding.getQualifiedName();
IBeansProject springProject = BeansCorePlugin.getModel().getProject(project);
return getMatchingBeans(typeName, springProject, qualifier);
}
public static Set<String> getMatchingBeans(JavaContentAssistInvocationContext context, ITypeBinding type) {
IBeansProject springProject = BeansCorePlugin.getModel().getProject(context.getProject().getProject());
String typeName = type.getQualifiedName();
return getMatchingBeans(typeName, springProject, null);
}
// public static Set<String> getMatchingBeans(String typeName, IBeansProject
// springProject) {
// Set<String> matchingBeans = new HashSet<String>();
//
// Class<?> clazz = null;
// try {
// clazz = ClassUtils.loadClass(typeName);
//
// Set<IBeansConfig> configs = springProject.getConfigs();
// for (IBeansConfig config : configs) {
// AutowireDependencyProvider provider = new
// AutowireDependencyProvider(config, config);
// provider.getBeansForType(clazz);
// }
// }
// catch (ClassNotFoundException e) {
// }
//
// return matchingBeans;
// }
public static Set<String> getMatchingBeans(String typeName, IBeansProject springProject, String qualifier) {
Set<String> matchingBeans = new HashSet<String>();
Class<?> clazz = null;
ClassLoader classLoader = JdtUtils.getProjectClassLoaderSupport(springProject.getProject(),
BeansCorePlugin.getClassLoader()).getProjectClassLoader();
try {
clazz = classLoader.loadClass(typeName);
}
catch (ClassNotFoundException e) {
return matchingBeans;
}
Set<IBean> beans = BeansModelUtils.getBeans(springProject);
for (IBean bean : beans) {
String beanClassName = ValidationRuleUtils.getBeanClassName(bean);
if (beanClassName != null) {
try {
Class<?> beanClass = classLoader.loadClass(beanClassName);
if (clazz.isAssignableFrom(beanClass)) {
if (qualifier == null || qualifier.equals(bean.getElementName())) {
matchingBeans.add(bean.getElementName());
}
}
}
catch (ClassNotFoundException e) {
}
}
}
return matchingBeans;
}
// public static Set<String> getMatchingBeans(BodyDeclaration declToMatch,
// ICompilationUnit cu, String typeName) {
// System.err.println("getMatchingBeans: " + typeName);
// Set<String> matchingBeans = new HashSet<String>();
//
// ValidationProblem problem = findProblem(declToMatch,
// AutowireDependencyProvider.TOO_MANY_MATCHING_BEANS, cu,
// typeName);
// if (problem != null) {
// ValidationProblemAttribute[] attributes = problem.getAttributes();
// for (ValidationProblemAttribute attribute : attributes) {
// if
// (attribute.getKey().startsWith(AutowireDependencyProvider.MATCHING_BEAN_NAME))
// {
// matchingBeans.add((String) attribute.getValue());
// }
// }
// }
//
// return matchingBeans;
// }
public static String getPathVariableName(SingleVariableDeclaration param) {
Set<Annotation> annotations = ProposalCalculatorUtil.findAnnotations("PathVariable", param);
for (Annotation annotation : annotations) {
if (annotation.isMarkerAnnotation()) {
return param.getName().getFullyQualifiedName();
}
else if (annotation.isSingleMemberAnnotation()) {
Expression value = ((SingleMemberAnnotation) annotation).getValue();
if (value instanceof StringLiteral) {
return ((StringLiteral) value).getLiteralValue();
}
}
else if (annotation.isNormalAnnotation()) {
NormalAnnotation nAnnotation = (NormalAnnotation) annotation;
@SuppressWarnings("unchecked")
List<MemberValuePair> valuePairs = nAnnotation.values();
for (MemberValuePair valuePair : valuePairs) {
if ("value".equals(valuePair.getName().getIdentifier())) {
Expression value = valuePair.getValue();
if (value instanceof StringLiteral) {
return ((StringLiteral) value).getLiteralValue();
}
}
}
}
}
return null;
}
@SuppressWarnings("unchecked")
public static MemberValuePair getRequiredMemberValuePair(BodyDeclaration decl) {
Set<Annotation> annotations = ProposalCalculatorUtil.findAnnotations("Autowired", decl);
for (Annotation annotation : annotations) {
if (annotation instanceof NormalAnnotation) {
NormalAnnotation normalAnnotation = (NormalAnnotation) annotation;
List<MemberValuePair> values = normalAnnotation.values();
for (MemberValuePair valuePair : values) {
if ("required".equals(valuePair.getName().toString())) {
return valuePair;
}
}
}
}
return null;
}
@SuppressWarnings("unchecked")
public static String getTypeName(Type type) {
StringBuilder result = new StringBuilder();
if (type.isArrayType()) {
result.append(getTypeName(((ArrayType) type).getElementType()));
result.append("[]");
}
else if (type.isParameterizedType()) {
ParameterizedType pType = (ParameterizedType) type;
result.append(getTypeName(pType.getType()));
List<Type> typeArguments = pType.typeArguments();
for (int i = 0; i < typeArguments.size(); i++) {
if (i > 0) {
result.append(", ");
}
result.append(getTypeName(typeArguments.get(i)));
}
}
else if (type.isPrimitiveType()) {
result.append(((PrimitiveType) type).getPrimitiveTypeCode().toString());
}
else if (type.isQualifiedType()) {
QualifiedType qType = (QualifiedType) type;
Type qualifier = qType.getQualifier();
if (qualifier != null) {
result.append(getTypeName(qualifier));
result.append(".");
}
result.append(qType.getName().getFullyQualifiedName());
}
else if (type.isSimpleType()) {
result.append(((SimpleType) type).getName().getFullyQualifiedName());
}
else if (type.isWildcardType()) {
WildcardType wType = (WildcardType) type;
result.append("? ");
if (wType.isUpperBound()) {
result.append("extends ");
}
else {
result.append("super ");
}
result.append(getTypeName(wType.getBound()));
}
return result.toString();
}
@SuppressWarnings("unchecked")
public static List<UriTemplateVariable> getUriTemplatVariables(Annotation annotation) {
if (annotation instanceof SingleMemberAnnotation) {
SingleMemberAnnotation sAnnotation = (SingleMemberAnnotation) annotation;
return getUriTemplateVariables(sAnnotation.getValue());
}
else if (annotation instanceof NormalAnnotation) {
NormalAnnotation nAnnotation = (NormalAnnotation) annotation;
List<MemberValuePair> pairs = nAnnotation.values();
for (MemberValuePair pair : pairs) {
if ("value".equals(pair.getName().getFullyQualifiedName())) {
return getUriTemplateVariables(pair.getValue());
}
}
}
return new ArrayList<UriTemplateVariable>();
}
/**
* @param annotation to be matched, or null if matches all annotations
* @param node
* @return true if there is a matching annotation on the node
*/
public static boolean hasAnnotation(String annotation, ASTNode node) {
AnnotationFinder finder = new AnnotationFinder(annotation);
node.accept(finder);
Set<Annotation> annotations = finder.getAnnotations();
return !annotations.isEmpty();
}
public static boolean hasProblem(BodyDeclaration declToMatch, String problemType, ICompilationUnit cu) {
return findProblem(declToMatch, problemType, cu, null) != null;
}
private static Class<?>[] KNOWN_REQUEST_MAPPING_PARAM_TYPE = new Class<?>[] { InputStream.class,
OutputStream.class, Reader.class, Writer.class, Principal.class, Locale.class, HttpServletRequest.class,
HttpServletResponse.class, HttpSession.class };
public static boolean isKnownRequestMappingParamType(IProject project, ITypeBinding typeBinding) {
String typeName = typeBinding.getQualifiedName();
ClassLoader classLoader = JdtUtils.getProjectClassLoaderSupport(project, BeansCorePlugin.getClassLoader())
.getProjectClassLoader();
try {
Class<?> clazz = classLoader.loadClass(typeName);
for (Class<?> knownClass : KNOWN_REQUEST_MAPPING_PARAM_TYPE) {
Class<?> loadedClass = classLoader.loadClass(knownClass.getCanonicalName());
if (loadedClass.isAssignableFrom(clazz)) {
return true;
}
}
}
catch (ClassNotFoundException e) {
}
return false;
}
private static BodyDeclaration getBodyDeclaration(IJavaElement element) {
try {
if (element instanceof IMember) {
IMember member = (IMember) element;
ICompilationUnit compilationUnit = member.getCompilationUnit();
ISourceRange sourceRange = member.getSourceRange();
AssistContext assistContext = new AssistContext(compilationUnit, null, sourceRange.getOffset(),
sourceRange.getLength());
ASTNode node = assistContext.getCoveringNode();
if (node instanceof BodyDeclaration) {
return (BodyDeclaration) node;
}
}
}
catch (JavaModelException e) {
}
return null;
}
private static List<UriTemplateVariable> getUriTemplateVariables(Expression expression) {
List<UriTemplateVariable> variables = new ArrayList<UriTemplateVariable>();
if (expression instanceof StringLiteral) {
StringLiteral literal = (StringLiteral) expression;
String uriTemplate = literal.getLiteralValue();
int offset = 1; // skip start quote
while (uriTemplate.length() > 0) {
int index = uriTemplate.indexOf("{");
if (index < 0) {
break;
}
uriTemplate = uriTemplate.substring(index + 1);
offset += index + 1;
index = uriTemplate.indexOf(":");
if (index < 0) {
index = uriTemplate.indexOf("}");
if (index < 0) {
break;
}
}
variables.add(new UriTemplateVariable(uriTemplate.substring(0, index), offset, literal));
uriTemplate = uriTemplate.substring(index + 1);
offset += index + 1;
}
}
return variables;
}
private static class AnnotationFinder extends ASTVisitor {
Set<Annotation> annotations = new HashSet<Annotation>();
private final String annotationToMatch;
private final int invocationOffset;
private boolean isTypeDecl;
public AnnotationFinder(String annotationToMatch) {
this.annotationToMatch = annotationToMatch;
this.invocationOffset = -1;
}
public AnnotationFinder(String annotationToMatch, int invocationOffset) {
this.annotationToMatch = annotationToMatch;
this.invocationOffset = invocationOffset;
}
public Set<Annotation> getAnnotations() {
return annotations;
}
@Override
public boolean visit(FieldDeclaration node) {
if (isTypeDecl) {
return false;
}
return super.visit(node);
}
@Override
public boolean visit(MarkerAnnotation node) {
if (matches(node)) {
annotations.add(node);
}
return false;
}
@Override
public boolean visit(MethodDeclaration node) {
if (isTypeDecl) {
return false;
}
return super.visit(node);
}
@Override
public boolean visit(NormalAnnotation node) {
if (matches(node)) {
annotations.add(node);
}
return false;
}
@Override
public boolean visit(SingleMemberAnnotation node) {
if (matches(node)) {
annotations.add(node);
}
return false;
}
@Override
public boolean visit(TypeDeclaration typeDecl) {
isTypeDecl = true;
return super.visit(typeDecl);
}
private boolean matches(Annotation annotation) {
Name typeName = annotation.getTypeName();
if (typeName != null) {
if (invocationOffset >= 0) {
int startPos = annotation.getStartPosition();
if (startPos > invocationOffset || startPos + annotation.getLength() < invocationOffset) {
return false;
}
}
return annotationToMatch == null || typeName.toString().equals(annotationToMatch);
}
return false;
}
}
}