blob: 77cf9fc39afc2038e54ec149b2205249b4280dbb [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.bval.jsr.descriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.validation.GroupDefinitionException;
import javax.validation.GroupSequence;
import javax.validation.ParameterNameProvider;
import javax.validation.groups.Default;
import javax.validation.metadata.PropertyDescriptor;
import javax.validation.metadata.Scope;
import org.apache.bval.jsr.ApacheValidatorFactory;
import org.apache.bval.jsr.groups.GroupConversion;
import org.apache.bval.jsr.metadata.ContainerElementKey;
import org.apache.bval.jsr.metadata.EmptyBuilder;
import org.apache.bval.jsr.metadata.MetadataBuilder;
import org.apache.bval.jsr.metadata.Meta;
import org.apache.bval.jsr.metadata.Signature;
import org.apache.bval.jsr.util.Methods;
import org.apache.bval.jsr.util.ToUnmodifiable;
import org.apache.bval.util.Exceptions;
import org.apache.bval.util.Validate;
import org.apache.bval.util.reflection.Reflection;
class MetadataReader {
class ForElement<E extends AnnotatedElement, B extends MetadataBuilder.ForElement<E>> {
final Meta<E> meta;
protected final B builder;
ForElement(Meta<E> meta, B builder) {
super();
this.meta = Validate.notNull(meta, "meta");
this.builder = Validate.notNull(builder, "builder");
}
Set<ConstraintD<?>> getConstraints() {
return builder.getConstraintsByScope(meta).entrySet().stream()
.flatMap(e -> describe(e.getValue(), e.getKey(), meta)).collect(ToUnmodifiable.set());
}
private Stream<ConstraintD<?>> describe(Annotation[] constraints, Scope scope, Meta<?> meta) {
return Stream.of(constraints).map(c -> new ConstraintD<>(c, scope, meta, validatorFactory));
}
}
class ForBean extends MetadataReader.ForElement<Class<?>, MetadataBuilder.ForClass> {
private final MetadataBuilder.ForBean beanBuilder;
ForBean(Meta<Class<?>> meta, MetadataBuilder.ForBean builder) {
super(meta, Validate.notNull(builder, "builder").getClass(meta));
this.beanBuilder = builder;
}
Map<String, PropertyDescriptor> getProperties(BeanD parent) {
final Map<String, List<PropertyD<?>>> properties = new LinkedHashMap<>();
final Function<? super String, ? extends List<PropertyD<?>>> descriptorList = k -> new ArrayList<>();
beanBuilder.getFields(meta).forEach((f, builder) -> {
final Field fld = Reflection.find(meta.getHost(), t -> Reflection.getDeclaredField(t, f));
properties.computeIfAbsent(f, descriptorList).add(new PropertyD.ForField(
new MetadataReader.ForContainer<>(new Meta.ForField(fld), builder), parent));
});
beanBuilder.getGetters(meta).forEach((g, builder) -> {
final Method getter = Reflection.find(meta.getHost(), t -> {
return Stream.of(Reflection.getDeclaredMethods(t)).filter(Methods::isGetter)
.filter(m -> g.equals(Methods.propertyName(m))).findFirst().orElse(null);
});
Exceptions.raiseIf(getter == null, IllegalStateException::new,
"Getter method for property %s not found", g);
properties.computeIfAbsent(g, descriptorList).add(new PropertyD.ForMethod(
new MetadataReader.ForContainer<>(new Meta.ForMethod(getter), builder), parent));
});
return properties.entrySet().stream().collect(ToUnmodifiable.map(Map.Entry::getKey, e -> {
final List<PropertyD<?>> delegates = e.getValue();
if (delegates.size() == 1) {
return delegates.get(0);
}
final Set<PropertyD<?>> constrained =
delegates.stream().filter(DescriptorManager::isConstrained).collect(Collectors.toSet());
if (constrained.isEmpty()) {
return delegates.get(0);
}
if (constrained.size() == 1) {
return constrained.iterator().next();
}
return new ComposedD.ForProperty(delegates);
}));
}
Map<Signature, MethodD> getMethods(BeanD parent) {
final Map<Signature, MetadataBuilder.ForExecutable<Method>> methodBuilders = beanBuilder.getMethods(meta);
if (methodBuilders.isEmpty()) {
return Collections.emptyMap();
}
final Map<Signature, MethodD> result = new LinkedHashMap<>();
methodBuilders.forEach((sig, builder) -> {
final Method m = Reflection.find(meta.getHost(),
t -> Reflection.getDeclaredMethod(t, sig.getName(), sig.getParameterTypes()));
result.put(sig, new MethodD(new MetadataReader.ForMethod(new Meta.ForMethod(m), builder), parent));
});
return Collections.unmodifiableMap(result);
}
Map<Signature, ConstructorD> getConstructors(BeanD parent) {
final Map<Signature, MetadataBuilder.ForExecutable<Constructor<?>>> ctorBuilders =
beanBuilder.getConstructors(meta);
if (ctorBuilders.isEmpty()) {
return Collections.emptyMap();
}
final Map<Signature, ConstructorD> result = new LinkedHashMap<>();
ctorBuilders.forEach((sig, builder) -> {
final Constructor<?> c = Reflection.getDeclaredConstructor(meta.getHost(), sig.getParameterTypes());
result.put(sig,
new ConstructorD(new MetadataReader.ForConstructor(new Meta.ForConstructor(c), builder), parent));
});
return Collections.unmodifiableMap(result);
}
List<Class<?>> getGroupSequence() {
List<Class<?>> result = builder.getGroupSequence(meta);
if (result == null) {
// resolve group sequence/Default redefinition up class hierarchy:
final Class<?> superclass = meta.getHost().getSuperclass();
if (superclass != null) {
// attempt to mock parent sequence intent by appending this type immediately after supertype:
result = ((ElementD<?, ?>) validatorFactory.getDescriptorManager().getBeanDescriptor(superclass))
.getGroupSequence();
if (result != null) {
result = new ArrayList<>(result);
result.add(result.indexOf(superclass) + 1, meta.getHost());
}
}
}
if (result == null) {
return null;
}
Exceptions.raiseUnless(result.contains(meta.getHost()), GroupDefinitionException::new,
"@%s for %s must contain %<s", GroupSequence.class.getSimpleName(), meta.getHost());
Exceptions.raiseIf(result.contains(Default.class), GroupDefinitionException::new,
"@%s for %s must not contain %s", GroupSequence.class.getSimpleName(), meta.getHost(),
Default.class.getName());
return Collections.unmodifiableList(result);
}
}
class ForContainer<E extends AnnotatedElement> extends ForElement<E, MetadataBuilder.ForContainer<E>> {
ForContainer(Meta<E> meta, MetadataBuilder.ForContainer<E> builder) {
super(meta, builder);
}
boolean isCascaded() {
return builder.isCascade(meta);
}
Set<GroupConversion> getGroupConversions() {
return builder.getGroupConversions(meta);
}
Set<ContainerElementTypeD> getContainerElementTypes(CascadableContainerD<?, ?> parent) {
final Map<ContainerElementKey, MetadataBuilder.ForContainer<AnnotatedType>> containerElementTypes =
builder.getContainerElementTypes(meta);
if (containerElementTypes.isEmpty()) {
return Collections.emptySet();
}
final Set<ContainerElementTypeD> result =
new TreeSet<>(Comparator.comparing(ContainerElementTypeD::getKey));
containerElementTypes.forEach((k, builder) -> {
result.add(new ContainerElementTypeD(k,
new MetadataReader.ForContainer<>(new Meta.ForContainerElement(meta, k), builder), parent));
});
return Collections.unmodifiableSet(result);
}
}
abstract class ForExecutable<E extends Executable, SELF extends ForExecutable<E, SELF>>
extends ForElement<E, MetadataBuilder.ForElement<E>> {
private final MetadataBuilder.ForExecutable<E> executableBuilder;
ForExecutable(Meta<E> meta, MetadataBuilder.ForExecutable<E> executableBuilder) {
super(meta, EmptyBuilder.instance().forElement());
this.executableBuilder = Validate.notNull(executableBuilder, "executableBuilder");
}
<X extends ExecutableD<E, SELF, X>> List<ParameterD<X>> getParameterDescriptors(X parent) {
final Parameter[] parameters = meta.getHost().getParameters();
final List<String> parameterNames =
getParameterNames(validatorFactory.getParameterNameProvider(), meta.getHost());
final List<MetadataBuilder.ForContainer<Parameter>> builders = executableBuilder.getParameters(meta);
return IntStream.range(0, parameters.length).mapToObj(i -> {
final Meta.ForParameter param = new Meta.ForParameter(parameters[i], parameterNames.get(i));
return new ParameterD<>(param, i, new MetadataReader.ForContainer<>(param, builders.get(i)), parent);
}).collect(ToUnmodifiable.list());
}
<X extends ExecutableD<E, SELF, X>> CrossParameterD<X, E> getCrossParameterDescriptor(X parent) {
final Meta.ForCrossParameter<E> cp = new Meta.ForCrossParameter<>(meta);
return new CrossParameterD<>(new MetadataReader.ForElement<>(cp, executableBuilder.getCrossParameter(cp)),
parent);
}
<X extends ExecutableD<E, SELF, X>> ReturnValueD<X, E> getReturnValueDescriptor(X parent) {
return new ReturnValueD<>(new MetadataReader.ForContainer<>(meta, executableBuilder.getReturnValue(meta)),
parent);
}
abstract List<String> getParameterNames(ParameterNameProvider parameterNameProvider, E host);
}
class ForMethod extends ForExecutable<Method, ForMethod> {
ForMethod(Meta<Method> meta, MetadataBuilder.ForExecutable<Method> builder) {
super(meta, builder);
}
@Override
List<String> getParameterNames(ParameterNameProvider parameterNameProvider, Method host) {
return parameterNameProvider.getParameterNames(host);
}
}
class ForConstructor extends ForExecutable<Constructor<?>, ForConstructor> {
ForConstructor(Meta<Constructor<?>> meta, MetadataBuilder.ForExecutable<Constructor<?>> builder) {
super(meta, builder);
}
@Override
List<String> getParameterNames(ParameterNameProvider parameterNameProvider, Constructor<?> host) {
return parameterNameProvider.getParameterNames(host);
}
}
private final ApacheValidatorFactory validatorFactory;
MetadataReader(ApacheValidatorFactory validatorFactory) {
super();
this.validatorFactory = Validate.notNull(validatorFactory, "validatorFactory");
}
MetadataReader.ForBean forBean(Class<?> beanClass, MetadataBuilder.ForBean builder) {
return new MetadataReader.ForBean(new Meta.ForClass(beanClass), builder);
}
}