/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.ap.classchecks;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.hibernate.validator.ap.checks.ConstraintCheckIssue;
import org.hibernate.validator.ap.util.CollectionHelper;
import org.hibernate.validator.ap.util.ConstraintHelper;
/**
* Checks if the return value of overridden and overriding methods respects the inheritance rules.
* <p>
* Return value constraints of a method must not be weakened in subtypes. One must not mark a method return value
* for cascaded validation more than once in a line of a class hierarchy. In other words, overriding methods
* on subtypes (be it sub classes/interfaces or interface implementations) cannot mark the return value
* for cascaded validation if the return value has already been marked on the overridden method of the super
* type or interface.
*
* @author Marko Bekhta
* @author Guillaume Smet
*/
public class ReturnValueMethodOverrideCheck extends AbstractMethodOverrideCheck {
public ReturnValueMethodOverrideCheck(Elements elementUtils, Types typeUtils, ConstraintHelper constraintHelper) {
super( elementUtils, typeUtils, constraintHelper );
}
@Override
protected Set<ConstraintCheckIssue> checkMethodInternal(ExecutableElement currentMethod, MethodInheritanceTree methodInheritanceTree) {
// if this method gets executed it means that the current method has a @Valid annotation and we
// need to check that there is no other @Valid annotations in the hierarchy of this method
Set<ConstraintCheckIssue> issues = CollectionHelper.newHashSet();
for ( ExecutableElement overriddenMethod : methodInheritanceTree.getOverriddenMethods() ) {
if ( methodIsAnnotatedWithValid( overriddenMethod ) ) {
issues.add( ConstraintCheckIssue.error(
currentMethod,
null,
"INCORRECT_METHOD_RETURN_VALUE_OVERRIDING",
getEnclosingTypeElementQualifiedName( overriddenMethod )
) );
}
}
return issues;
}
@Override
protected boolean needToPerformAnyChecks(ExecutableElement currentMethod) {
// we only check the method if:
// - it does not return void
// - it is marked with @Valid
return !currentMethod.getReturnType().getKind().equals( TypeKind.VOID ) && methodIsAnnotatedWithValid( currentMethod );
}
/**
* Check if there is a {@code @Valid} annotation present on the method.
*
* @param method a method to check for annotation presence
* @return {@code true} if {@code @Valid} annotation is present on return value of a given method, {@code false}
* otherwise
*/
private boolean methodIsAnnotatedWithValid(ExecutableElement method) {
for ( AnnotationMirror annotationMirror : method.getAnnotationMirrors() ) {
if ( ConstraintHelper.AnnotationType.GRAPH_VALIDATION_ANNOTATION.equals( constraintHelper.getAnnotationType( annotationMirror ) ) ) {
return true;
}
}
return false;
}
}