Author: rgodfrey
Date: Fri May  9 15:24:21 2014
New Revision: 1593562

URL: http://svn.apache.org/r1593562
Log:
QPID-5759 : [Java Broker] add annotation validation

Added:
    
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/
    
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
    
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
Modified:
    
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor

Added: 
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java?rev=1593562&view=auto
==============================================================================
--- 
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
 (added)
+++ 
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeAnnotationValidator.java
 Fri May  9 15:24:21 2014
@@ -0,0 +1,327 @@
+/*
+ *
+ * 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 java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+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.DeclaredType;
+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;
+
+
+@SupportedAnnotationTypes({AttributeAnnotationValidator.MANAGED_ATTRIBUTE_CLASS_NAME,
+                           
AttributeAnnotationValidator.DERIVED_ATTRIBUTE_CLASS_NAME,
+                           
AttributeAnnotationValidator.MANAGED_STATISTIC_CLASS_NAME})
+public class AttributeAnnotationValidator extends AbstractProcessor
+{
+
+    public static final String MANAGED_ATTRIBUTE_CLASS_NAME = 
"org.apache.qpid.server.model.ManagedAttribute";
+    public static final String DERIVED_ATTRIBUTE_CLASS_NAME = 
"org.apache.qpid.server.model.DerivedAttribute";
+
+    public static final String MANAGED_STATISTIC_CLASS_NAME = 
"org.apache.qpid.server.model.ManagedStatistic";
+
+
+
+    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(final Set<? extends TypeElement> annotations, final 
RoundEnvironment roundEnv)
+    {
+
+        processAttributes(roundEnv, MANAGED_ATTRIBUTE_CLASS_NAME);
+        processAttributes(roundEnv, DERIVED_ATTRIBUTE_CLASS_NAME);
+
+        processStatistics(roundEnv, MANAGED_STATISTIC_CLASS_NAME);
+
+        return false;
+    }
+
+    public void processAttributes(final RoundEnvironment roundEnv,
+                                  String elementName)
+    {
+
+        Elements elementUtils = processingEnv.getElementUtils();
+        TypeElement annotationElement = 
elementUtils.getTypeElement(elementName);
+
+        for (Element e : roundEnv.getElementsAnnotatedWith(annotationElement))
+        {
+            checkAnnotationIsOnMethodInInterface(annotationElement, e);
+
+            ExecutableElement methodElement = (ExecutableElement) e;
+
+            checkInterfaceExtendsConfiguredObject(annotationElement, 
methodElement);
+            checkMethodTakesNoArgs(annotationElement, methodElement);
+            checkMethodName(annotationElement, methodElement);
+            checkMethodReturnType(annotationElement, methodElement);
+
+            checkTypeAgreesWithName(annotationElement, methodElement);
+        }
+    }
+
+    public void processStatistics(final RoundEnvironment roundEnv,
+                                  String elementName)
+    {
+
+        Elements elementUtils = processingEnv.getElementUtils();
+        TypeElement annotationElement = 
elementUtils.getTypeElement(elementName);
+
+        for (Element e : roundEnv.getElementsAnnotatedWith(annotationElement))
+        {
+            checkAnnotationIsOnMethodInInterface(annotationElement, e);
+
+            ExecutableElement methodElement = (ExecutableElement) e;
+
+            checkInterfaceExtendsConfiguredObject(annotationElement, 
methodElement);
+            checkMethodTakesNoArgs(annotationElement, methodElement);
+            checkMethodName(annotationElement, methodElement);
+            checkTypeAgreesWithName(annotationElement, methodElement);
+            checkMethodReturnTypeIsNumber(annotationElement, methodElement);
+
+        }
+    }
+
+    private void checkMethodReturnTypeIsNumber(final TypeElement 
annotationElement,
+                                               final ExecutableElement 
methodElement)
+    {
+        Types typeUtils = processingEnv.getTypeUtils();
+        Elements elementUtils = processingEnv.getElementUtils();
+
+        TypeMirror numberType = 
elementUtils.getTypeElement("java.lang.Number").asType();
+        if(!typeUtils.isAssignable(methodElement.getReturnType(),numberType))
+        {
+            processingEnv.getMessager()
+                    .printMessage(Diagnostic.Kind.ERROR,
+                                  "@"
+                                  + annotationElement.getSimpleName()
+                                  + " return type does not extend Number: "
+                                  + methodElement.getReturnType().toString(),
+                                  methodElement
+                                 );
+        }
+    }
+
+    public void checkTypeAgreesWithName(final TypeElement annotationElement, 
final ExecutableElement methodElement)
+    {
+        Types typeUtils = processingEnv.getTypeUtils();
+
+        String methodName = methodElement.getSimpleName().toString();
+
+        if((methodName.startsWith("is") || methodName.startsWith("has"))
+            && !(methodElement.getReturnType().getKind() == TypeKind.BOOLEAN
+                 || 
typeUtils.isSameType(typeUtils.boxedClass(typeUtils.getPrimitiveType(TypeKind.BOOLEAN)).asType(),
 methodElement.getReturnType())))
+        {
+            processingEnv.getMessager()
+                    .printMessage(Diagnostic.Kind.ERROR,
+                                  "@"
+                                  + annotationElement.getSimpleName()
+                                  + " return type is not boolean or Boolean: "
+                                  + methodElement.getReturnType().toString(),
+                                  methodElement
+                                 );
+        }
+    }
+
+    public void checkMethodReturnType(final TypeElement annotationElement, 
final ExecutableElement methodElement)
+    {
+        if (!isValidType(methodElement.getReturnType()))
+        {
+            processingEnv.getMessager()
+                    .printMessage(Diagnostic.Kind.ERROR,
+                                  "@"
+                                  + annotationElement.getSimpleName()
+                                  + " cannot be applied to methods with return 
type "
+                                  + methodElement.getReturnType().toString(),
+                                  methodElement
+                                 );
+        }
+    }
+
+    public void checkMethodName(final TypeElement annotationElement, final 
ExecutableElement methodElement)
+    {
+        String methodName = methodElement.getSimpleName().toString();
+
+        if (methodName.length() < 3
+            || (methodName.length() < 4 && !methodName.startsWith("is"))
+            || !(methodName.startsWith("is") || methodName.startsWith("get") 
|| methodName.startsWith("has")))
+        {
+            processingEnv.getMessager()
+                    .printMessage(Diagnostic.Kind.ERROR,
+                                  "@"
+                                  + annotationElement.getSimpleName()
+                                  + " can only be applied to methods which of 
the form getXXX(), isXXX() or hasXXX()",
+                                  methodElement
+                                 );
+        }
+    }
+
+    public void checkMethodTakesNoArgs(final TypeElement annotationElement, 
final ExecutableElement methodElement)
+    {
+        if (!methodElement.getParameters().isEmpty())
+        {
+            processingEnv.getMessager()
+                    .printMessage(Diagnostic.Kind.ERROR,
+                                  "@"
+                                  + annotationElement.getSimpleName()
+                                  + " can only be applied to methods which 
take no parameters",
+                                  methodElement
+                                 );
+        }
+    }
+
+    public void checkInterfaceExtendsConfiguredObject(final TypeElement 
annotationElement, final Element e)
+    {
+        Types typeUtils = processingEnv.getTypeUtils();
+        TypeMirror configuredObjectType = 
getErasure("org.apache.qpid.server.model.ConfiguredObject");
+        TypeElement parent = (TypeElement) e.getEnclosingElement();
+
+
+        if (!typeUtils.isAssignable(typeUtils.erasure(parent.asType()), 
configuredObjectType))
+        {
+            processingEnv.getMessager()
+                    .printMessage(Diagnostic.Kind.ERROR,
+                                  "@"
+                                  + annotationElement.getSimpleName()
+                                  + " can only be applied to methods within an 
interface which extends "
+                                  + configuredObjectType.toString()
+                                  + " which does not apply to "
+                                  + parent.asType().toString(),
+                                  e);
+        }
+    }
+
+    public void checkAnnotationIsOnMethodInInterface(final TypeElement 
annotationElement, final Element e)
+    {
+        if (e.getKind() != ElementKind.METHOD || 
e.getEnclosingElement().getKind() != ElementKind.INTERFACE)
+        {
+            processingEnv.getMessager()
+                    .printMessage(Diagnostic.Kind.ERROR,
+                                  "@"
+                                  + annotationElement.getSimpleName()
+                                  + " can only be applied to methods within an 
interface",
+                                  e
+                                 );
+        }
+    }
+
+    private boolean isValidType(final 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.getKind()==ElementKind.ENUM)
+        {
+            return true;
+        }
+
+        String className = "org.apache.qpid.server.model.ConfiguredObject";
+        TypeMirror configuredObjectType = getErasure(className);
+
+        if(typeUtils.isAssignable(typeUtils.erasure(type), 
configuredObjectType))
+        {
+            return true;
+        }
+
+        if(typeUtils.isSameType(type, 
elementUtils.getTypeElement("java.lang.String").asType()))
+        {
+            return true;
+        }
+
+
+        
if(typeUtils.isSameType(type,elementUtils.getTypeElement("java.util.UUID").asType()))
+        {
+            return true;
+        }
+
+        TypeMirror erasedType = typeUtils.erasure(type);
+        if(typeUtils.isSameType(erasedType, getErasure("java.util.List"))
+                || typeUtils.isSameType(erasedType, 
getErasure("java.util.Set"))
+                || typeUtils.isSameType(erasedType, 
getErasure("java.util.Collection")))
+        {
+
+
+            for(TypeMirror paramType : ((DeclaredType)type).getTypeArguments())
+            {
+
+                if(!isValidType(paramType))
+                {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        if(typeUtils.isSameType(erasedType, getErasure("java.util.Map")))
+        {
+            List<? extends TypeMirror> args = ((DeclaredType) 
type).getTypeArguments();
+            return isValidType(args.get(0)) && (isValidType(args.get(1)) || 
typeUtils.isSameType(args.get(1), getErasure("java.lang.Object")));
+        }
+
+
+        return false;
+    }
+
+    private TypeMirror getErasure(final String className)
+    {
+        final Types typeUtils = processingEnv.getTypeUtils();
+        final Elements elementUtils = processingEnv.getElementUtils();
+        return 
typeUtils.erasure(elementUtils.getTypeElement(className).asType());
+    }
+
+}

Added: 
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java?rev=1593562&view=auto
==============================================================================
--- 
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
 (added)
+++ 
qpid/trunk/qpid/java/broker-codegen/src/main/java/org/apache/qpid/server/model/validation/AttributeFieldValidation.java
 Fri May  9 15:24:21 2014
@@ -0,0 +1,106 @@
+/*
+ *
+ * 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 java.util.Map;
+import java.util.Set;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+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.util.Elements;
+import javax.lang.model.util.Types;
+import javax.tools.Diagnostic;
+
+@SupportedAnnotationTypes(AttributeFieldValidation.MANAGED_ATTRIBUTE_FIELD_CLASS_NAME)
+public class AttributeFieldValidation extends AbstractProcessor
+{
+    public static final String MANAGED_ATTRIBUTE_FIELD_CLASS_NAME = 
"org.apache.qpid.server.model.ManagedAttributeField";
+
+
+    @Override
+    public SourceVersion getSupportedSourceVersion()
+    {
+        return SourceVersion.latest();
+    }
+
+    @Override
+    public boolean process(final Set<? extends TypeElement> annotations, final 
RoundEnvironment roundEnv)
+    {
+        Elements elementUtils = processingEnv.getElementUtils();
+        Types typeUtils = processingEnv.getTypeUtils();
+
+        TypeElement annotationElement = 
elementUtils.getTypeElement(MANAGED_ATTRIBUTE_FIELD_CLASS_NAME);
+        for (Element e : roundEnv.getElementsAnnotatedWith(annotationElement))
+        {
+            for(AnnotationMirror am : e.getAnnotationMirrors())
+            {
+                if(typeUtils.isSameType(am.getAnnotationType(), 
annotationElement.asType()))
+                {
+                    for(Map.Entry<? extends ExecutableElement, ? extends 
AnnotationValue> entry : am.getElementValues().entrySet())
+                    {
+                        String elementName = 
entry.getKey().getSimpleName().toString();
+                        if(elementName.equals("beforeSet") || 
elementName.equals("afterSet"))
+                        {
+                            String methodName = 
entry.getValue().getValue().toString();
+                            if(!"".equals(methodName))
+                            {
+                                TypeElement parent = (TypeElement) 
e.getEnclosingElement();
+                                if(!containsMethod(parent, methodName))
+                                {
+                                    
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
+                                                                             
"Could not find method '"
+                                                                             + 
methodName
+                                                                             + 
"' which is defined as the "
+                                                                             + 
elementName
+                                                                             + 
" action", e);
+
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean containsMethod(final TypeElement parent, final String 
methodName)
+    {
+        for(Element element : parent.getEnclosedElements())
+        {
+            if(element.getKind().equals(ElementKind.METHOD)
+               && element.getSimpleName().toString().equals(methodName)
+               && ((ExecutableElement)element).getParameters().isEmpty())
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+}

Modified: 
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor?rev=1593562&r1=1593561&r2=1593562&view=diff
==============================================================================
--- 
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
 (original)
+++ 
qpid/trunk/qpid/java/broker-codegen/src/main/resources/META-INF/services/javax.annotation.processing.Processor
 Fri May  9 15:24:21 2014
@@ -19,3 +19,5 @@
 org.apache.qpid.server.model.ConfiguredObjectFactoryGenerator
 org.apache.qpid.server.plugin.PluggableProcessor
 org.apache.qpid.server.model.ConfiguredObjectRegistrationGenerator
+org.apache.qpid.server.model.validation.AttributeAnnotationValidator
+org.apache.qpid.server.model.validation.AttributeFieldValidation



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to