blob: f25a918dec9b79795c111ee95efef98f51e6bee6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.qpid.server.model.validation;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@SupportedAnnotationTypes(ContentHeaderAnnotationValidator.CONTENT_HEADER_CLASS_NAME)
public class ContentHeaderAnnotationValidator extends AbstractProcessor
{
public static final String CONTENT_HEADER_CLASS_NAME = "org.apache.qpid.server.model.RestContentHeader";
public static final String CONTENT_CLASS_NAME = "org.apache.qpid.server.model.CustomRestHeaders";
private static final Set<TypeKind> VALID_PRIMITIVE_TYPES = new HashSet<>(Arrays.asList(
TypeKind.BOOLEAN,
TypeKind.BYTE,
TypeKind.CHAR,
TypeKind.DOUBLE,
TypeKind.FLOAT,
TypeKind.INT,
TypeKind.LONG,
TypeKind.SHORT));
@Override
public SourceVersion getSupportedSourceVersion()
{
return SourceVersion.latest();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
{
Elements elementUtils = processingEnv.getElementUtils();
TypeElement annotationElement = elementUtils.getTypeElement(CONTENT_HEADER_CLASS_NAME);
for (Element e : roundEnv.getElementsAnnotatedWith(annotationElement))
{
ExecutableElement methodElement = (ExecutableElement) e;
checkClassExtendsContent(annotationElement, methodElement);
checkMethodHasNoArgs(annotationElement, methodElement);
checkMethodReturnsString(annotationElement, methodElement);
}
return false;
}
private void checkMethodReturnsString(TypeElement annotationElement, ExecutableElement methodElement)
{
final TypeMirror returnType = methodElement.getReturnType();
if (!isValidType(returnType))
{
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.ERROR,
"@"
+ annotationElement.getSimpleName()
+ " can only be applied to methods with primitive, boxed primitive, enum, or String type return type but annotated Method "
+ methodElement.getSimpleName() + " returns "
+ returnType.toString(),
methodElement
);
}
}
private boolean isValidType(TypeMirror type)
{
Types typeUtils = processingEnv.getTypeUtils();
Elements elementUtils = processingEnv.getElementUtils();
Element typeElement = typeUtils.asElement(type);
if (VALID_PRIMITIVE_TYPES.contains(type.getKind()))
{
return true;
}
for (TypeKind primitive : VALID_PRIMITIVE_TYPES)
{
if (typeUtils.isSameType(type, typeUtils.boxedClass(typeUtils.getPrimitiveType(primitive)).asType()))
{
return true;
}
}
if (typeElement != null && typeElement.getKind() == ElementKind.ENUM)
{
return true;
}
if (typeUtils.isSameType(type, elementUtils.getTypeElement("java.lang.String").asType()))
{
return true;
}
return false;
}
private void checkMethodHasNoArgs(TypeElement annotationElement, ExecutableElement methodElement)
{
if (!methodElement.getParameters().isEmpty())
{
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.ERROR,
"@"
+ annotationElement.getSimpleName()
+ " can only be applied to methods without arguments"
+ " which does not apply to "
+ methodElement.getSimpleName(),
methodElement);
}
}
private void checkClassExtendsContent(TypeElement annotationElement, Element e)
{
Types typeUtils = processingEnv.getTypeUtils();
TypeMirror contentType = getErasure(CONTENT_CLASS_NAME);
TypeElement parent = (TypeElement) e.getEnclosingElement();
if (!typeUtils.isAssignable(typeUtils.erasure(parent.asType()), contentType))
{
processingEnv.getMessager()
.printMessage(Diagnostic.Kind.ERROR,
"@"
+ annotationElement.getSimpleName()
+ " can only be applied to methods within a class implementing "
+ contentType.toString()
+ " which does not apply to "
+ parent.asType().toString(),
e);
}
}
private TypeMirror getErasure(final String className)
{
return getErasure(processingEnv, className);
}
private static TypeMirror getErasure(ProcessingEnvironment processingEnv, final String className)
{
final Types typeUtils = processingEnv.getTypeUtils();
final Elements elementUtils = processingEnv.getElementUtils();
return typeUtils.erasure(elementUtils.getTypeElement(className).asType());
}
}