blob: 72bea3bf5323b5c0eeaeb8082f2043ba4023c415 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 Shane Clarke.
* 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:
* Shane Clarke - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.ws.internal.jaxws.core.annotations.validation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.jdt.apt.core.env.EclipseAnnotationProcessorEnvironment;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jst.ws.annotations.core.processor.AbstractAnnotationProcessor;
import org.eclipse.jst.ws.annotations.core.utils.AnnotationUtils;
import org.eclipse.jst.ws.internal.jaxws.core.JAXWSCoreMessages;
import org.eclipse.jst.ws.internal.jaxws.core.JAXWSCorePlugin;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.ClassDeclaration;
import com.sun.mirror.declaration.Declaration;
import com.sun.mirror.declaration.InterfaceDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.Modifier;
import com.sun.mirror.declaration.ParameterDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;
import com.sun.mirror.type.ArrayType;
import com.sun.mirror.type.ClassType;
import com.sun.mirror.type.InterfaceType;
import com.sun.mirror.type.TypeMirror;
public class WebServiceParametersReturnTypesRule extends AbstractAnnotationProcessor {
private static final Set<String> JAVA_TYPES = new HashSet<String>();
private static final Set<String> ALLOWED_IN_MULTIPLE_INHERITANCE = new HashSet<String>();
static {
JAVA_TYPES.add("java.util.Calendar"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.Date"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.GregorianCalendar"); //$NON-NLS-1$
JAVA_TYPES.add("javax.xml.datatype.XMLGregorianCalendar"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.ArrayList"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.HashSet"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.LinkedList"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.List"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.Stack"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.Vector"); //$NON-NLS-1$
JAVA_TYPES.add("java.math.BigDecimal"); //$NON-NLS-1$
JAVA_TYPES.add("java.math.BigInteger"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Object"); //$NON-NLS-1$
JAVA_TYPES.add("javax.xml.namespace.QName"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.UUID"); //$NON-NLS-1$
JAVA_TYPES.add("java.net.URI"); //$NON-NLS-1$
JAVA_TYPES.add("java.awt.Image"); //$NON-NLS-1$
JAVA_TYPES.add("javax.xml.datatype.Duration"); //$NON-NLS-1$
JAVA_TYPES.add("javax.xml.transform.Source"); //$NON-NLS-1$
JAVA_TYPES.add("javax.activation.DataHandler"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.String"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Boolean"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Integer"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Character"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Float"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Byte"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Double"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Long"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Short"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Void"); //$NON-NLS-1$
JAVA_TYPES.add("java.rmi.RemoteException"); //$NON-NLS-1$
JAVA_TYPES.add("java.io.Serializable"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Cloneable"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Comparable"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.Map"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.HashMap"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.Hashtable"); //$NON-NLS-1$
JAVA_TYPES.add("java.util.Collection"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Exception"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.Throwable"); //$NON-NLS-1$
JAVA_TYPES.add("java.lang.StackTraceElement"); //$NON-NLS-1$
ALLOWED_IN_MULTIPLE_INHERITANCE.add("java.lang.Cloneable"); // $NON-NLS$
ALLOWED_IN_MULTIPLE_INHERITANCE.add("java.lang.Comparable"); // $NON-NLS$
ALLOWED_IN_MULTIPLE_INHERITANCE.add("java.io.Serializable"); // $NON-NLS$
}
private static final String REMOTE_OBJECT = "java.rmi.Remote";
@Override
public void process() {
AnnotationTypeDeclaration webServiceDeclaration = (AnnotationTypeDeclaration) environment
.getTypeDeclaration(WebService.class.getName());
Collection<Declaration> annotatedTypes = environment.getDeclarationsAnnotatedWith(webServiceDeclaration);
for (Declaration declaration : annotatedTypes) {
if (declaration instanceof InterfaceDeclaration) {
validateMethodParametersAndReturnTypes((InterfaceDeclaration) declaration);
}
}
}
private void validateMethodParametersAndReturnTypes(InterfaceDeclaration interfaceDeclaration) {
Collection<? extends MethodDeclaration> methods = interfaceDeclaration.getMethods();
for (MethodDeclaration methodDeclaration : methods) {
TypeMirror returnType = methodDeclaration.getReturnType();
validateTypeMirror(returnType, methodDeclaration);
Collection<ParameterDeclaration> parameters = methodDeclaration.getParameters();
for (ParameterDeclaration parameterDeclaration : parameters) {
validateTypeMirror(parameterDeclaration.getType(), methodDeclaration);
}
}
}
private void validateTypeMirror(TypeMirror returnType, MethodDeclaration methodDeclaration) {
if (returnType instanceof ArrayType) {
ArrayType arrayType = (ArrayType) returnType;
TypeMirror componentType = arrayType.getComponentType();
if (componentType instanceof ClassDeclaration) {
ClassDeclaration classDeclaration = (ClassDeclaration) componentType;
checkClassRestriction(classDeclaration, methodDeclaration);
}
}
if (returnType instanceof InterfaceType) {
InterfaceType interfaceType = (InterfaceType) returnType;
Collection<TypeMirror> typeArguments = interfaceType.getActualTypeArguments();
if (typeArguments.size() > 0) {
for (TypeMirror typeMirror : typeArguments) {
if (typeMirror instanceof ClassDeclaration) {
ClassDeclaration classDeclaration = (ClassDeclaration) typeMirror;
checkClassRestriction(classDeclaration, methodDeclaration);
}
if (typeMirror instanceof InterfaceDeclaration) {
checkInterface((InterfaceDeclaration) typeMirror, methodDeclaration);
}
}
}
checkInterface(interfaceType.getDeclaration(), methodDeclaration);
}
if (returnType instanceof ClassType) {
ClassType classType = (ClassType) returnType;
Collection<TypeMirror> typeArguments = classType.getActualTypeArguments();
if (typeArguments.size() > 0) {
for (TypeMirror typeMirror : typeArguments) {
if (typeMirror instanceof ClassDeclaration) {
ClassDeclaration classDeclaration = (ClassDeclaration) typeMirror;
checkClassRestriction(classDeclaration, methodDeclaration);
}
}
} else {
if (classType.getDeclaration() != null) {
checkClassRestriction(classType.getDeclaration(), methodDeclaration);
}
}
}
}
private void checkClassRestriction(ClassDeclaration classDeclaration, MethodDeclaration methodDeclaration) {
checkInnerPublicStaticTypes(classDeclaration, methodDeclaration);
checkIfRemoteObject(classDeclaration, methodDeclaration);
checkAbstactType(classDeclaration, methodDeclaration);
checkMultipleInheritance(classDeclaration, methodDeclaration);
}
private void checkInnerPublicStaticTypes(ClassDeclaration classDeclaration, MethodDeclaration methodDeclaration) {
if (hasInnerNonPublicStaticTypes(classDeclaration)) {
printError(methodDeclaration.getPosition(), JAXWSCoreMessages.bind(
JAXWSCoreMessages.HAS_INADMISSIBLE_INNER_TYPES, classDeclaration.getQualifiedName()));
}
}
private boolean hasInnerNonPublicStaticTypes(ClassDeclaration classDeclaration) {
if (isJavaType(classDeclaration.getQualifiedName()) && JAVA_TYPES.contains(classDeclaration.getQualifiedName())) {
return false;
}
Collection<TypeDeclaration> nestedTypes = classDeclaration.getNestedTypes();
for (TypeDeclaration typeDeclaration : nestedTypes) {
if (!typeDeclaration.getModifiers().contains(Modifier.PUBLIC)
|| !typeDeclaration.getModifiers().contains(Modifier.STATIC)) {
return true;
}
}
return false;
}
private void checkInterface(InterfaceDeclaration interfaceDeclaration, MethodDeclaration methodDeclaration) {
if (!isSuitableInterface(interfaceDeclaration)) {
printError(methodDeclaration.getPosition(), JAXWSCoreMessages.bind(
JAXWSCoreMessages.INTERFACES_NOT_SUPPORTED, interfaceDeclaration.getQualifiedName()));
}
}
private boolean isSuitableInterface(InterfaceDeclaration interfaceDeclaration) {
return isJavaType(interfaceDeclaration.getQualifiedName()) && JAVA_TYPES.contains(interfaceDeclaration.getQualifiedName())
|| isXMLType(interfaceDeclaration);
}
private boolean isXMLType(InterfaceDeclaration interfaceDeclaration) {
return AnnotationUtils.getAnnotation(interfaceDeclaration, XmlType.class) != null;
}
private void checkIfRemoteObject(ClassDeclaration classDeclaration, MethodDeclaration methodDeclaration) {
if (isRemoteObject(classDeclaration)) {
printError(methodDeclaration.getPosition(), JAXWSCoreMessages.bind(
JAXWSCoreMessages.IS_REMOTE_OBJECT, classDeclaration.getQualifiedName()));
}
}
private boolean isRemoteObject(ClassDeclaration classDeclaration) {
Collection<InterfaceType> superInterfaces = classDeclaration.getSuperinterfaces();
for (InterfaceType interfaceType : superInterfaces) {
if (interfaceType.getDeclaration() != null && interfaceType.getDeclaration().getQualifiedName().equals(REMOTE_OBJECT)) {
return true;
}
}
return false;
}
private void checkAbstactType(ClassDeclaration classDeclaration, MethodDeclaration methodDeclaration) {
if (classDeclaration.getModifiers().contains(Modifier.ABSTRACT)) {
if (environment instanceof EclipseAnnotationProcessorEnvironment) {
EclipseAnnotationProcessorEnvironment eclipseEnvironment = (EclipseAnnotationProcessorEnvironment) environment;
IJavaProject javaProject = eclipseEnvironment.getJavaProject();
try {
IType type = javaProject.findType(classDeclaration.getQualifiedName());
ITypeHierarchy hierarchy = type.newTypeHierarchy(null);
IType[] subTypes = hierarchy.getAllSubtypes(type);
if (subTypes.length > 0) {
for (IType subType : subTypes) {
if (!Flags.isAbstract(subType.getFlags())) {
TypeDeclaration typeDeclaration = environment.getTypeDeclaration(subType.getFullyQualifiedName());
if (typeDeclaration instanceof ClassDeclaration) {
ClassDeclaration subTypeDeclaration = (ClassDeclaration) typeDeclaration;
if (isRemoteObject(subTypeDeclaration) || hasInnerNonPublicStaticTypes(subTypeDeclaration)
|| implementsMultipleInterfaces(subTypeDeclaration)
|| implementsClassAndInterface(subTypeDeclaration)) {
printError(methodDeclaration.getPosition(), JAXWSCoreMessages.bind(
JAXWSCoreMessages.ABSTRACT_CLASS_NOT_IMPLEMENTED,
classDeclaration.getQualifiedName()));
}
}
}
}
} else {
printError(methodDeclaration.getPosition(), JAXWSCoreMessages.bind(
JAXWSCoreMessages.ABSTRACT_CLASS_NOT_IMPLEMENTED,
classDeclaration.getQualifiedName()));
}
} catch (JavaModelException jme) {
JAXWSCorePlugin.log(jme.getStatus());
}
}
}
}
private boolean implementsMultipleInterfaces(ClassDeclaration classDeclaration) {
if (hasSuperClass(classDeclaration)) {
return countAllowedInterfaces(classDeclaration.getSuperinterfaces()) > 1;
}
return false;
}
private boolean implementsClassAndInterface(ClassDeclaration classDeclaration) {
if (!hasSuperClass(classDeclaration)) {
return countAllowedInterfaces(classDeclaration.getSuperinterfaces()) > 0;
}
return false;
}
private boolean hasSuperClass(ClassDeclaration classDeclaration) {
ClassType superClass = classDeclaration.getSuperclass();
if (superClass.getDeclaration() != null) {
String qualifiedName = superClass.getDeclaration().getQualifiedName();
return qualifiedName.equals(Object.class.getName()) || qualifiedName.equals(Exception.class.getName());
}
return false;
}
private void checkMultipleInheritance(ClassDeclaration classDeclaration, MethodDeclaration methodDeclaration) {
if (implementsMultipleInterfaces(classDeclaration)) {
List<String> interfaceNames = getAllowedInterfaceNames(classDeclaration.getSuperinterfaces());
printError(methodDeclaration.getPosition(), JAXWSCoreMessages.bind(
JAXWSCoreMessages.IMPLEMENTS_MULTIPLE_INTERFACES,
new Object [] { classDeclaration.getQualifiedName(), interfaceNames.get(0), interfaceNames.get(1) }));
}
if (implementsClassAndInterface(classDeclaration)) {
List<String> interfaceNames = getAllowedInterfaceNames(classDeclaration.getSuperinterfaces());
printError(methodDeclaration.getPosition(), JAXWSCoreMessages.bind(
JAXWSCoreMessages.INHERITANCE_AND_IMPLEMENTATION,
new Object[] {classDeclaration.getQualifiedName(),
classDeclaration.getSuperclass().getDeclaration().getQualifiedName(),
interfaceNames.get(0)} ));
}
}
private List<String> getAllowedInterfaceNames(Collection<InterfaceType> superInterfaces) {
List<String> interfaceNames = new ArrayList<String>();
for (InterfaceType interfaceType : superInterfaces) {
if (interfaceType.getDeclaration() != null) {
InterfaceDeclaration superInterface = interfaceType.getDeclaration();
if (!ALLOWED_IN_MULTIPLE_INHERITANCE.contains(superInterface.getQualifiedName())) {
interfaceNames.add(superInterface.getQualifiedName());
}
}
}
return interfaceNames;
}
private int countAllowedInterfaces(Collection<InterfaceType> superInterfaces) {
int count = 0;
for (InterfaceType interfaceType : superInterfaces) {
if (interfaceType.getDeclaration() != null) {
InterfaceDeclaration superInterface = interfaceType.getDeclaration();
if (!ALLOWED_IN_MULTIPLE_INHERITANCE.contains(superInterface.getQualifiedName())) {
count++;
}
}
}
return count;
}
private boolean isJavaType(String typeName) {
return typeName.startsWith("java.") || typeName.startsWith("javax."); //$NON-NLS-1$ //$NON-NLS-2$
}
}