BVAL-174
diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java
index 9fb5c98..e0d7746 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ElementD.java
@@ -1,128 +1,128 @@
-/*
- * 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.bval.jsr.descriptor;
-
-import java.lang.annotation.ElementType;
-import java.lang.reflect.AnnotatedElement;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-import java.util.Map;
-import java.util.Set;
-
-import javax.validation.metadata.ConstraintDescriptor;
-import javax.validation.metadata.ElementDescriptor;
-
-import org.apache.bval.jsr.groups.GroupStrategy;
-import org.apache.bval.jsr.groups.GroupsComputer;
-import org.apache.bval.jsr.metadata.Meta;
-import org.apache.bval.util.Validate;
-import org.apache.bval.util.reflection.TypeUtils;
-
-public abstract class ElementD<E extends AnnotatedElement, R extends MetadataReader.ForElement<E, ?>>
-    implements ElementDescriptor {
-
-    public static abstract class NonRoot<P extends ElementD<?, ?>, E extends AnnotatedElement, R extends MetadataReader.ForElement<E, ?>>
-        extends ElementD<E, R> {
-
-        protected final P parent;
-
-        protected NonRoot(R reader, P parent) {
-            super(reader);
-            this.parent = Validate.notNull(parent, "parent");
-        }
-
-        public P getParent() {
-            return parent;
-        }
-
-        @Override
-        public final Type getGenericType() {
-            if (TypeUtils.containsTypeVariables(genericType)) {
-                final Map<TypeVariable<?>, Type> args =
-                    TypeUtils.getTypeArguments(parent.getGenericType(), Object.class);
-                return TypeUtils.unrollVariables(args, genericType);
-            }
-            return genericType;
-        }
-
-        @Override
-        final protected BeanD<?> getBean() {
-            return parent.getBean();
-        }
-
-        @Override
-        public final GroupStrategy getGroupStrategy() {
-            return getBean().getGroupStrategy();
-        }
-    }
-
-    protected final Type genericType;
-    final GroupsComputer groupsComputer;
-
-    private final Meta<E> meta;
-    private final Set<ConstraintD<?>> constraints;
-
-    protected ElementD(R reader) {
-        super();
-        Validate.notNull(reader, "reader");
-        this.meta = reader.meta;
-        this.genericType = reader.meta.getType();
-        this.constraints = reader.getConstraints();
-        this.groupsComputer = reader.getValidatorFactory().getGroupsComputer();
-    }
-
-    @Override
-    public final boolean hasConstraints() {
-        return !constraints.isEmpty();
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    public final Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
-        return (Set) constraints;
-    }
-
-    @Override
-    public final ConstraintFinder findConstraints() {
-        return new Finder(groupsComputer, this);
-    }
-
-    public final ElementType getElementType() {
-        return meta.getElementType();
-    }
-
-    public final E getTarget() {
-        return meta.getHost();
-    }
-
-    public final Class<?> getDeclaringClass() {
-        return meta.getDeclaringClass();
-    }
-
-    public abstract Type getGenericType();
-
-    public abstract GroupStrategy getGroupStrategy();
-
-    @Override
-    public String toString() {
-        return String.format("%s: %s", getClass().getSimpleName(), meta.describeHost());
-    }
-
-    protected abstract BeanD<?> getBean();
-}
+/*

+ * 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.bval.jsr.descriptor;

+

+import java.lang.annotation.ElementType;

+import java.lang.reflect.AnnotatedElement;

+import java.lang.reflect.Type;

+import java.lang.reflect.TypeVariable;

+import java.util.Map;

+import java.util.Set;

+

+import javax.validation.metadata.ConstraintDescriptor;

+import javax.validation.metadata.ElementDescriptor;

+

+import org.apache.bval.jsr.groups.GroupStrategy;

+import org.apache.bval.jsr.groups.GroupsComputer;

+import org.apache.bval.jsr.metadata.Meta;

+import org.apache.bval.util.Validate;

+import org.apache.bval.util.reflection.TypeUtils;

+

+public abstract class ElementD<E extends AnnotatedElement, R extends MetadataReader.ForElement<E, ?>>

+    implements ElementDescriptor {

+

+    public static abstract class NonRoot<P extends ElementD<?, ?>, E extends AnnotatedElement, R extends MetadataReader.ForElement<E, ?>>

+        extends ElementD<E, R> {

+

+        protected final P parent;

+

+        protected NonRoot(R reader, P parent) {

+            super(reader);

+            this.parent = Validate.notNull(parent, "parent");

+        }

+

+        public P getParent() {

+            return parent;

+        }

+

+        @Override

+        public final Type getGenericType() {

+            if (TypeUtils.containsTypeVariables(genericType)) {

+                final Map<TypeVariable<?>, Type> args =

+                    TypeUtils.getTypeArguments(parent.getGenericType(), Object.class);

+                return TypeUtils.unrollVariables(args, genericType);

+            }

+            return genericType;

+        }

+

+        @Override

+        final protected BeanD<?> getBean() {

+            return parent.getBean();

+        }

+

+        @Override

+        public final GroupStrategy getGroupStrategy() {

+            return getBean().getGroupStrategy();

+        }

+    }

+

+    protected final Type genericType;

+    final GroupsComputer groupsComputer;

+

+    private final Meta<E> meta;

+    private final Set<ConstraintD<?>> constraints;

+

+    protected ElementD(R reader) {

+        super();

+        Validate.notNull(reader, "reader");

+        this.meta = reader.meta;

+        this.genericType = reader.meta.getType();

+        this.constraints = reader.getConstraints();

+        this.groupsComputer = reader.getValidatorFactory().getGroupsComputer();

+    }

+

+    @Override

+    public boolean hasConstraints() {

+        return !constraints.isEmpty();

+    }

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    @Override

+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {

+        return (Set) constraints;

+    }

+

+    @Override

+    public final ConstraintFinder findConstraints() {

+        return new Finder(groupsComputer, this);

+    }

+

+    public final ElementType getElementType() {

+        return meta.getElementType();

+    }

+

+    public final E getTarget() {

+        return meta.getHost();

+    }

+

+    public final Class<?> getDeclaringClass() {

+        return meta.getDeclaringClass();

+    }

+

+    public abstract Type getGenericType();

+

+    public abstract GroupStrategy getGroupStrategy();

+

+    @Override

+    public String toString() {

+        return String.format("%s: %s", getClass().getSimpleName(), meta.describeHost());

+    }

+

+    protected abstract BeanD<?> getBean();

+}

diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
index a2204fc..7be73f9 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/descriptor/ReturnValueD.java
@@ -1,36 +1,98 @@
-/*
- * 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.bval.jsr.descriptor;
-
-import java.lang.reflect.Executable;
-
-import javax.validation.metadata.ReturnValueDescriptor;
-
-public class ReturnValueD<P extends ExecutableD<?, ?, P>, E extends Executable> extends CascadableContainerD<P, E>
-    implements ReturnValueDescriptor {
-
-    ReturnValueD(MetadataReader.ForContainer<E> reader, P parent) {
-        super(reader, parent);
-    }
-
-    @Override
-    public Class<?> getElementClass() {
-        return parent.getElementClass();
-    }
-}
+/*

+ * 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.bval.jsr.descriptor;

+

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Executable;

+import java.lang.reflect.Method;

+import java.util.HashSet;

+import java.util.Set;

+import javax.validation.ConstraintValidator;

+import javax.validation.UnexpectedTypeException;

+import javax.validation.metadata.ConstraintDescriptor;

+

+import javax.validation.metadata.ReturnValueDescriptor;

+import org.apache.bval.util.ValidatorUtils;

+import org.apache.bval.util.reflection.TypeUtils;

+

+public class ReturnValueD<P extends ExecutableD<?, ?, P>, E extends Executable> extends CascadableContainerD<P, E>

+    implements ReturnValueDescriptor {

+

+    private final Set<ConstraintD<?>> constraints;

+

+    ReturnValueD(MetadataReader.ForContainer<E> reader, P parent) {

+        super(reader, parent);

+        this.constraints = new HashSet<>(reader.getConstraints());

+

+        Class<?> validatedType;

+        if (reader.meta.getHost() instanceof Constructor)

+        {

+            validatedType = reader.meta.getDeclaringClass();

+        }

+        else

+        {

+            validatedType = ((Method) reader.meta.getHost()).getReturnType();

+        }

+        

+        for (ConstraintDescriptor<?> c : constraints)

+        {

+            if (!hasValidatorForType(validatedType, c)

+                    && (!c.getConstraintValidatorClasses().isEmpty() || !c.getComposingConstraints().isEmpty()))

+            {

+                String msg = "No validator found for (composition) constraint @"

+                        + c.getAnnotation().annotationType().getSimpleName()

+                        + " declared on \"" + reader.meta.getHost().toString()

+                        + "\" for validated type \"" + validatedType.getName() + "\"";

+                throw new UnexpectedTypeException(msg);

+            }

+        }

+    }

+

+    private boolean hasValidatorForType(Class<?> validatedType, ConstraintDescriptor<?> c)

+    {

+        for (Class<? extends ConstraintValidator<?, ?>> validatorClass : c.getConstraintValidatorClasses())

+        {

+            if (TypeUtils.isAssignable(validatedType, ValidatorUtils.getValidatedType(validatorClass)))

+            {

+                return true;

+            }

+        }

+        

+        for (ConstraintDescriptor<?> composite : c.getComposingConstraints())

+        {

+            if (hasValidatorForType(validatedType, composite))

+            {

+                return true;

+            }

+        }

+

+        return false;

+    }

+    

+    @Override

+    public boolean hasConstraints() {

+        return !constraints.isEmpty();

+    }

+

+    @SuppressWarnings({ "unchecked", "rawtypes" })

+    @Override

+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {

+        return (Set) constraints;

+    }

+}

diff --git a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java
index 123754b..1336e2f 100644
--- a/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java
+++ b/bval-jsr/src/main/java/org/apache/bval/jsr/job/ComputeConstraintValidatorClass.java
@@ -1,214 +1,191 @@
-/*
- *  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.bval.jsr.job;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.WildcardType;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import javax.validation.ConstraintDefinitionException;
-import javax.validation.ConstraintValidator;
-import javax.validation.UnexpectedTypeException;
-import javax.validation.constraintvalidation.ValidationTarget;
-
-import org.apache.bval.jsr.ConstraintCached;
-import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;
-import org.apache.bval.jsr.descriptor.ConstraintD;
-import org.apache.bval.util.Exceptions;
-import org.apache.bval.util.Validate;
-import org.apache.bval.util.reflection.Reflection;
-import org.apache.bval.util.reflection.Reflection.Interfaces;
-import org.apache.bval.util.reflection.TypeUtils;
-import org.apache.commons.weaver.privilizer.Privilizing;
-import org.apache.commons.weaver.privilizer.Privilizing.CallTo;
-
-@Privilizing(@CallTo(Reflection.class))
-class ComputeConstraintValidatorClass<A extends Annotation>
-    implements Supplier<Class<? extends ConstraintValidator<A, ?>>> {
-
-    private static class TypeWrapper {
-        final Class<?> componentType;
-        final int arrayDepth;
-
-        TypeWrapper(Class<?> type) {
-            Class<?> c = type;
-            int d = 0;
-            while (Object[].class.isAssignableFrom(c)) {
-                d++;
-                c = c.getComponentType();
-            }
-            this.componentType = c;
-            this.arrayDepth = d;
-        }
-
-        Class<?> unwrapArrayComponentType(Class<?> t) {
-            Exceptions.raiseUnless(t.isAssignableFrom(componentType), IllegalArgumentException::new,
-                "%s not assignable from %s", t, componentType);
-            if (arrayDepth == 0) {
-                return t;
-            }
-            return Array.newInstance(t, new int[arrayDepth]).getClass();
-        }
-    }
-
-    private static final String CV = ConstraintValidator.class.getSimpleName();
-    private static final WildcardType UNBOUNDED = TypeUtils.wildcardType().build();
-
-    private static Class<?> getValidatedType(Class<? extends ConstraintValidator<?, ?>> validatorType) {
-        final Type result = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class)
-            .get(ConstraintValidator.class.getTypeParameters()[1]);
-        if (!isSupported(result)) {
-            Exceptions.raise(ConstraintDefinitionException::new, "Validated type %s declared by %s %s is unsupported",
-                result, CV, validatorType.getName());
-        }
-        return TypeUtils.getRawType(result, null);
-    }
-
-    private static boolean isSupported(Type validatedType) {
-        if (validatedType instanceof Class<?>) {
-            return true;
-        }
-        if (validatedType instanceof ParameterizedType) {
-            return Stream.of(((ParameterizedType) validatedType).getActualTypeArguments())
-                .allMatch(arg -> TypeUtils.equals(arg, UNBOUNDED));
-        }
-        return false;
-    }
-
-    private final ConstraintCached constraintsCache;
-    private final ConstraintD<?> descriptor;
-    private final ValidationTarget validationTarget;
-    private final Class<?> validatedType;
-
-    ComputeConstraintValidatorClass(ConstraintCached constraintsCache, ConstraintD<A> descriptor,
-        ValidationTarget validationTarget, Class<?> validatedType) {
-        super();
-        this.constraintsCache = Validate.notNull(constraintsCache, "constraintsCache");
-        this.descriptor = Validate.notNull(descriptor, "descriptor");
-        this.validationTarget = Validate.notNull(validationTarget, "validationTarget");
-        this.validatedType = Validate.notNull(validatedType, "validatedType");
-    }
-
-    @Override
-    public Class<? extends ConstraintValidator<A, ?>> get() {
-        @SuppressWarnings("unchecked")
-        final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType();
-        return findValidator(constraintsCache.getConstraintValidatorInfo(constraintType));
-    }
-
-    private Class<? extends ConstraintValidator<A, ?>> findValidator(Set<ConstraintValidatorInfo<A>> infos) {
-        switch (validationTarget) {
-        case PARAMETERS:
-            return findCrossParameterValidator(infos);
-        case ANNOTATED_ELEMENT:
-            return findAnnotatedElementValidator(infos);
-        default:
-            return null;
-        }
-    }
-
-    private Class<? extends ConstraintValidator<A, ?>> findCrossParameterValidator(
-        Set<ConstraintValidatorInfo<A>> infos) {
-
-        final Set<ConstraintValidatorInfo<A>> set =
-            infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.PARAMETERS))
-                .collect(Collectors.toSet());
-
-        @SuppressWarnings("unchecked")
-        final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType();
-
-        final int size = set.size();
-        Exceptions.raiseIf(size > 1 || !isComposed() && set.isEmpty(), ConstraintDefinitionException::new,
-            "%d cross-parameter %ss found for constraint type %s", size, CV, constraintType);
-
-        final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType();
-        if (!TypeUtils.isAssignable(Object[].class, getValidatedType(result))) {
-            Exceptions.raise(ConstraintDefinitionException::new,
-                "Cross-parameter %s %s does not support the validation of an object array", CV, result.getName());
-        }
-        return result;
-    }
-
-    private Class<? extends ConstraintValidator<A, ?>> findAnnotatedElementValidator(
-        Set<ConstraintValidatorInfo<A>> infos) {
-
-        final Map<Class<?>, Class<? extends ConstraintValidator<?, ?>>> validators = infos.stream()
-            .filter(info -> info.getSupportedTargets().contains(ValidationTarget.ANNOTATED_ELEMENT))
-            .map(ConstraintValidatorInfo::getType).collect(
-                Collectors.toMap(ComputeConstraintValidatorClass::getValidatedType, Function.identity(), (v1, v2) -> {
-                    Exceptions.raiseUnless(Objects.equals(v1, v2), UnexpectedTypeException::new,
-                        "Detected collision of constraint and target type between %s and %s", v1, v2);
-                    return v1;
-                }));
-
-        final Map<Type, Class<? extends ConstraintValidator<?, ?>>> candidates = new HashMap<>();
-
-        walkHierarchy().filter(validators::containsKey).forEach(type -> {
-            // if we haven't already found a candidate whose validated type
-            // is a subtype of the current evaluated type, save:
-            if (!candidates.keySet().stream().anyMatch(k -> TypeUtils.isAssignable(k, type))) {
-                candidates.put(type, validators.get(type));
-            }
-        });
-        final String cond;
-        switch (candidates.size()) {
-        case 1:
-            @SuppressWarnings("unchecked")
-            final Class<? extends ConstraintValidator<A, ?>> result =
-                (Class<? extends ConstraintValidator<A, ?>>) candidates.values().iterator().next();
-            return result;
-        case 0:
-            if (isComposed()) {
-                return null;
-            }
-            cond = "No compliant";
-            break;
-        default:
-            cond = "> 1 maximally specific";
-            break;
-        }
-        throw Exceptions.create(UnexpectedTypeException::new, "%s %s %s found for annotated element of type %s", cond,
-            descriptor.getAnnotation().annotationType().getName(), CV, TypeUtils.toString(validatedType));
-    }
-
-    // account for validated array types by unwrapping and rewrapping component
-    // type hierarchy:
-    private Stream<Class<?>> walkHierarchy() {
-        final TypeWrapper w = new TypeWrapper(Reflection.primitiveToWrapper(validatedType));
-        Stream.Builder<Class<?>> hierarchy = Stream.builder();
-        Reflection.hierarchy(w.componentType, Interfaces.INCLUDE).forEach(hierarchy);
-        final Stream<Class<?>> result = hierarchy.build().map(w::unwrapArrayComponentType);
-        if (validatedType.isInterface() || validatedType.isArray()) {
-            return Stream.concat(result, Stream.of(Object.class));
-        }
-        return result;
-    }
-
-    private boolean isComposed() {
-        return !descriptor.getComposingConstraints().isEmpty();
-    }
-}
+/*

+ *  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.bval.jsr.job;

+

+import java.lang.annotation.Annotation;

+import java.lang.reflect.Array;

+import java.lang.reflect.Type;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.Objects;

+import java.util.Set;

+import java.util.function.Function;

+import java.util.function.Supplier;

+import java.util.stream.Collectors;

+import java.util.stream.Stream;

+

+import javax.validation.ConstraintDefinitionException;

+import javax.validation.ConstraintValidator;

+import javax.validation.UnexpectedTypeException;

+import javax.validation.constraintvalidation.ValidationTarget;

+

+import org.apache.bval.jsr.ConstraintCached;

+import org.apache.bval.jsr.ConstraintCached.ConstraintValidatorInfo;

+import org.apache.bval.jsr.descriptor.ConstraintD;

+import org.apache.bval.util.Exceptions;

+import org.apache.bval.util.Validate;

+import org.apache.bval.util.ValidatorUtils;

+import org.apache.bval.util.reflection.Reflection;

+import org.apache.bval.util.reflection.Reflection.Interfaces;

+import org.apache.bval.util.reflection.TypeUtils;

+import org.apache.commons.weaver.privilizer.Privilizing;

+import org.apache.commons.weaver.privilizer.Privilizing.CallTo;

+

+@Privilizing(@CallTo(Reflection.class))

+class ComputeConstraintValidatorClass<A extends Annotation>

+    implements Supplier<Class<? extends ConstraintValidator<A, ?>>> {

+

+    private static class TypeWrapper {

+        final Class<?> componentType;

+        final int arrayDepth;

+

+        TypeWrapper(Class<?> type) {

+            Class<?> c = type;

+            int d = 0;

+            while (Object[].class.isAssignableFrom(c)) {

+                d++;

+                c = c.getComponentType();

+            }

+            this.componentType = c;

+            this.arrayDepth = d;

+        }

+

+        Class<?> unwrapArrayComponentType(Class<?> t) {

+            Exceptions.raiseUnless(t.isAssignableFrom(componentType), IllegalArgumentException::new,

+                "%s not assignable from %s", t, componentType);

+            if (arrayDepth == 0) {

+                return t;

+            }

+            return Array.newInstance(t, new int[arrayDepth]).getClass();

+        }

+    }

+

+    private static final String CV = ConstraintValidator.class.getSimpleName();

+

+    private final ConstraintCached constraintsCache;

+    private final ConstraintD<?> descriptor;

+    private final ValidationTarget validationTarget;

+    private final Class<?> validatedType;

+

+    ComputeConstraintValidatorClass(ConstraintCached constraintsCache, ConstraintD<A> descriptor,

+        ValidationTarget validationTarget, Class<?> validatedType) {

+        super();

+        this.constraintsCache = Validate.notNull(constraintsCache, "constraintsCache");

+        this.descriptor = Validate.notNull(descriptor, "descriptor");

+        this.validationTarget = Validate.notNull(validationTarget, "validationTarget");

+        this.validatedType = Validate.notNull(validatedType, "validatedType");

+    }

+

+    @Override

+    public Class<? extends ConstraintValidator<A, ?>> get() {

+        @SuppressWarnings("unchecked")

+        final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType();

+        return findValidator(constraintsCache.getConstraintValidatorInfo(constraintType));

+    }

+

+    private Class<? extends ConstraintValidator<A, ?>> findValidator(Set<ConstraintValidatorInfo<A>> infos) {

+        switch (validationTarget) {

+        case PARAMETERS:

+            return findCrossParameterValidator(infos);

+        case ANNOTATED_ELEMENT:

+            return findAnnotatedElementValidator(infos);

+        default:

+            return null;

+        }

+    }

+

+    private Class<? extends ConstraintValidator<A, ?>> findCrossParameterValidator(

+        Set<ConstraintValidatorInfo<A>> infos) {

+

+        final Set<ConstraintValidatorInfo<A>> set =

+            infos.stream().filter(info -> info.getSupportedTargets().contains(ValidationTarget.PARAMETERS))

+                .collect(Collectors.toSet());

+

+        @SuppressWarnings("unchecked")

+        final Class<A> constraintType = (Class<A>) descriptor.getAnnotation().annotationType();

+

+        final int size = set.size();

+        Exceptions.raiseIf(size > 1 || !isComposed() && set.isEmpty(), ConstraintDefinitionException::new,

+            "%d cross-parameter %ss found for constraint type %s", size, CV, constraintType);

+

+        final Class<? extends ConstraintValidator<A, ?>> result = set.iterator().next().getType();

+        if (!TypeUtils.isAssignable(Object[].class, ValidatorUtils.getValidatedType(result))) {

+            Exceptions.raise(ConstraintDefinitionException::new,

+                "Cross-parameter %s %s does not support the validation of an object array", CV, result.getName());

+        }

+        return result;

+    }

+

+    private Class<? extends ConstraintValidator<A, ?>> findAnnotatedElementValidator(

+        Set<ConstraintValidatorInfo<A>> infos) {

+

+        final Map<Class<?>, Class<? extends ConstraintValidator<?, ?>>> validators = infos.stream()

+            .filter(info -> info.getSupportedTargets().contains(ValidationTarget.ANNOTATED_ELEMENT))

+            .map(ConstraintValidatorInfo::getType).collect(

+                Collectors.toMap(ValidatorUtils::getValidatedType, Function.identity(), (v1, v2) -> {

+                    Exceptions.raiseUnless(Objects.equals(v1, v2), UnexpectedTypeException::new,

+                        "Detected collision of constraint and target type between %s and %s", v1, v2);

+                    return v1;

+                }));

+

+        final Map<Type, Class<? extends ConstraintValidator<?, ?>>> candidates = new HashMap<>();

+

+        walkHierarchy().filter(validators::containsKey).forEach(type -> {

+            // if we haven't already found a candidate whose validated type

+            // is a subtype of the current evaluated type, save:

+            if (!candidates.keySet().stream().anyMatch(k -> TypeUtils.isAssignable(k, type))) {

+                candidates.put(type, validators.get(type));

+            }

+        });

+        final String cond;

+        switch (candidates.size()) {

+        case 1:

+            @SuppressWarnings("unchecked")

+            final Class<? extends ConstraintValidator<A, ?>> result =

+                (Class<? extends ConstraintValidator<A, ?>>) candidates.values().iterator().next();

+            return result;

+        case 0:

+            if (isComposed()) {

+                return null;

+            }

+            cond = "No compliant";

+            break;

+        default:

+            cond = "> 1 maximally specific";

+            break;

+        }

+        throw Exceptions.create(UnexpectedTypeException::new, "%s %s %s found for annotated element of type %s", cond,

+            descriptor.getAnnotation().annotationType().getName(), CV, TypeUtils.toString(validatedType));

+    }

+

+    // account for validated array types by unwrapping and rewrapping component

+    // type hierarchy:

+    private Stream<Class<?>> walkHierarchy() {

+        final TypeWrapper w = new TypeWrapper(Reflection.primitiveToWrapper(validatedType));

+        Stream.Builder<Class<?>> hierarchy = Stream.builder();

+        Reflection.hierarchy(w.componentType, Interfaces.INCLUDE).forEach(hierarchy);

+        final Stream<Class<?>> result = hierarchy.build().map(w::unwrapArrayComponentType);

+        if (validatedType.isInterface() || validatedType.isArray()) {

+            return Stream.concat(result, Stream.of(Object.class));

+        }

+        return result;

+    }

+

+    private boolean isComposed() {

+        return !descriptor.getComposingConstraints().isEmpty();

+    }

+}

diff --git a/bval-jsr/src/main/java/org/apache/bval/util/ValidatorUtils.java b/bval-jsr/src/main/java/org/apache/bval/util/ValidatorUtils.java
new file mode 100644
index 0000000..11dad25
--- /dev/null
+++ b/bval-jsr/src/main/java/org/apache/bval/util/ValidatorUtils.java
@@ -0,0 +1,52 @@
+/*

+ *  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.bval.util;

+

+import java.lang.reflect.ParameterizedType;

+import java.lang.reflect.Type;

+import java.lang.reflect.WildcardType;

+import java.util.stream.Stream;

+import javax.validation.ConstraintDefinitionException;

+import javax.validation.ConstraintValidator;

+import org.apache.bval.util.reflection.TypeUtils;

+

+public class ValidatorUtils {

+

+    private static final WildcardType UNBOUNDED = TypeUtils.wildcardType().build();

+    private static final String CV = ConstraintValidator.class.getSimpleName();

+    

+    public static Class<?> getValidatedType(Class<? extends ConstraintValidator<?, ?>> validatorType) {

+        final Type result = TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class)

+            .get(ConstraintValidator.class.getTypeParameters()[1]);

+        if (!isSupported(result)) {

+            Exceptions.raise(ConstraintDefinitionException::new, "Validated type %s declared by %s %s is unsupported",

+                result, CV, validatorType.getName());

+        }

+        return TypeUtils.getRawType(result, null);

+    }

+    

+    private static boolean isSupported(Type validatedType) {

+        if (validatedType instanceof Class<?>) {

+            return true;

+        }

+        if (validatedType instanceof ParameterizedType) {

+            return Stream.of(((ParameterizedType) validatedType).getActualTypeArguments())

+                .allMatch(arg -> TypeUtils.equals(arg, UNBOUNDED));

+        }

+        return false;

+    }

+}

diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/issues/BVAL174.java b/bval-jsr/src/test/java/org/apache/bval/jsr/issues/BVAL174.java
new file mode 100644
index 0000000..b55ce04
--- /dev/null
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/issues/BVAL174.java
@@ -0,0 +1,80 @@
+/*

+ * 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.bval.jsr.issues;

+

+import java.lang.annotation.Documented;

+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;

+import static java.lang.annotation.ElementType.METHOD;

+import java.lang.annotation.Retention;

+import static java.lang.annotation.RetentionPolicy.RUNTIME;

+import java.lang.annotation.Target;

+import java.util.Set;

+import javax.validation.ConstraintValidator;

+import javax.validation.ConstraintValidatorContext;

+import javax.validation.Payload;

+

+public class BVAL174 {

+

+    @Audience("movies")

+    public String getMovie() {

+        return "";

+    }

+

+    @Audience("movies")

+    public void addMovie(String newMovie) {

+

+    }

+

+    @Documented

+    @javax.validation.Constraint(validatedBy = {Audience.Constraint.class})

+    @Target({METHOD, ANNOTATION_TYPE})

+    @Retention(RUNTIME)

+    public @interface Audience {

+

+        String value();

+

+        Class<?>[] groups() default {};

+

+        String message() default "The 'aud' claim must contain '{value}'";

+

+        Class<? extends Payload>[] payload() default {};

+

+        class Constraint implements ConstraintValidator<Audience, JsonWebToken> {

+            private Audience audience;

+

+            @Override

+            public void initialize(final Audience constraint) {

+                this.audience = constraint;

+            }

+

+            @Override

+            public boolean isValid(final JsonWebToken value, final ConstraintValidatorContext context) {

+                final Set<String> audience = value.getAudience();

+                return audience != null && audience.contains(this.audience.value());

+            }

+        }

+    }

+

+    public class JsonWebToken {

+

+        public Set<String> getAudience() {

+            return null;

+        }

+    }

+}

diff --git a/bval-jsr/src/test/java/org/apache/bval/jsr/issues/BVAL174Test.java b/bval-jsr/src/test/java/org/apache/bval/jsr/issues/BVAL174Test.java
new file mode 100644
index 0000000..92ebead
--- /dev/null
+++ b/bval-jsr/src/test/java/org/apache/bval/jsr/issues/BVAL174Test.java
@@ -0,0 +1,55 @@
+/*

+ * 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.bval.jsr.issues;

+

+import java.lang.reflect.Method;

+import javax.validation.UnexpectedTypeException;

+import javax.validation.Validation;

+import javax.validation.Validator;

+import javax.validation.metadata.MethodDescriptor;

+import org.apache.bval.jsr.ApacheValidationProvider;

+import static org.junit.Assert.assertTrue;

+import org.junit.Test;

+

+public class BVAL174Test {

+    

+    private Validator getValidator() {

+        return Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory().getValidator();

+    }

+    

+    @Test(expected = UnexpectedTypeException.class)

+    public void testValidateReturnValue() throws NoSuchMethodException {

+        Validator validator = getValidator();        

+        

+        BVAL174 service = new BVAL174();

+        Method getMovie = service.getClass().getMethod("getMovie");

+        Method addMovie = service.getClass().getMethod("addMovie", String.class);

+        

+        MethodDescriptor getMovieConstraints = validator.getConstraintsForClass(service.getClass())

+            .getConstraintsForMethod(getMovie.getName(), getMovie.getParameterTypes());

+        

+        assertTrue(getMovieConstraints == null);

+        

+        MethodDescriptor addMovieConstraints = validator.getConstraintsForClass(service.getClass())

+            .getConstraintsForMethod(addMovie.getName(), addMovie.getParameterTypes());

+        

+        assertTrue(addMovieConstraints == null);

+    }

+

+}